diff --git a/src/client/settings.go b/src/client/settings.go index 6c5dbba..b9cc26c 100644 --- a/src/client/settings.go +++ b/src/client/settings.go @@ -59,3 +59,17 @@ func (cl *SettingsClient) ReportErrors(reports *settings.ClientErrorReports, tok Send(reports). End() } + +func (cl *SettingsClient) WorkerQueueLen(token *http.Cookie) (*http.Response, *settings.WorkerQueueLenResp, []error) { + resp, body, errs := cl.r.Get(cl.url("/v1/settings/workers/queue-len")). + AddCookie(token). + End() + + mResp := &settings.WorkerQueueLenResp{} + err := json.Unmarshal([]byte(body), mResp) + if err != nil { + errs = append(errs, err) + return nil, nil, errs + } + return resp, mResp, nil +} diff --git a/src/client/users.go b/src/client/users.go index b41fc73..eb5de39 100644 --- a/src/client/users.go +++ b/src/client/users.go @@ -187,3 +187,12 @@ func (cl *SingleUserClient) IsAuthed(token *http.Cookie) (*http.Response, string AddCookie(token). End() } + +func (cl *SingleUserClient) ResetUsedSpace(userID uint64, token *http.Cookie) (*http.Response, string, []error) { + return cl.r.Put(cl.url("/v1/users/used-space")). + Send(multiusers.ResetUsedSpaceReq{ + UserID: userID, + }). + AddCookie(token). + End() +} diff --git a/src/handlers/multiusers/handlers.go b/src/handlers/multiusers/handlers.go index 61e4a47..4e89fbc 100644 --- a/src/handlers/multiusers/handlers.go +++ b/src/handlers/multiusers/handlers.go @@ -37,49 +37,51 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error) apiACRules := map[string]bool{ // TODO: make these configurable // admin rules - apiRuleCname(userstore.AdminRole, "GET", "/"): true, - apiRuleCname(userstore.AdminRole, "GET", publicPath): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/users/login"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/users/logout"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/users/isauthed"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/pwd"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/pwd/force-set"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/users/"): true, - apiRuleCname(userstore.AdminRole, "DELETE", "/v1/users/"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/users/list"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/users/self"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/preferences"): true, - apiRuleCname(userstore.AdminRole, "PUT", "/v1/users//used-space"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/roles/"): true, - apiRuleCname(userstore.AdminRole, "DELETE", "/v1/roles/"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/roles/list"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/files"): true, - apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/files"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/files"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/chunks"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/files/chunks"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/copy"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/move"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/dirs"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/dirs/home"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/dirs"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/uploadings"): true, - apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/uploadings"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/metadata"): true, - apiRuleCname(userstore.AdminRole, "OPTIONS", "/v1/settings/health"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/settings/client"): true, - apiRuleCname(userstore.AdminRole, "PATCH", "/v1/settings/client"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/settings/errors"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/imgs"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/sharings"): true, - apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/sharings"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/exist"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/dirs"): true, - apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/ids"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true, + apiRuleCname(userstore.AdminRole, "GET", "/"): true, + apiRuleCname(userstore.AdminRole, "GET", publicPath): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/users/login"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/users/logout"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/users/isauthed"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/pwd"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/pwd/force-set"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/users/"): true, + apiRuleCname(userstore.AdminRole, "DELETE", "/v1/users/"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/users/list"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/users/self"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/users/preferences"): true, + apiRuleCname(userstore.AdminRole, "PUT", "/v1/users/used-space"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/roles/"): true, + apiRuleCname(userstore.AdminRole, "DELETE", "/v1/roles/"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/roles/list"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/files"): true, + apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/files"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/files"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/chunks"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/files/chunks"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/copy"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/fs/files/move"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/dirs"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/dirs/home"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/dirs"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/uploadings"): true, + apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/uploadings"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/metadata"): true, + apiRuleCname(userstore.AdminRole, "OPTIONS", "/v1/settings/health"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/settings/client"): true, + apiRuleCname(userstore.AdminRole, "PATCH", "/v1/settings/client"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/settings/errors"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/settings/workers/queue-len"): true, + + apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/imgs"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/sharings"): true, + apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/sharings"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/exist"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/dirs"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/ids"): true, + apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true, // user rules apiRuleCname(userstore.UserRole, "GET", "/"): true, diff --git a/src/handlers/settings/handlers.go b/src/handlers/settings/handlers.go index 01fd122..d0c7294 100644 --- a/src/handlers/settings/handlers.go +++ b/src/handlers/settings/handlers.go @@ -107,3 +107,13 @@ func (h *SettingsSvc) ReportErrors(c *gin.Context) { } c.JSON(q.Resp(200)) } + +type WorkerQueueLenResp struct { + QueueLen int `json:"queueLen"` +} + +func (h *SettingsSvc) WorkerQueueLen(c *gin.Context) { + c.JSON(200, &WorkerQueueLenResp{ + QueueLen: h.deps.Workers().QueueLen(), + }) +} diff --git a/src/server/server.go b/src/server/server.go index 98a59b7..e227f8a 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -319,6 +319,7 @@ func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.E settingsAPI.GET("/client", settingsSvc.GetClientCfg) settingsAPI.PATCH("/client", settingsSvc.SetClientCfg) settingsAPI.POST("/errors", settingsSvc.ReportErrors) + settingsAPI.GET("/workers/queue-len", settingsSvc.WorkerQueueLen) return router, nil } diff --git a/src/server/server_settings_test.go b/src/server/server_settings_test.go index ed8e4e8..b50d51e 100644 --- a/src/server/server_settings_test.go +++ b/src/server/server_settings_test.go @@ -182,4 +182,15 @@ func TestSettingsHandlers(t *testing.T) { t.Fatalf("log does not contain error: %s", content) } }) + + t.Run("WorkerQueueLen", func(t *testing.T) { + settingsCl := client.NewSettingsClient(addr) + + reportResp, _, errs := settingsCl.WorkerQueueLen(adminToken) + if len(errs) > 0 { + t.Fatal(errs) + } else if reportResp.StatusCode != 200 { + t.Fatal(reportResp.StatusCode) + } + }) } diff --git a/src/server/server_space_limit_test.go b/src/server/server_space_limit_test.go index 18fa065..8e250be 100644 --- a/src/server/server_space_limit_test.go +++ b/src/server/server_space_limit_test.go @@ -4,7 +4,9 @@ import ( "fmt" "os" "path/filepath" + "strconv" "testing" + "time" "github.com/ihexxa/quickshare/src/client" "github.com/ihexxa/quickshare/src/db/userstore" @@ -29,7 +31,14 @@ func TestSpaceLimit(t *testing.T) { "downloadSpeedLimit": 4096000, "spaceLimit": %d, "limiterCapacity": 1000, - "limiterCyc": 1000 + "limiterCyc": 1000, + "predefinedUsers": [ + { + "name": "test", + "pwd": "test", + "role": "admin" + } + ] }, "server": { "debug": true, @@ -220,6 +229,66 @@ func TestSpaceLimit(t *testing.T) { } }) + t.Run("ResetUsedSpace", func(t *testing.T) { + usersCl := client.NewSingleUserClient(addr) + resp, _, errs := usersCl.Login("test", "test") + if len(errs) > 0 { + t.Fatal(errs) + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) + } + token := client.GetCookie(resp.Cookies(), q.TokenCookie) + + ok := assertUploadOK(t, "test/files/spacelimit/byte1", "0", addr, token) + if !ok { + t.Fatal("upload failed") + } + + resp, selfResp, errs := usersCl.Self(token) + if len(errs) > 0 { + t.Fatal(errs) + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) + } + originalUsedSpace := selfResp.UsedSpace + + uidInt, err := strconv.ParseUint(selfResp.ID, 10, 64) + if err != nil { + t.Fatal(err) + } + resp, _, errs = usersCl.ResetUsedSpace(uidInt, token) + if len(errs) > 0 { + t.Fatal(errs) + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) + } + + settingsCl := client.NewSettingsClient(addr) + for i := 0; i < 20; i++ { + resp, wqlResp, errs := settingsCl.WorkerQueueLen(token) + if len(errs) > 0 { + t.Fatal(errs) + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) + } + + if wqlResp.QueueLen == 0 { + break + } + + time.Sleep(200) + } + + resp, selfResp, errs = usersCl.Self(token) + if len(errs) > 0 { + t.Fatal(errs) + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) + } else if selfResp.UsedSpace != originalUsedSpace { + t.Fatalf("used space not equal %d %d", selfResp.UsedSpace, originalUsedSpace) + } + }) + resp, _, errs = usersCl.Logout(token) if len(errs) > 0 { t.Fatal(errs) diff --git a/src/server/testdata/test_quickshare.db b/src/server/testdata/test_quickshare.db index 1d9535b..20304f1 100644 Binary files a/src/server/testdata/test_quickshare.db and b/src/server/testdata/test_quickshare.db differ diff --git a/src/worker/interface.go b/src/worker/interface.go index f060b60..dcf7326 100644 --- a/src/worker/interface.go +++ b/src/worker/interface.go @@ -25,4 +25,5 @@ type IWorkerPool interface { Stop() AddHandler(msgType string, handler MsgHandler) DelHandler(msgType string) + QueueLen() int } diff --git a/src/worker/localworker/worker.go b/src/worker/localworker/worker.go index f4237bf..a6635a5 100644 --- a/src/worker/localworker/worker.go +++ b/src/worker/localworker/worker.go @@ -174,3 +174,7 @@ func (wp *WorkerPool) AddHandler(msgType string, handler worker.MsgHandler) { func (wp *WorkerPool) DelHandler(msgType string) { delete(wp.msgHandlers, msgType) } + +func (wp *WorkerPool) QueueLen() int { + return len(wp.queue) +}