From 261e92a659a4d1af97d11ecadd600b169debf825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Wed, 17 Sep 2025 05:09:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ace/wire_gen.go | 6 +++--- cmd/cli/wire_gen.go | 4 ++-- go.mod | 4 ++-- go.sum | 10 ++++++++++ internal/bootstrap/db.go | 19 ++++++++++++++++--- internal/bootstrap/session.go | 4 ---- internal/http/middleware/middleware.go | 20 +++++++++++++++----- internal/http/middleware/must_login.go | 15 +++++++++++++++ internal/service/user.go | 14 ++++++++++++++ 9 files changed, 77 insertions(+), 19 deletions(-) diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 1d404861..f9810c69 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -55,8 +55,7 @@ func initWeb() (*app.Web, error) { if err != nil { return nil, err } - logger := bootstrap.NewLog(koanf) - db, err := bootstrap.NewDB(koanf, logger) + db, err := bootstrap.NewDB(koanf) if err != nil { return nil, err } @@ -65,11 +64,12 @@ func initWeb() (*app.Web, error) { return nil, err } cacheRepo := data.NewCacheRepo(db) + logger := bootstrap.NewLog(koanf) queue := bootstrap.NewQueue() taskRepo := data.NewTaskRepo(locale, db, logger, queue) appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo) userTokenRepo := data.NewUserTokenRepo(locale, db) - middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo, userTokenRepo) + middlewares := middleware.NewMiddlewares(koanf, manager, appRepo, userTokenRepo) userRepo := data.NewUserRepo(locale, db) userService := service.NewUserService(locale, koanf, manager, userRepo) userTokenService := service.NewUserTokenService(locale, userTokenRepo) diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go index ba975259..b065afb6 100644 --- a/cmd/cli/wire_gen.go +++ b/cmd/cli/wire_gen.go @@ -53,12 +53,12 @@ func initCli() (*app.Cli, error) { if err != nil { return nil, err } - logger := bootstrap.NewLog(koanf) - db, err := bootstrap.NewDB(koanf, logger) + db, err := bootstrap.NewDB(koanf) if err != nil { return nil, err } cacheRepo := data.NewCacheRepo(db) + logger := bootstrap.NewLog(koanf) queue := bootstrap.NewQueue() taskRepo := data.NewTaskRepo(locale, db, logger, queue) appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo) diff --git a/go.mod b/go.mod index 197c8813..137d4711 100644 --- a/go.mod +++ b/go.mod @@ -35,8 +35,8 @@ require ( github.com/libdns/tencentcloud v1.4.2 github.com/libdns/westcn v1.0.2 github.com/libtnb/chix v1.3.0 - github.com/libtnb/gormstore v1.1.0 - github.com/libtnb/sessions v1.2.0 + github.com/libtnb/gormstore v1.1.1 + github.com/libtnb/sessions v1.2.1 github.com/libtnb/utils v1.2.0 github.com/mholt/acmez/v3 v3.1.3 github.com/ncruces/go-sqlite3 v0.29.0 diff --git a/go.sum b/go.sum index d3b2a2c1..051f633e 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4= github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -218,10 +220,14 @@ github.com/libtnb/chix v1.3.0 h1:/U+CyuxI41ooeB6M/762PHOjQlfHRg6BQjHKLZarlrM= github.com/libtnb/chix v1.3.0/go.mod h1:o8nQLEp/UrUojBKYzw8K8sltU/h0XxI2VLZ/z7AQCQg= github.com/libtnb/gormstore v1.1.0 h1:VbX8u0hhyl2YFSGfkSlF/xSfFG/LE29DCLrY8MJ+uxM= github.com/libtnb/gormstore v1.1.0/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA= +github.com/libtnb/gormstore v1.1.1 h1:FG/3P4PuWM6/vB4weVJ31meiSaoeXns1NQlP66quKeg= +github.com/libtnb/gormstore v1.1.1/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA= github.com/libtnb/securecookie v1.2.0 h1:2uc0PBDm0foeSTrcZ9QTX1IEjf6kFEwfgEYSIXQSKrA= github.com/libtnb/securecookie v1.2.0/go.mod h1:ja+wNGnQzYqcqXQnJWu6icsaWi5JEBwNEMJ2ReTVDxA= github.com/libtnb/sessions v1.2.0 h1:g/RMcfGTC5P2BQE1IqrgppPEQ++x/QjOuiwbN9Frke8= github.com/libtnb/sessions v1.2.0/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0= +github.com/libtnb/sessions v1.2.1 h1:O9gkEIeZuqyaxopXrUJcGxlNxmNfRBI8BOK43yLJXDI= +github.com/libtnb/sessions v1.2.1/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0= github.com/libtnb/utils v1.2.0 h1:6bTZrWn2OkNrODpCY4dhuHwbhsVRV7HICIgmZ31we98= github.com/libtnb/utils v1.2.0/go.mod h1:9gSEuhkADlvYbM3qJRQUAMC5ypMrhrYpX9YMuYJ6ws8= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -374,6 +380,8 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -446,6 +454,8 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/internal/bootstrap/db.go b/internal/bootstrap/db.go index 30c0c3ec..354cd541 100644 --- a/internal/bootstrap/db.go +++ b/internal/bootstrap/db.go @@ -9,16 +9,29 @@ import ( _ "github.com/ncruces/go-sqlite3/embed" "github.com/ncruces/go-sqlite3/gormlite" sloggorm "github.com/orandin/slog-gorm" + "gopkg.in/natefinch/lumberjack.v2" "gorm.io/gorm" "github.com/tnborg/panel/internal/app" "github.com/tnborg/panel/internal/migration" ) -func NewDB(conf *koanf.Koanf, log *slog.Logger) (*gorm.DB, error) { - // You can use any other database, like MySQL or PostgreSQL. +func NewDB(conf *koanf.Koanf) (*gorm.DB, error) { + ljLogger := &lumberjack.Logger{ + Filename: filepath.Join(app.Root, "panel/storage/logs/db.log"), + MaxSize: 10, + MaxAge: 30, + Compress: true, + } + + handler := slog.New(slog.NewJSONHandler(ljLogger, nil)).Handler() + options := []sloggorm.Option{sloggorm.WithHandler(handler)} + if conf.Bool("database.debug") { + options = append(options, sloggorm.WithTraceAll()) + } + return gorm.Open(gormlite.Open("file:"+filepath.Join(app.Root, "panel/storage/app.db?_txlock=immediate")), &gorm.Config{ - Logger: sloggorm.New(sloggorm.WithHandler(log.Handler())), + Logger: sloggorm.New(options...), SkipDefaultTransaction: true, DisableForeignKeyConstraintWhenMigrating: true, }) diff --git a/internal/bootstrap/session.go b/internal/bootstrap/session.go index eaa8e4cc..8535e760 100644 --- a/internal/bootstrap/session.go +++ b/internal/bootstrap/session.go @@ -10,10 +10,6 @@ import ( func NewSession(conf *koanf.Koanf, db *gorm.DB) (*sessions.Manager, error) { // initialize session manager lifetime := conf.Int("session.lifetime") - // TODO: will remove this fallback in v3 - if lifetime == 0 { - lifetime = 120 - } manager, err := sessions.NewManager(&sessions.ManagerOptions{ Key: conf.MustString("app.key"), Lifetime: lifetime, diff --git a/internal/http/middleware/middleware.go b/internal/http/middleware/middleware.go index bafaac3a..5e87e19d 100644 --- a/internal/http/middleware/middleware.go +++ b/internal/http/middleware/middleware.go @@ -3,6 +3,7 @@ package middleware import ( "log/slog" "net/http" + "path/filepath" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -12,6 +13,8 @@ import ( "github.com/leonelquinteros/gotext" "github.com/libtnb/sessions" sessionmiddleware "github.com/libtnb/sessions/middleware" + "github.com/tnborg/panel/internal/app" + "gopkg.in/natefinch/lumberjack.v2" "github.com/tnborg/panel/internal/biz" ) @@ -22,16 +25,23 @@ type Middlewares struct { conf *koanf.Koanf log *slog.Logger session *sessions.Manager - app biz.AppRepo + appRepo biz.AppRepo userToken biz.UserTokenRepo } -func NewMiddlewares(conf *koanf.Koanf, log *slog.Logger, session *sessions.Manager, app biz.AppRepo, userToken biz.UserTokenRepo) *Middlewares { +func NewMiddlewares(conf *koanf.Koanf, session *sessions.Manager, appRepo biz.AppRepo, userToken biz.UserTokenRepo) *Middlewares { + ljLogger := &lumberjack.Logger{ + Filename: filepath.Join(app.Root, "panel/storage/logs/http.log"), + MaxSize: 10, + MaxAge: 30, + Compress: true, + } + return &Middlewares{ conf: conf, - log: log, + log: slog.New(slog.NewJSONHandler(ljLogger, &slog.HandlerOptions{Level: slog.LevelInfo})), session: session, - app: app, + appRepo: appRepo, userToken: userToken, } } @@ -50,6 +60,6 @@ func (r *Middlewares) Globals(t *gotext.Locale, mux *chi.Mux) []func(http.Handle Status(t), Entrance(t, r.conf, r.session), MustLogin(t, r.session, r.userToken), - MustInstall(t, r.app), + MustInstall(t, r.appRepo), } } diff --git a/internal/http/middleware/must_login.go b/internal/http/middleware/must_login.go index 582d1ee9..75650105 100644 --- a/internal/http/middleware/must_login.go +++ b/internal/http/middleware/must_login.go @@ -8,6 +8,7 @@ import ( "net/http" "slices" "strings" + "time" "github.com/leonelquinteros/gotext" "github.com/libtnb/sessions" @@ -72,6 +73,20 @@ func MustLogin(t *gotext.Locale, session *sessions.Manager, userToken biz.UserTo } userID = cast.ToUint(sess.Get("user_id")) + refreshAt := cast.ToInt64(sess.Get("refresh_at")) // 上次刷新的时间戳 + // 距离上次刷新时间超过 10 分钟刷新 Cookie 有效期 + if time.Now().Unix()-refreshAt > 600 { + sess.Put("refresh_at", time.Now().Unix()) + // 重新设置 Cookie + http.SetCookie(w, &http.Cookie{ + Name: sess.GetName(), + Value: sess.GetID(), + Expires: time.Now().Add(time.Duration(session.Lifetime) * time.Minute), + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) + } } if userID == 0 { diff --git a/internal/service/user.go b/internal/service/user.go index 8467ecfc..758cfeb3 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -11,6 +11,7 @@ import ( "net" "net/http" "strings" + "time" "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" @@ -98,6 +99,12 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { } } + // 重新生成会话 ID + if err = sess.Regenerate(true); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + // 安全登录下,将当前客户端与会话绑定 // 安全登录只在未启用面板 HTTPS 时生效 ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)) @@ -114,6 +121,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { } sess.Put("user_id", user.ID) + sess.Put("refresh_at", time.Now().Unix()) sess.Forget("key") Success(w, nil) } @@ -129,6 +137,12 @@ func (s *UserService) Logout(w http.ResponseWriter, r *http.Request) { sess.Forget("safe_login") sess.Forget("safe_client") + // 重新生成会话 ID + if err = sess.Regenerate(true); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + Success(w, nil) }