diff --git a/configs/dev.yml b/configs/dev.yml index ee933ea..6ad8454 100644 --- a/configs/dev.yml +++ b/configs/dev.yml @@ -15,6 +15,11 @@ server: captchaWidth: 256 captchaHeight: 60 captchaEnabled: true + uploadSpeedLimit: 409600 # 400KB/limiterCyc + downloadSpeedLimit: 409600 # 400KB/limiterCyc + spaceLimit: 1024 # 1GB + limiterCapacity: 1000 + limiterCyc: 1000 # 1s users: enableAuth: true defaultAdmin: "" diff --git a/configs/docker.yml b/configs/docker.yml index 780f9b6..990fd50 100644 --- a/configs/docker.yml +++ b/configs/docker.yml @@ -15,6 +15,11 @@ server: captchaWidth: 256 captchaHeight: 60 captchaEnabled: true + uploadSpeedLimit: 102400 # 100k/limiterCyc + downloadSpeedLimit: 102400 # 100k/limiterCyc + spaceLimit: 1024 # 1GB + limiterCapacity: 1000 + limiterCyc: 1000 # 1s users: enableAuth: true defaultAdmin: "" diff --git a/src/client/web/src/client/index.ts b/src/client/web/src/client/index.ts index 7b2f9c5..fa91ea0 100644 --- a/src/client/web/src/client/index.ts +++ b/src/client/web/src/client/index.ts @@ -3,11 +3,18 @@ import axios, { AxiosRequestConfig } from "axios"; export const defaultTimeout = 10000; export const userIDParam = "uid"; +export interface Quota { + spaceLimit: number; + uploadSpeedLimit: number; + downloadSpeedLimit: number; +} + export interface User { id: string; name: string; pwd: string; role: string; + quota: Quota; } export interface ListUsersResp { diff --git a/src/client/web/src/components/pane_admin.tsx b/src/client/web/src/components/pane_admin.tsx index 0d29ada..ca11e8c 100644 --- a/src/client/web/src/components/pane_admin.tsx +++ b/src/client/web/src/components/pane_admin.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { Map, Set } from "immutable"; import { ICoreState } from "./core_state"; -import { User } from "../client"; +import { User, Quota } from "../client"; import { Updater as PanesUpdater } from "./panes"; export interface Props { @@ -16,6 +16,7 @@ export interface UserFormProps { id: string; name: string; role: string; + quota: Quota; roles: Set; update?: (updater: (prevState: ICoreState) => ICoreState) => void; } @@ -102,9 +103,14 @@ export class UserForm extends React.Component< flexDirection: "column", }} > -
Name: {this.props.name}
+
+ Name: {this.props.name} / ID: {this.props.id} / Role:{" "} + {this.props.role} +
- ID: {this.props.id} / Role: {this.props.role} + SpaceLimit: {this.props.quota.spaceLimit} / UploadSpeedLimit:{" "} + {this.props.quota.uploadSpeedLimit} / DownloadSpeedLimit:{" "} + {this.props.quota.downloadSpeedLimit}
@@ -169,7 +175,6 @@ export class UserForm extends React.Component< Update - ); } @@ -255,6 +260,7 @@ export class AdminPane extends React.Component { name: this.state.newUserName, pwd: this.state.newUserPwd1, role: this.state.newUserRole, + quota: undefined, }) .then((ok: boolean) => { if (!ok) { @@ -282,6 +288,7 @@ export class AdminPane extends React.Component { id={user.id} name={user.name} role={user.role} + quota={user.quota} roles={this.props.roles} update={this.props.update} /> diff --git a/src/golimiter/limiter.go b/src/golimiter/limiter.go index 2a5eb06..97444bd 100644 --- a/src/golimiter/limiter.go +++ b/src/golimiter/limiter.go @@ -2,7 +2,6 @@ package golimiter import ( "fmt" - // "math" "sync" "time" ) @@ -36,7 +35,6 @@ func (b *Bucket) Access(cyc, incr, decr int) bool { After(now) { return false } - fmt.Println(4) b.token = incr - decr b.refreshedAt = now return true diff --git a/src/golimiter/limiter_test.go b/src/golimiter/limiter_test.go index 86203bd..105f32f 100644 --- a/src/golimiter/limiter_test.go +++ b/src/golimiter/limiter_test.go @@ -42,144 +42,3 @@ func TestLimiter(t *testing.T) { } }) } - -// var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) - -// const rndCap = 10000 -// const addCap = 1 - -// // how to set time -// // extend: wait can be greater than ttl/2 -// // cyc is smaller than ttl and wait, then it can be clean in time -// const cap = 40 -// const ttl = 3 -// const cyc = 1 -// const bucketCap = 2 -// const id1 = "id1" -// const id2 = "id2" -// const op1 int16 = 0 -// const op2 int16 = 1 - -// var customCaps = map[int16]int16{ -// op2: 1000, -// } - -// const wait = 1 - -// var limiter = New(cap, cyc, bucketCap) - -// var idSeed = 0 - -// func randId() string { -// idSeed++ -// return fmt.Sprintf("%d", idSeed) -// } - -// func TestAccess(t *testing.T) { -// func(t *testing.T) { -// canAccess := limiter.Access(id1) -// if !canAccess { -// t.Fatal("access: fail") -// } - -// for i := 0; i < bucketCap; i++ { -// canAccess = limiter.Access(id1) -// } - -// if canAccess { -// t.Fatal("access: fail to deny access") -// } - -// time.Sleep(time.Duration(limiter.GetCyc()) * time.Second) - -// canAccess = limiter.Access(id1) -// if !canAccess { -// t.Fatal("access: fail to refresh tokens") -// } -// }(t) -// } - -// func TestCap(t *testing.T) { -// originalCap := limiter.GetCap() -// fmt.Printf("cap:info: %d\n", originalCap) - -// ok := limiter.ExpandCap(originalCap + addCap) - -// if !ok || limiter.GetCap() != originalCap+addCap { -// t.Fatal("cap: fail to expand") -// } - -// ok = limiter.ExpandCap(limiter.GetSize() - addCap) -// if ok { -// t.Fatal("cap: shrink cap") -// } - -// ids := []string{} -// for limiter.GetSize() < limiter.GetCap() { -// id := randId() -// ids = append(ids, id) - -// ok := limiter.Access(id, 0) -// if !ok { -// t.Fatal("cap: not full") -// } -// } - -// if limiter.GetSize() != limiter.GetCap() { -// t.Fatal("cap: incorrect size") -// } - -// if limiter.Access(randId(), 0) { -// t.Fatal("cap: more than cap") -// } - -// limiter.truncate() -// } - -// func TestTtl(t *testing.T) { -// var addTtl int32 = 1 -// originalTTL := limiter.GetTTL() -// fmt.Printf("ttl:info: %d\n", originalTTL) - -// limiter.UpdateTTL(originalTTL + addTtl) -// if limiter.GetTTL() != originalTTL+addTtl { -// t.Fatal("ttl: update fail") -// } -// } - -// func cycTest(t *testing.T) { -// var addCyc int32 = 1 -// originalCyc := limiter.GetCyc() -// fmt.Printf("cyc:info: %d\n", originalCyc) - -// limiter.UpdateCyc(originalCyc + addCyc) -// if limiter.GetCyc() != originalCyc+addCyc { -// t.Fatal("cyc: update fail") -// } -// } - -// func autoCleanTest(t *testing.T) { -// ids := []string{ -// randId(), -// randId(), -// } - -// for _, id := range ids { -// ok := limiter.Access(id, 0) -// if ok { -// t.Fatal("autoClean: warning: add fail") -// } -// } - -// time.Sleep(time.Duration(limiter.GetTTL()+wait) * time.Second) - -// for _, id := range ids { -// _, exist := limiter.get(id) -// if exist { -// t.Fatal("autoClean: item still exist") -// } -// } -// } - -// func snapshotTest(t *testing.T) { -// } diff --git a/src/handlers/fileshdr/handlers.go b/src/handlers/fileshdr/handlers.go index fed1c68..757d7f1 100644 --- a/src/handlers/fileshdr/handlers.go +++ b/src/handlers/fileshdr/handlers.go @@ -301,7 +301,7 @@ func (h *FileHandlers) UploadChunk(c *gin.Context) { c.JSON(q.ErrResp(c, 500, err)) return } else if !ok { - c.JSON(q.ErrResp(c, 503, errors.New("retry later"))) + c.JSON(q.ErrResp(c, 429, errors.New("retry later"))) return } diff --git a/src/handlers/multiusers/handlers.go b/src/handlers/multiusers/handlers.go index 382b4f0..6879c0a 100644 --- a/src/handlers/multiusers/handlers.go +++ b/src/handlers/multiusers/handlers.go @@ -354,18 +354,15 @@ func (h *MultiUsersSvc) AddUser(c *gin.Context) { return } - spaceLimit := h.cfg.IntOr("Users.SpaceLimit", 1024) - uploadSpeedLimit := h.cfg.IntOr("Users.UploadSpeedLimit", 100*1024) - downloadSpeedLimit := h.cfg.IntOr("Users.DownloadSpeedLimit", 100*1024) err = h.deps.Users().AddUser(&userstore.User{ ID: uid, Name: req.Name, Pwd: string(pwdHash), Role: req.Role, Quota: &userstore.Quota{ - SpaceLimit: spaceLimit, - UploadSpeedLimit: uploadSpeedLimit, - DownloadSpeedLimit: downloadSpeedLimit, + SpaceLimit: h.cfg.IntOr("Users.SpaceLimit", 1024), + UploadSpeedLimit: h.cfg.IntOr("Users.UploadSpeedLimit", 100*1024), + DownloadSpeedLimit: h.cfg.IntOr("Users.DownloadSpeedLimit", 100*1024), }, }) if err != nil { diff --git a/src/server/config.go b/src/server/config.go index f112605..4cf7045 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -23,6 +23,8 @@ type UsersCfg struct { UploadSpeedLimit int `json:"uploadSpeedLimit" yaml:"uploadSpeedLimit"` DownloadSpeedLimit int `json:"downloadSpeedLimit" yaml:"downloadSpeedLimit"` SpaceLimit int `json:"spaceLimit" yaml:"spaceLimit"` + LimiterCapacity int `json:"limiterCapacity" yaml:"limiterCapacity"` + LimiterCyc int `json:"limiterCyc" yaml:"limiterCyc"` } type Secrets struct { @@ -72,6 +74,8 @@ func DefaultConfig() (string, error) { UploadSpeedLimit: 100 * 1024, // B DownloadSpeedLimit: 100 * 1024, // B SpaceLimit: 1024, // GB + LimiterCapacity: 1000, + LimiterCyc: 1000, // 1s }, Secrets: &Secrets{ TokenSecret: "", diff --git a/src/server/server.go b/src/server/server.go index a20f39f..62e66d6 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -105,8 +105,8 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps { panic(fmt.Sprintf("fail to init user store: %s", err)) } - limiterCap := cfg.IntOr("Users.LimiterCapacity", 4096) - limiterCyc := cfg.IntOr("Users.LimiterCyc", 3000) + limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) + limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) limiter := iolimiter.NewIOLimiter(limiterCap, limiterCyc, users) deps := depidx.NewDeps(cfg) diff --git a/src/userstore/user_store.go b/src/userstore/user_store.go index 2f84588..48d1e95 100644 --- a/src/userstore/user_store.go +++ b/src/userstore/user_store.go @@ -20,9 +20,9 @@ const ( RoleListNs = "roleList" InitTimeKey = "initTime" - defaultSpaceLimit = 1024 // 1GB - defaultUploadSpeedLimit = 100 * 1024 // 100KB - defaultDownloadSpeedLimit = 100 * 1024 // 100KB + defaultSpaceLimit = 1024 // 1GB + defaultUploadSpeedLimit = 50 * 1024 * 1024 // 50MB + defaultDownloadSpeedLimit = 50 * 1024 * 1024 // 50MB ) type Quota struct {