system.disk.quota.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "strings"
  10. "log"
  11. )
  12. /*
  13. Disk Quota Management System
  14. This module manage the user groups disk quota in the system
  15. Disk quota can only be set on a user group bases.
  16. (aka all users in the same group has the identical number of disk quota to the group settings)
  17. */
  18. func system_disk_quota_init() {
  19. //Initiate quota storage table
  20. err := sysdb.NewTable("diskquota")
  21. if err != nil {
  22. panic(err)
  23. }
  24. //Register Endpoints
  25. http.HandleFunc("/system/disk/quota/setQuota", system_disk_quota_setQuota)
  26. http.HandleFunc("/system/disk/quota/listQuota", system_disk_quota_listQuota)
  27. http.HandleFunc("/system/disk/quota/quotaInfo", system_disk_quota_handleQuotaInfo)
  28. http.HandleFunc("/system/disk/quota/quotaDist", system_disk_quota_handleFileDistributionView)
  29. //Register Setting Interfaces
  30. //Register interface fow viewing the user storage quota
  31. registerSetting(settingModule{
  32. Name: "Storage Quota",
  33. Desc: "User Remaining Space",
  34. IconPath: "SystemAO/disk/quota/img/small_icon.png",
  35. Group: "Disk",
  36. StartDir: "SystemAO/disk/quota/quota.system",
  37. })
  38. //Register interface for admin to setup quota settings
  39. registerSetting(settingModule{
  40. Name: "Quota Settings",
  41. Desc: "Setup Group Storage Limit",
  42. IconPath: "SystemAO/disk/quota/img/small_icon.png",
  43. Group: "Disk",
  44. StartDir: "SystemAO/disk/quota/manage.html",
  45. RequireAdmin: true,
  46. })
  47. }
  48. //Get a list of quota on user groups and their storage limit
  49. func system_disk_quota_listQuota(w http.ResponseWriter, r *http.Request) {
  50. _, err := authAgent.GetUserName(w,r);
  51. if err != nil {
  52. sendErrorResponse(w, "User not logged in")
  53. return
  54. }
  55. isAdmin := system_permission_checkUserIsAdmin(w, r)
  56. if !isAdmin {
  57. sendErrorResponse(w, "Permission denied")
  58. return
  59. }
  60. groups := system_permission_listGroup()
  61. results := map[string]int64{}
  62. for _, group := range groups {
  63. quota, _ := system_disk_quota_getQuotaFromGroupname(group)
  64. results[group] = quota
  65. }
  66. jsonString, _ := json.Marshal(results)
  67. sendJSONResponse(w, string(jsonString))
  68. }
  69. //Check the storage quota on this usergroup. Return -1 for unlimited quota and 0 if error
  70. func system_disk_quota_getQuotaFromGroupname(groupname string) (int64, error) {
  71. //If administrator, always return -1
  72. if groupname == "administrator" {
  73. return -1, nil
  74. }
  75. //Check if group exists
  76. if !system_permission_groupExists(groupname) {
  77. return 0, errors.New("Group not exists")
  78. }
  79. //Group exists. Get the group quota from db
  80. groupQuota := int64(0)
  81. err := sysdb.Read("diskquota", "quota/"+groupname, &groupQuota)
  82. if err != nil {
  83. return 0, err
  84. }
  85. return groupQuota, nil
  86. }
  87. //Check if the given size can fit into the user remaining quota, return true if the file fit user quota
  88. func system_disk_quota_validateQuota(username string, filesize int64) bool {
  89. remaining, _, _, err := system_disk_quota_quotaInfo(username)
  90. if err != nil{
  91. log.Println("Upload failed for user: " + username + " " + err.Error())
  92. return false
  93. }
  94. //log.Println(remaining, filesize, err)
  95. if remaining == -1{
  96. //Unlimited quota. Always return true
  97. return true
  98. }else if (remaining == 0){
  99. //Read only account. Always return false
  100. return false
  101. }else if (remaining >= filesize ){
  102. //This file fits in the user's remaining space
  103. return true
  104. }else{
  105. return false
  106. }
  107. return false
  108. }
  109. //Check if the given path apply quota limitation
  110. func system_disk_quota_checkIfQuotaApply(path string, username string) bool{
  111. targetStoargeDevice, err := system_storage_getStorageByPath(path, username);
  112. if (err != nil){
  113. return false
  114. }
  115. if targetStoargeDevice.Hierarchy == "user"{
  116. //User Hierarchy Storage Device, count as user's private storage
  117. return true
  118. }
  119. //Not user's private storage. Calculate as public one
  120. return false
  121. }
  122. //Set the storage quota of the particular user
  123. func system_disk_quota_setQuota(w http.ResponseWriter, r *http.Request) {
  124. authed := authAgent.CheckAuth(r)
  125. if !authed {
  126. sendErrorResponse(w, "User not logged in")
  127. return
  128. }
  129. isAdmin := system_permission_checkUserIsAdmin(w, r)
  130. if !isAdmin {
  131. sendErrorResponse(w, "Permission denied")
  132. return
  133. }
  134. //OK to proceed
  135. groupname, err := mv(r, "groupname", true)
  136. if err != nil {
  137. sendErrorResponse(w, "Group name not defned")
  138. return
  139. }
  140. quotaSizeString, err := mv(r, "quota", true)
  141. if err != nil {
  142. sendErrorResponse(w, "Quota not defined")
  143. return
  144. }
  145. quotaSize, err := StringToInt64(quotaSizeString)
  146. if err != nil || quotaSize < 0 {
  147. sendErrorResponse(w, "Invalid quota size given")
  148. return
  149. }
  150. //Qutasize unit is in MB
  151. quotaSize = quotaSize << 20
  152. //Check groupname exists
  153. if !system_permission_groupExists(groupname) {
  154. sendErrorResponse(w, "Group name not exists. Given "+groupname)
  155. return
  156. }
  157. //Ok to proceed.
  158. err = sysdb.Write("diskquota", "quota/"+groupname, quotaSize)
  159. if err != nil {
  160. sendErrorResponse(w, err.Error())
  161. return
  162. }
  163. sendOK(w)
  164. }
  165. //Show the current user's quota information
  166. func system_disk_quota_handleQuotaInfo(w http.ResponseWriter, r *http.Request) {
  167. username, err := authAgent.GetUserName(w,r);
  168. if err != nil {
  169. sendErrorResponse(w, "User not logged in")
  170. return
  171. }
  172. remainingSpace, usedSpace, totalSpace, err := system_disk_quota_quotaInfo(username)
  173. type quotaInformation struct {
  174. Remaining int64
  175. Used int64
  176. Total int64
  177. }
  178. jsonString, _ := json.Marshal(quotaInformation{
  179. Remaining: remainingSpace,
  180. Used: usedSpace,
  181. Total: totalSpace,
  182. })
  183. sendJSONResponse(w, string(jsonString))
  184. }
  185. //Get all the users file and see how
  186. func system_disk_quota_handleFileDistributionView(w http.ResponseWriter, r *http.Request) {
  187. //Check if the user logged in
  188. username, err := authAgent.GetUserName(w,r);
  189. if err != nil {
  190. sendErrorResponse(w, "User not logged in")
  191. return
  192. }
  193. //Create a file distribution list
  194. fileDist := map[string]int64{}
  195. userpaths := system_storage_getUserDirectory(username)
  196. for _, thispath := range userpaths {
  197. filepath.Walk(thispath, func(filepath string, info os.FileInfo, err error) error {
  198. if err != nil {
  199. return err
  200. }
  201. if !info.IsDir() {
  202. mime, _, err := system_fs_getMime(filepath)
  203. if err != nil {
  204. return err
  205. }
  206. mediaType := strings.SplitN(mime, "/", 2)[0]
  207. mediaType = strings.Title(mediaType)
  208. fileDist[mediaType] = fileDist[mediaType] + info.Size()
  209. }
  210. return err
  211. })
  212. }
  213. //Sort the file according to the number of files in the
  214. type kv struct {
  215. Mime string
  216. Size int64
  217. }
  218. var ss []kv
  219. for k, v := range fileDist {
  220. ss = append(ss, kv{k, v})
  221. }
  222. sort.Slice(ss, func(i, j int) bool {
  223. return ss[i].Size > ss[j].Size
  224. })
  225. //Return the distrubution using json string
  226. jsonString, _ := json.Marshal(ss)
  227. sendJSONResponse(w, string(jsonString))
  228. }
  229. //Get the quota information of the current user. Return the followings
  230. /*
  231. Remaining space of the user quota (int64)
  232. Used space of the user quota (int64)
  233. Total theoretical space of the user quota (int64)
  234. Error (error). Standard error message if something goes wrong
  235. */
  236. func system_disk_quota_quotaInfo(username string) (int64, int64, int64, error) {
  237. //Get the user group information
  238. usergroup := system_permission_getUserPermissionGroup(username)
  239. groupExists := system_permission_groupExists(usergroup)
  240. if !groupExists {
  241. return 0, 0, 0, errors.New("User group not exists")
  242. }
  243. //Get the group quota information
  244. groupQuota := int64(-1)
  245. sysdb.Read("diskquota", "quota/"+usergroup, &groupQuota)
  246. //Calculate user limit
  247. userpaths := system_storage_getUserDirectory(username)
  248. totalUserUsedSpace := int64(0)
  249. for _, thispath := range userpaths {
  250. filepath.Walk(thispath, func(_ string, info os.FileInfo, err error) error {
  251. if err != nil {
  252. return err
  253. }
  254. if !info.IsDir() {
  255. totalUserUsedSpace += info.Size()
  256. }
  257. return err
  258. })
  259. }
  260. remainingSpace := groupQuota - totalUserUsedSpace
  261. if groupQuota == -1 {
  262. remainingSpace = -1
  263. }
  264. return remainingSpace, totalUserUsedSpace, groupQuota, nil
  265. }