From becfa688aa11fdc0c4128aca0f9b36ec22e68c29 Mon Sep 17 00:00:00 2001 From: hexxa Date: Sat, 24 Sep 2022 11:54:35 +0800 Subject: [PATCH] fix(server): clean up handlers initing --- src/server/init_deps.go | 173 +++++++++++++++++++----------------- src/server/init_handlers.go | 125 ++++++++++++++++++++++++++ src/server/server.go | 120 +------------------------ 3 files changed, 221 insertions(+), 197 deletions(-) create mode 100644 src/server/init_handlers.go diff --git a/src/server/init_deps.go b/src/server/init_deps.go index 2d31a1a..61fada5 100644 --- a/src/server/init_deps.go +++ b/src/server/init_deps.go @@ -12,30 +12,52 @@ import ( "github.com/gin-gonic/gin" "github.com/ihexxa/gocfg" "github.com/ihexxa/quickshare/src/db/rdb/sqlite" + "github.com/ihexxa/quickshare/src/worker" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "golang.org/x/crypto/bcrypt" + "github.com/ihexxa/quickshare/src/cryptoutil" "github.com/ihexxa/quickshare/src/cryptoutil/jwt" "github.com/ihexxa/quickshare/src/db" "github.com/ihexxa/quickshare/src/depidx" "github.com/ihexxa/quickshare/src/fs" "github.com/ihexxa/quickshare/src/fs/local" + "github.com/ihexxa/quickshare/src/idgen" "github.com/ihexxa/quickshare/src/idgen/simpleidgen" "github.com/ihexxa/quickshare/src/iolimiter" "github.com/ihexxa/quickshare/src/search/fileindex" "github.com/ihexxa/quickshare/src/worker/localworker" ) -func InitCfg(cfg gocfg.ICfg, logger *zap.SugaredLogger) (gocfg.ICfg, error) { - _, ok := cfg.String("ENV.TOKENSECRET") - if !ok { - cfg.SetString("ENV.TOKENSECRET", makeRandToken()) - logger.Info("warning: TOKENSECRET is not set, generated a random token") +func initDeps(cfg gocfg.ICfg) *depidx.Deps { + ider := simpleidgen.New() + logger := initLogger(cfg) + jwtEncDec := initJWT(cfg, logger) + workers := initWorkerPool(cfg, logger) + filesystem, err := initFs(cfg, ider, logger) + if err != nil { + logger.Fatalf("failed to init DB: %s", err) } + quickshareDb, err := initDb(cfg, filesystem) + if err != nil { + logger.Fatalf("failed to init DB: %s", err) + } + rateLimiter := initRateLimiter(cfg, quickshareDb) + fileIndex := initSearchIndex(cfg, filesystem, logger) - return cfg, nil + deps := depidx.NewDeps(cfg) + deps.SetDB(quickshareDb) + deps.SetFS(filesystem) + deps.SetToken(jwtEncDec) + deps.SetID(ider) + deps.SetLog(logger) + deps.SetLimiter(rateLimiter) + deps.SetWorkers(workers) + deps.SetFileIndex(fileIndex) + + return deps } func initLogger(cfg gocfg.ICfg) *zap.SugaredLogger { @@ -57,16 +79,70 @@ func initLogger(cfg gocfg.ICfg) *zap.SugaredLogger { return zap.New(core).Sugar() } -func makeRandToken() string { - b := make([]byte, 32) - _, err := rand.Read(b) - if err != nil { - panic(fmt.Sprintf("make rand token error: %s", err)) +func initJWT(cfg gocfg.ICfg, logger *zap.SugaredLogger) cryptoutil.ITokenEncDec { + secret, ok := cfg.String("ENV.TOKENSECRET") + if !ok { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + panic(fmt.Sprintf("make rand token error: %s", err)) + } + secret = string(b) + logger.Info("warning: TOKENSECRET is not set, a random token is generated") } - return string(b) + + return jwt.NewJWTEncDec(secret) } -func mkRoot(rootPath string, logger *zap.SugaredLogger) { +func initRateLimiter(cfg gocfg.ICfg, quickshareDb db.IDBQuickshare) iolimiter.ILimiter { + limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) + limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) + return iolimiter.NewIOLimiter(limiterCap, limiterCyc, quickshareDb) +} + +func initWorkerPool(cfg gocfg.ICfg, logger *zap.SugaredLogger) worker.IWorkerPool { + queueSize := cfg.GrabInt("Workers.QueueSize") + sleepCyc := cfg.GrabInt("Workers.SleepCyc") + workerCount := cfg.GrabInt("Workers.WorkerCount") + + workers := localworker.NewWorkerPool(queueSize, sleepCyc, workerCount, logger) + workers.Start() + return workers +} + +func initSearchIndex(cfg gocfg.ICfg, filesystem fs.ISimpleFS, logger *zap.SugaredLogger) fileindex.IFileIndex { + searchResultLimit := cfg.GrabInt("Server.SearchResultLimit") + fileIndex := fileindex.NewFileTreeIndex(filesystem, "/", searchResultLimit) + + indexInited := false + indexInfo, err := filesystem.Stat(fileIndexPath) + if err != nil { + if !os.IsNotExist(err) { + logger.Warnf("detect index file error: %s", err) + } else { + logger.Warnf("index file not found") + } + } else if indexInfo.IsDir() { + logger.Warnf("file index is a folder, not a file: %s", fileIndexPath) + } else { + err = fileIndex.ReadFrom(fileIndexPath) + if err != nil { + logger.Warnf("failed to load file index: %s", err) + } else { + indexInited = true + } + } + + logger.Infof("file index inited(%t)", indexInited) + return fileIndex +} + +func initFs(cfg gocfg.ICfg, idGenerator idgen.IIDGen, logger *zap.SugaredLogger) (fs.ISimpleFS, error) { + rootPath := cfg.GrabString("Fs.Root") + opensLimit := cfg.GrabInt("Fs.OpensLimit") + openTTL := cfg.GrabInt("Fs.OpenTTL") + readerTTL := cfg.GrabInt("Server.WriteTimeout") / 1000 // millisecond -> second + info, err := os.Stat(rootPath) if err != nil { if os.IsNotExist(err) { @@ -80,76 +156,11 @@ func mkRoot(rootPath string, logger *zap.SugaredLogger) { } else if !info.IsDir() { logger.Fatalf("can not create %s folder: there is a file with same name", rootPath) } + + return local.NewLocalFS(rootPath, 0660, opensLimit, openTTL, readerTTL, idGenerator), nil } -func initDeps(cfg gocfg.ICfg) *depidx.Deps { - var err error - logger := initLogger(cfg) - - rootPath := cfg.GrabString("Fs.Root") - mkRoot(rootPath, logger) - opensLimit := cfg.GrabInt("Fs.OpensLimit") - openTTL := cfg.GrabInt("Fs.OpenTTL") - readerTTL := cfg.GrabInt("Server.WriteTimeout") / 1000 // millisecond -> second - ider := simpleidgen.New() - filesystem := local.NewLocalFS(rootPath, 0660, opensLimit, openTTL, readerTTL, ider) - - secret, _ := cfg.String("ENV.TOKENSECRET") - jwtEncDec := jwt.NewJWTEncDec(secret) - - quickshareDb, err := initDB(cfg, filesystem) - if err != nil { - logger.Errorf("failed to init DB: %s", err) - os.Exit(1) - } - - limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) - limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) - limiter := iolimiter.NewIOLimiter(limiterCap, limiterCyc, quickshareDb) - - deps := depidx.NewDeps(cfg) - deps.SetDB(quickshareDb) - deps.SetFS(filesystem) - deps.SetToken(jwtEncDec) - deps.SetID(ider) - deps.SetLog(logger) - deps.SetLimiter(limiter) - - queueSize := cfg.GrabInt("Workers.QueueSize") - sleepCyc := cfg.GrabInt("Workers.SleepCyc") - workerCount := cfg.GrabInt("Workers.WorkerCount") - - workers := localworker.NewWorkerPool(queueSize, sleepCyc, workerCount, logger) - workers.Start() - deps.SetWorkers(workers) - - searchResultLimit := cfg.GrabInt("Server.SearchResultLimit") - fileIndex := fileindex.NewFileTreeIndex(filesystem, "/", searchResultLimit) - indexInfo, err := filesystem.Stat(fileIndexPath) - indexInited := false - if err != nil { - if !os.IsNotExist(err) { - logger.Warnf("failed to detect file index: %s", err) - } else { - logger.Warnf("no file index found") - } - } else if indexInfo.IsDir() { - logger.Warnf("file index is folder, not file: %s", fileIndexPath) - } else { - err = fileIndex.ReadFrom(fileIndexPath) - if err != nil { - logger.Infof("failed to load file index: %s", err) - } else { - indexInited = true - } - } - logger.Infof("file index inited(%t)", indexInited) - deps.SetFileIndex(fileIndex) - - return deps -} - -func initDB(cfg gocfg.ICfg, filesystem fs.ISimpleFS) (db.IDBQuickshare, error) { +func initDb(cfg gocfg.ICfg, filesystem fs.ISimpleFS) (db.IDBQuickshare, error) { dbPath := cfg.GrabString("Db.DbPath") dbDir := path.Dir(dbPath) diff --git a/src/server/init_handlers.go b/src/server/init_handlers.go new file mode 100644 index 0000000..95f5003 --- /dev/null +++ b/src/server/init_handlers.go @@ -0,0 +1,125 @@ +package server + +import ( + "context" + "errors" + "fmt" + + "github.com/gin-contrib/static" + "github.com/gin-gonic/gin" + "github.com/ihexxa/gocfg" + + "github.com/ihexxa/quickshare/src/depidx" + "github.com/ihexxa/quickshare/src/handlers/fileshdr" + "github.com/ihexxa/quickshare/src/handlers/multiusers" + "github.com/ihexxa/quickshare/src/handlers/settings" + qsstatic "github.com/ihexxa/quickshare/static" +) + +func initHandlers(cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { + router := gin.Default() + + // handlers + userHdrs, err := multiusers.NewMultiUsersSvc(cfg, deps) + if err != nil { + return nil, fmt.Errorf("new users svc error: %w", err) + } + + adminName := cfg.GrabString("ENV.DEFAULTADMIN") + _, err = userHdrs.Init(context.TODO(), adminName) + if err != nil { + return nil, fmt.Errorf("failed to init user handlers: %w", err) + } + + fileHdrs, err := fileshdr.NewFileHandlers(cfg, deps) + if err != nil { + return nil, fmt.Errorf("new files service error: %w", err) + } + settingsSvc, err := settings.NewSettingsSvc(cfg, deps) + if err != nil { + return nil, fmt.Errorf("new setting service error: %w", err) + } + + // middlewares + router.Use(userHdrs.AuthN()) + router.Use(userHdrs.APIAccessControl()) + + publicPath, ok := cfg.String("Server.PublicPath") + if !ok || publicPath == "" { + return nil, errors.New("publicPath not found or empty") + } + if cfg.BoolOr("Server.Debug", false) { + router.Use(static.Serve("/", static.LocalFile(publicPath, false))) + } else { + embedFs, err := qsstatic.NewEmbedStaticFS() + if err != nil { + return nil, err + } + router.Use(static.Serve("/", embedFs)) + } + + // handlers + v1 := router.Group("/v1") + + usersAPI := v1.Group("/users") + usersAPI.POST("/login", userHdrs.Login) + usersAPI.POST("/logout", userHdrs.Logout) + usersAPI.GET("/isauthed", userHdrs.IsAuthed) + usersAPI.PATCH("/pwd", userHdrs.SetPwd) + usersAPI.PATCH("/pwd/force-set", userHdrs.ForceSetPwd) + usersAPI.POST("/", userHdrs.AddUser) + usersAPI.DELETE("/", userHdrs.DelUser) + usersAPI.GET("/list", userHdrs.ListUsers) + usersAPI.GET("/self", userHdrs.Self) + usersAPI.PATCH("/", userHdrs.SetUser) + usersAPI.PATCH("/preferences", userHdrs.SetPreferences) + usersAPI.PUT("/used-space", userHdrs.ResetUsedSpace) + + rolesAPI := v1.Group("/roles") + // rolesAPI.POST("/", userHdrs.AddRole) + // rolesAPI.DELETE("/", userHdrs.DelRole) + rolesAPI.GET("/list", userHdrs.ListRoles) + + captchaAPI := v1.Group("/captchas") + captchaAPI.GET("/", userHdrs.GetCaptchaID) + captchaAPI.GET("/imgs", userHdrs.GetCaptchaImg) + + filesAPI := v1.Group("/fs") + filesAPI.POST("/files", fileHdrs.Create) + filesAPI.DELETE("/files", fileHdrs.Delete) + filesAPI.GET("/files", fileHdrs.Download) + filesAPI.PATCH("/files/chunks", fileHdrs.UploadChunk) + filesAPI.GET("/files/chunks", fileHdrs.UploadStatus) + filesAPI.PATCH("/files/copy", fileHdrs.Copy) + filesAPI.PATCH("/files/move", fileHdrs.Move) + + filesAPI.GET("/dirs", fileHdrs.List) + filesAPI.GET("/dirs/home", fileHdrs.ListHome) + filesAPI.POST("/dirs", fileHdrs.Mkdir) + // files.POST("/dirs/copy", fileHdrs.CopyDir) + + filesAPI.GET("/uploadings", fileHdrs.ListUploadings) + filesAPI.DELETE("/uploadings", fileHdrs.DelUploading) + + filesAPI.POST("/sharings", fileHdrs.AddSharing) + filesAPI.DELETE("/sharings", fileHdrs.DelSharing) + filesAPI.GET("/sharings", fileHdrs.ListSharings) + filesAPI.GET("/sharings/ids", fileHdrs.ListSharingIDs) + filesAPI.GET("/sharings/exist", fileHdrs.IsSharing) + filesAPI.GET("/sharings/dirs", fileHdrs.GetSharingDir) + + filesAPI.GET("/metadata", fileHdrs.Metadata) + filesAPI.GET("/search", fileHdrs.SearchItems) + filesAPI.PUT("/reindex", fileHdrs.Reindex) + + filesAPI.POST("/hashes/sha1", fileHdrs.GenerateHash) + + settingsAPI := v1.Group("/settings") + settingsAPI.OPTIONS("/health", settingsSvc.Health) + 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.go b/src/server/server.go index 3508933..cb492ba 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -2,8 +2,7 @@ package server import ( "context" - "crypto/rand" - "errors" + "fmt" "net/http" "os" @@ -12,17 +11,12 @@ import ( "syscall" "time" - "github.com/gin-contrib/static" "github.com/gin-gonic/gin" "github.com/ihexxa/gocfg" "github.com/ihexxa/quickshare/src/depidx" "github.com/ihexxa/quickshare/src/fs" - "github.com/ihexxa/quickshare/src/handlers/fileshdr" - "github.com/ihexxa/quickshare/src/handlers/multiusers" - "github.com/ihexxa/quickshare/src/handlers/settings" "github.com/ihexxa/quickshare/src/kvstore" - qsstatic "github.com/ihexxa/quickshare/static" ) type Server struct { @@ -38,9 +32,7 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { } deps := initDeps(cfg) - router := gin.Default() - adminName := cfg.GrabString("ENV.DEFAULTADMIN") - router, err := initHandlers(router, adminName, cfg, deps) + router, err := initHandlers(cfg, deps) if err != nil { return nil, fmt.Errorf("init handlers error: %w", err) } @@ -50,10 +42,11 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { if ok && portStr != "" { port, err = strconv.Atoi(portStr) if err != nil { - panic(fmt.Sprintf("invalid port: %s", portStr)) + deps.Log().Fatalf("invalid port: %s", portStr) } cfg.SetInt("Server.Port", port) } + srv := &http.Server{ Addr: fmt.Sprintf("%s:%d", cfg.GrabString("Server.Host"), port), Handler: router, @@ -69,111 +62,6 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { }, nil } -func initHandlers(router *gin.Engine, adminName string, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { - // handlers - userHdrs, err := multiusers.NewMultiUsersSvc(cfg, deps) - if err != nil { - return nil, fmt.Errorf("new users svc error: %w", err) - } - - _, err = userHdrs.Init(context.TODO(), adminName) - if err != nil { - return nil, fmt.Errorf("failed to init user handlers: %w", err) - } - - fileHdrs, err := fileshdr.NewFileHandlers(cfg, deps) - if err != nil { - return nil, fmt.Errorf("new files service error: %w", err) - } - settingsSvc, err := settings.NewSettingsSvc(cfg, deps) - if err != nil { - return nil, fmt.Errorf("new setting service error: %w", err) - } - - // middlewares - router.Use(userHdrs.AuthN()) - router.Use(userHdrs.APIAccessControl()) - - publicPath, ok := cfg.String("Server.PublicPath") - if !ok || publicPath == "" { - return nil, errors.New("publicPath not found or empty") - } - if cfg.BoolOr("Server.Debug", false) { - router.Use(static.Serve("/", static.LocalFile(publicPath, false))) - } else { - embedFs, err := qsstatic.NewEmbedStaticFS() - if err != nil { - return nil, err - } - router.Use(static.Serve("/", embedFs)) - } - - // handlers - v1 := router.Group("/v1") - - usersAPI := v1.Group("/users") - usersAPI.POST("/login", userHdrs.Login) - usersAPI.POST("/logout", userHdrs.Logout) - usersAPI.GET("/isauthed", userHdrs.IsAuthed) - usersAPI.PATCH("/pwd", userHdrs.SetPwd) - usersAPI.PATCH("/pwd/force-set", userHdrs.ForceSetPwd) - usersAPI.POST("/", userHdrs.AddUser) - usersAPI.DELETE("/", userHdrs.DelUser) - usersAPI.GET("/list", userHdrs.ListUsers) - usersAPI.GET("/self", userHdrs.Self) - usersAPI.PATCH("/", userHdrs.SetUser) - usersAPI.PATCH("/preferences", userHdrs.SetPreferences) - usersAPI.PUT("/used-space", userHdrs.ResetUsedSpace) - - rolesAPI := v1.Group("/roles") - // rolesAPI.POST("/", userHdrs.AddRole) - // rolesAPI.DELETE("/", userHdrs.DelRole) - rolesAPI.GET("/list", userHdrs.ListRoles) - - captchaAPI := v1.Group("/captchas") - captchaAPI.GET("/", userHdrs.GetCaptchaID) - captchaAPI.GET("/imgs", userHdrs.GetCaptchaImg) - - filesAPI := v1.Group("/fs") - filesAPI.POST("/files", fileHdrs.Create) - filesAPI.DELETE("/files", fileHdrs.Delete) - filesAPI.GET("/files", fileHdrs.Download) - filesAPI.PATCH("/files/chunks", fileHdrs.UploadChunk) - filesAPI.GET("/files/chunks", fileHdrs.UploadStatus) - filesAPI.PATCH("/files/copy", fileHdrs.Copy) - filesAPI.PATCH("/files/move", fileHdrs.Move) - - filesAPI.GET("/dirs", fileHdrs.List) - filesAPI.GET("/dirs/home", fileHdrs.ListHome) - filesAPI.POST("/dirs", fileHdrs.Mkdir) - // files.POST("/dirs/copy", fileHdrs.CopyDir) - - filesAPI.GET("/uploadings", fileHdrs.ListUploadings) - filesAPI.DELETE("/uploadings", fileHdrs.DelUploading) - - filesAPI.POST("/sharings", fileHdrs.AddSharing) - filesAPI.DELETE("/sharings", fileHdrs.DelSharing) - filesAPI.GET("/sharings", fileHdrs.ListSharings) - filesAPI.GET("/sharings/ids", fileHdrs.ListSharingIDs) - filesAPI.GET("/sharings/exist", fileHdrs.IsSharing) - filesAPI.GET("/sharings/dirs", fileHdrs.GetSharingDir) - - filesAPI.GET("/metadata", fileHdrs.Metadata) - filesAPI.GET("/search", fileHdrs.SearchItems) - filesAPI.PUT("/reindex", fileHdrs.Reindex) - - filesAPI.POST("/hashes/sha1", fileHdrs.GenerateHash) - - settingsAPI := v1.Group("/settings") - settingsAPI.OPTIONS("/health", settingsSvc.Health) - 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 -} - func (s *Server) Start() error { s.signalChan = make(chan os.Signal, 4) signal.Notify(s.signalChan, syscall.SIGINT, syscall.SIGTERM)