quota.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package main
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "sort"
  8. "strconv"
  9. "strings"
  10. fs "imuslab.com/arozos/mod/filesystem"
  11. "imuslab.com/arozos/mod/utils"
  12. //user "imuslab.com/arozos/mod/user"
  13. )
  14. func DiskQuotaInit() {
  15. //Register Endpoints
  16. http.HandleFunc("/system/disk/quota/setQuota", system_disk_quota_setQuota)
  17. http.HandleFunc("/system/disk/quota/quotaInfo", system_disk_quota_handleQuotaInfo)
  18. http.HandleFunc("/system/disk/quota/quotaDist", system_disk_quota_handleFileDistributionView)
  19. //Register Setting Interfaces
  20. registerSetting(settingModule{
  21. Name: "Storage Quota",
  22. Desc: "User Remaining Space",
  23. IconPath: "SystemAO/disk/quota/img/small_icon.png",
  24. Group: "Disk",
  25. StartDir: "SystemAO/disk/quota/quota.html",
  26. })
  27. //Register the timer for running the global user quota recalculation
  28. nightlyManager.RegisterNightlyTask(system_disk_quota_updateAllUserQuotaEstimation)
  29. }
  30. //Register the handler for automatically updating all user storage quota
  31. func system_disk_quota_updateAllUserQuotaEstimation() {
  32. registeredUsers := authAgent.ListUsers()
  33. for _, username := range registeredUsers {
  34. //For each user, update their current quota usage
  35. userinfo, _ := userHandler.GetUserInfoFromUsername(username)
  36. userinfo.StorageQuota.CalculateQuotaUsage()
  37. }
  38. }
  39. //Set the storage quota of the particular user
  40. func system_disk_quota_setQuota(w http.ResponseWriter, r *http.Request) {
  41. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  42. if err != nil {
  43. utils.SendErrorResponse(w, "Unknown User")
  44. return
  45. }
  46. //Check if admin
  47. if !userinfo.IsAdmin() {
  48. utils.SendErrorResponse(w, "Permission Denied")
  49. return
  50. }
  51. groupname, err := utils.PostPara(r, "groupname")
  52. if err != nil {
  53. utils.SendErrorResponse(w, "Group name not defned")
  54. return
  55. }
  56. quotaSizeString, err := utils.PostPara(r, "quota")
  57. if err != nil {
  58. utils.SendErrorResponse(w, "Quota not defined")
  59. return
  60. }
  61. quotaSize, err := utils.StringToInt64(quotaSizeString)
  62. if err != nil || quotaSize < 0 {
  63. utils.SendErrorResponse(w, "Invalid quota size given")
  64. return
  65. }
  66. //Qutasize unit is in MB
  67. quotaSize = quotaSize << 20
  68. systemWideLogger.PrintAndLog("Quota", "Updating "+groupname+" to "+strconv.FormatInt(quotaSize, 10)+"WIP", nil)
  69. utils.SendOK(w)
  70. }
  71. func system_disk_quota_handleQuotaInfo(w http.ResponseWriter, r *http.Request) {
  72. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  73. if err != nil {
  74. utils.SendErrorResponse(w, "Unknown User")
  75. return
  76. }
  77. //Get quota information
  78. type quotaInformation struct {
  79. Remaining int64
  80. Used int64
  81. Total int64
  82. }
  83. jsonString, _ := json.Marshal(quotaInformation{
  84. Remaining: userinfo.StorageQuota.TotalStorageQuota - userinfo.StorageQuota.UsedStorageQuota,
  85. Used: userinfo.StorageQuota.UsedStorageQuota,
  86. Total: userinfo.StorageQuota.TotalStorageQuota,
  87. })
  88. utils.SendJSONResponse(w, string(jsonString))
  89. go func() {
  90. //Update this user's quota estimation in go routine
  91. userinfo.StorageQuota.CalculateQuotaUsage()
  92. }()
  93. }
  94. var fileExtensions = map[string][]string{
  95. "CAD": {"stl", "step", "stp", "obj", "dwg", "dxf", "iges", "igs", "3dm", "sat", "sldprt", "sldasm"},
  96. "Music": {"mp3", "flac", "wav", "aac", "ogg", "m4a", "wma", "opus", "alac", "aiff", "mid", "midi"},
  97. "Video": {"mp4", "webm", "avi", "mov", "mkv", "flv", "wmv", "m4v", "3gp", "ts", "mts", "vob"},
  98. "Design": {"psd", "ai", "xd", "sketch", "fig", "indd", "eps", "cdr", "afdesign", "afphoto"},
  99. "Documents": {"pdf", "doc", "docx", "odt", "rtf", "txt", "md", "pages", "tex", "wpd", "fodt", "docm"},
  100. "Spreadsheets": {"xls", "xlsx", "ods", "csv", "tsv", "numbers", "xlsm", "xlsb", "fods", "ots"},
  101. "Presentations": {"ppt", "pptx", "odp", "key", "pptm", "fodp", "otp"},
  102. "Images": {"jpg", "jpeg", "png", "gif", "bmp", "tiff", "svg", "webp", "heic", "raw", "cr2", "nef", "arw", "avif", "jxl"},
  103. "Archives": {"zip", "rar", "7z", "tar", "gz", "bz2", "xz", "zst", "lz4", "cab", "arj", "lzma"},
  104. "Code": {"c", "cpp", "h", "hpp", "java", "py", "js", "ts", "go", "rs", "rb", "php", "swift", "kt", "cs", "vb", "scala", "dart", "zig", "nim", "ex", "exs"},
  105. "Web": {"html", "htm", "css", "xml", "json", "yaml", "yml", "toml", "graphql", "wasm", "vue", "jsx", "tsx", "svelte"},
  106. "Data": {"sql", "db", "sqlite", "mdb", "accdb", "parquet", "avro", "orc", "hdf5", "feather"},
  107. "Executables": {"exe", "msi", "bat", "sh", "app", "apk", "ipa", "deb", "rpm", "run", "com"},
  108. "System": {"dll", "sys", "ini", "cfg", "log", "reg", "drv", "tmp", "lock", "pid"},
  109. "Fonts": {"ttf", "otf", "woff", "woff2", "eot", "fon", "psf", "pfb", "pfm"},
  110. "Ebooks": {"epub", "mobi", "azw", "djvu", "azw3", "fb2", "lit", "cbz", "cbr"},
  111. "DiskImages": {"iso", "img", "dmg", "vhd", "vhdx", "vmdk", "qcow2", "vdi", "ova", "ovf"},
  112. "3DModels": {"fbx", "blend", "dae", "3ds", "gltf", "glb", "abc", "usd", "usda", "x3d"},
  113. "Scripts": {"pl", "lua", "r", "matlab", "ps1", "psm1", "bash", "zsh", "fish", "awk", "sed", "tcl"},
  114. }
  115. //Get all the users files and return size + count per category
  116. func system_disk_quota_handleFileDistributionView(w http.ResponseWriter, r *http.Request) {
  117. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  118. if err != nil {
  119. utils.SendErrorResponse(w, "Unknown User")
  120. return
  121. }
  122. // Build reverse lookup: extension → category
  123. extToCategory := make(map[string]string, 300)
  124. for category, exts := range fileExtensions {
  125. for _, ext := range exts {
  126. extToCategory[ext] = category
  127. }
  128. }
  129. type distEntry struct {
  130. Count int64
  131. Size int64
  132. }
  133. fileDist := map[string]*distEntry{}
  134. userFileSystemHandlers := userinfo.GetAllFileSystemHandler()
  135. for _, thisHandler := range userFileSystemHandlers {
  136. if thisHandler.Hierarchy == "user" && !thisHandler.IsNetworkDrive() {
  137. // We only calculate the file distribution for the user's local storage, not network drives, and we only calculate for the user level handler to avoid duplication
  138. thispath := filepath.ToSlash(filepath.Clean(thisHandler.Path)) + "/users/" + userinfo.Username + "/"
  139. filepath.Walk(thispath, func(fpath string, info os.FileInfo, err error) error {
  140. if err != nil {
  141. return err
  142. }
  143. if !info.IsDir() {
  144. ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(fpath), "."))
  145. category, ok := extToCategory[ext]
  146. if !ok {
  147. mime, _, merr := fs.GetMime(fpath)
  148. if merr != nil || mime == "" {
  149. category = "Others"
  150. } else {
  151. mediaType := strings.SplitN(mime, "/", 2)[0]
  152. category = strings.Title(mediaType)
  153. }
  154. }
  155. if fileDist[category] == nil {
  156. fileDist[category] = &distEntry{}
  157. }
  158. fileDist[category].Count++
  159. fileDist[category].Size += info.Size()
  160. }
  161. return nil
  162. })
  163. }
  164. }
  165. type kv struct {
  166. Mime string
  167. Count int64
  168. Size int64
  169. }
  170. var ss []kv
  171. for k, v := range fileDist {
  172. ss = append(ss, kv{k, v.Count, v.Size})
  173. }
  174. sort.Slice(ss, func(i, j int) bool {
  175. return ss[i].Size > ss[j].Size
  176. })
  177. jsonString, _ := json.Marshal(ss)
  178. utils.SendJSONResponse(w, string(jsonString))
  179. }