diff --git a/CLAUDE.md b/CLAUDE.md index 85c4306a..3baf05cf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -192,7 +192,6 @@ pnpm build - 路由:`github.com/go-chi/chi/v5` - ORM:`gorm.io/gorm` - 依赖注入:`github.com/google/wire` -- 配置管理:`github.com/knadh/koanf/v2` - 验证:`github.com/gookit/validate` ### 助手函数(service 层) diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 23538d41..a0d87557 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -43,42 +43,42 @@ import ( // initWeb init application. func initWeb() (*app.Web, error) { - koanf, err := bootstrap.NewConf() + config, err := bootstrap.NewConf() if err != nil { return nil, err } - locale, err := bootstrap.NewT(koanf) + locale, err := bootstrap.NewT(config) if err != nil { return nil, err } - db, err := bootstrap.NewDB(koanf) + db, err := bootstrap.NewDB(config) if err != nil { return nil, err } - manager, err := bootstrap.NewSession(koanf, db) + manager, err := bootstrap.NewSession(config, db) if err != nil { return nil, err } - logger := bootstrap.NewLog(koanf) + logger := bootstrap.NewLog(config) cacheRepo := data.NewCacheRepo(db) queue := bootstrap.NewQueue() taskRepo := data.NewTaskRepo(locale, db, logger, queue) - appRepo := data.NewAppRepo(locale, koanf, db, logger, cacheRepo, taskRepo) - userTokenRepo := data.NewUserTokenRepo(locale, koanf, db) - middlewares := middleware.NewMiddlewares(koanf, manager, appRepo, userTokenRepo) + appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo) + userTokenRepo := data.NewUserTokenRepo(locale, config, db) + middlewares := middleware.NewMiddlewares(config, manager, appRepo, userTokenRepo) userRepo := data.NewUserRepo(locale, db) - userService := service.NewUserService(locale, koanf, manager, userRepo) + userService := service.NewUserService(locale, config, manager, userRepo) userTokenService := service.NewUserTokenService(locale, userTokenRepo) databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger) databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo) databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo) certRepo := data.NewCertRepo(locale, db, logger) certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger) - settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo) + settingRepo := data.NewSettingRepo(locale, db, config, taskRepo) websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo) cronRepo := data.NewCronRepo(locale, db) backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo) - homeService := service.NewHomeService(locale, koanf, taskRepo, websiteRepo, appRepo, settingRepo, cronRepo, backupRepo) + homeService := service.NewHomeService(locale, config, taskRepo, websiteRepo, appRepo, settingRepo, cronRepo, backupRepo) taskService := service.NewTaskService(taskRepo) websiteService := service.NewWebsiteService(websiteRepo, settingRepo) databaseService := service.NewDatabaseService(databaseRepo) @@ -134,24 +134,24 @@ func initWeb() (*app.Web, error) { s3fsApp := s3fs.NewApp(locale) supervisorApp := supervisor.NewApp(locale) loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, openrestyApp, perconaApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp) - http := route.NewHttp(koanf, userService, userTokenService, homeService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, loader) - wsService := service.NewWsService(locale, koanf, logger, sshRepo) + http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, loader) + wsService := service.NewWsService(locale, config, logger, sshRepo) ws := route.NewWs(wsService) mux, err := bootstrap.NewRouter(locale, middlewares, http, ws) if err != nil { return nil, err } - server, err := bootstrap.NewHttp(koanf, mux) + server, err := bootstrap.NewHttp(config, mux) if err != nil { return nil, err } gormigrate := bootstrap.NewMigrate(db) jobs := job.NewJobs(db, logger, settingRepo, certRepo, backupRepo, cacheRepo, taskRepo) - cron, err := bootstrap.NewCron(koanf, logger, jobs) + cron, err := bootstrap.NewCron(config, logger, jobs) if err != nil { return nil, err } - validation := bootstrap.NewValidator(koanf, db) - web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation) + validation := bootstrap.NewValidator(config, db) + web := app.NewWeb(config, mux, server, gormigrate, cron, queue, validation) return web, nil } diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go index 3cbbd271..8521e23f 100644 --- a/cmd/cli/wire_gen.go +++ b/cmd/cli/wire_gen.go @@ -41,25 +41,25 @@ import ( // initCli init command line. func initCli() (*app.Cli, error) { - koanf, err := bootstrap.NewConf() + config, err := bootstrap.NewConf() if err != nil { return nil, err } - locale, err := bootstrap.NewT(koanf) + locale, err := bootstrap.NewT(config) if err != nil { return nil, err } - db, err := bootstrap.NewDB(koanf) + db, err := bootstrap.NewDB(config) if err != nil { return nil, err } - logger := bootstrap.NewLog(koanf) + logger := bootstrap.NewLog(config) cacheRepo := data.NewCacheRepo(db) queue := bootstrap.NewQueue() taskRepo := data.NewTaskRepo(locale, db, logger, queue) - appRepo := data.NewAppRepo(locale, koanf, db, logger, cacheRepo, taskRepo) + appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo) userRepo := data.NewUserRepo(locale, db) - settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo) + settingRepo := data.NewSettingRepo(locale, db, config, taskRepo) databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger) databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo) databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo) @@ -67,7 +67,7 @@ func initCli() (*app.Cli, error) { certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger) websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo) backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo) - cliService := service.NewCliService(locale, koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo) + cliService := service.NewCliService(locale, config, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo) cli := route.NewCli(locale, cliService) command := bootstrap.NewCli(locale, cli) gormigrate := bootstrap.NewMigrate(db) diff --git a/go.mod b/go.mod index 09e4cd96..04ef999d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/acepanel/panel go 1.25 require ( + github.com/DeRuina/timberjack v1.3.9 github.com/bddjr/hlfhr v1.4.0 github.com/beevik/ntp v1.5.0 github.com/coder/websocket v1.8.14 @@ -19,9 +20,6 @@ require ( github.com/gookit/validate v1.5.6 github.com/hashicorp/go-version v1.8.0 github.com/klauspost/compress v1.18.2 - github.com/knadh/koanf/parsers/yaml v1.1.0 - github.com/knadh/koanf/providers/file v1.2.1 - github.com/knadh/koanf/v2 v2.3.0 github.com/leonelquinteros/gotext v1.7.2 github.com/lib/pq v1.10.9 github.com/libdns/alidns v1.0.6-beta.3 @@ -53,10 +51,9 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701 github.com/urfave/cli/v3 v3.6.1 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v4 v4.0.0-rc.3 golang.org/x/crypto v0.46.0 golang.org/x/net v0.48.0 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/gorm v1.31.1 ) @@ -67,32 +64,25 @@ require ( github.com/boombuler/barcode v1.1.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gofiber/schema v1.6.0 // indirect github.com/gookit/filter v1.2.3 // indirect - github.com/gookit/goutil v0.7.1 // indirect + github.com/gookit/goutil v0.7.3 // indirect github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef // indirect github.com/jaevor/go-nanoid v1.4.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/knadh/koanf/maps v0.1.2 // indirect github.com/libtnb/securecookie v1.2.0 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect - github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/tetratelabs/wazero v1.11.0 // indirect github.com/timtadh/data-structures v0.6.2 // indirect github.com/timtadh/lexmachine v0.2.3 // indirect @@ -104,13 +94,12 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) replace ( github.com/mholt/acmez/v3 => github.com/libtnb/acmez/v3 v3.0.0-20260103184942-a835890fc93e github.com/moby/moby/client => github.com/libtnb/moby/client v0.0.0-20260103192150-39cfd5376055 + github.com/stretchr/testify => github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696 ) tool github.com/google/wire diff --git a/go.sum b/go.sum index 9acb1233..2b43e86e 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DeRuina/timberjack v1.3.9 h1:6UXZ1I7ExPGTX/1UNYawR58LlOJUHKBPiYC7WQ91eBo= +github.com/DeRuina/timberjack v1.3.9/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE= github.com/G-Core/gcore-dns-sdk-go v0.3.3 h1:McILJSbJ5nOcT0MI0aBYhEuufCF329YbqKwFIN0RjCI= github.com/G-Core/gcore-dns-sdk-go v0.3.3/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -52,8 +54,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -66,11 +66,11 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8= github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -92,8 +92,6 @@ github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2m github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY= github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -130,8 +128,8 @@ github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/gookit/filter v1.2.3 h1:Zo7cBOtsVzAoa/jtf+Ury6zlsbJXqInFdUpbbnB2vMM= github.com/gookit/filter v1.2.3/go.mod h1:nFLJcOV8dRgS1iiX23gUQgmHUhpuS40qCvAGgIvA1pM= -github.com/gookit/goutil v0.7.1 h1:AaFJPN9mrdeYBv8HOybri26EHGCC34WJVT7jUStGJsI= -github.com/gookit/goutil v0.7.1/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU= +github.com/gookit/goutil v0.7.3 h1:nXDd/AB17nEjqVCNDGioDhVL/gVqdlqRMfFergKDjHE= +github.com/gookit/goutil v0.7.3/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU= github.com/gookit/validate v1.5.6 h1:D6vbSZzreuKYpeeXm5FDDEJy3K5E4lcWsQE4saSMZbU= github.com/gookit/validate v1.5.6/go.mod h1:WYEHndRNepIIkM+6CtgEX9MQ9ToIQRhXxmz5oLHF/fc= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -181,19 +179,10 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= -github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= -github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/parsers/yaml v1.1.0 h1:3ltfm9ljprAHt4jxgeYLlFPmUaunuCgu1yILuTXRdM4= -github.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg= -github.com/knadh/koanf/providers/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP3AESOJYp9wM= -github.com/knadh/koanf/providers/file v1.2.1/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA= -github.com/knadh/koanf/v2 v2.3.0 h1:Qg076dDRFHvqnKG97ZEsi9TAg2/nFTa9hCdcSa1lvlM= -github.com/knadh/koanf/v2 v2.3.0/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -236,6 +225,8 @@ github.com/libtnb/securecookie v1.2.0 h1:2uc0PBDm0foeSTrcZ9QTX1IEjf6kFEwfgEYSIXQ github.com/libtnb/securecookie v1.2.0/go.mod h1:ja+wNGnQzYqcqXQnJWu6icsaWi5JEBwNEMJ2ReTVDxA= github.com/libtnb/sessions v1.2.2 h1:VTTzzeBDJEkJbaPaIU9C4bRj2oAqD0rgQ7UHFkkaNT4= github.com/libtnb/sessions v1.2.2/go.mod h1:qw+FWtBtrPDYCf6MfX0Lk5EhTArpvT72z5Ei4RUMTRg= +github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696 h1:GN0Y3DG27mMruX536k0jtCSLegjJb/gypzj5gZL6tRI= +github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696/go.mod h1:HeQeTfKU6tj2Lx1z79UacwYeDioo6M4ZD7BDDI6+rrg= github.com/libtnb/utils v1.2.1 h1:LJmReRREnpqfHyy9PZtNgBh3ZaIGct81b8ZaAsolMkM= github.com/libtnb/utils v1.2.1/go.mod h1:o6LEDeC42PXI21uLWdWJWTVYvR9BtAZfzzTGJVQoQiU= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -244,8 +235,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -253,8 +242,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.53.0-rc.1 h1:M5SUwRbTrNy+plCTiV6gn4ZiN/Csynk0imIsUmOgHGI= @@ -280,7 +267,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= @@ -325,16 +311,9 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= @@ -366,8 +345,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= +go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -493,22 +472,14 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= diff --git a/internal/app/web.go b/internal/app/web.go index e25b8466..ad92607a 100644 --- a/internal/app/web.go +++ b/internal/app/web.go @@ -11,14 +11,14 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-gormigrate/gormigrate/v2" "github.com/gookit/validate" - "github.com/knadh/koanf/v2" "github.com/robfig/cron/v3" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/queue" ) type Web struct { - conf *koanf.Koanf + conf *config.Config router *chi.Mux server *hlfhr.Server migrator *gormigrate.Gormigrate @@ -26,7 +26,7 @@ type Web struct { queue *queue.Queue } -func NewWeb(conf *koanf.Koanf, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Web { +func NewWeb(conf *config.Config, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Web { return &Web{ conf: conf, router: router, @@ -52,15 +52,15 @@ func (r *Web) Run() error { r.queue.Run(context.TODO()) // run http server - if r.conf.Bool("http.tls") { + if r.conf.HTTP.TLS { cert := filepath.Join(Root, "panel/storage/cert.pem") key := filepath.Join(Root, "panel/storage/cert.key") - fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"), "with tls") + fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port, "with tls") if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) { return err } } else { - fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port")) + fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port) if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { return err } diff --git a/internal/bootstrap/conf.go b/internal/bootstrap/conf.go index a76ebca1..063d99a8 100644 --- a/internal/bootstrap/conf.go +++ b/internal/bootstrap/conf.go @@ -4,22 +4,13 @@ import ( "log" "time" - "github.com/knadh/koanf/parsers/yaml" - "github.com/knadh/koanf/providers/file" - "github.com/knadh/koanf/v2" - "github.com/acepanel/panel/internal/app" - "github.com/acepanel/panel/pkg/io" + "github.com/acepanel/panel/pkg/config" ) -func NewConf() (*koanf.Koanf, error) { - config := "/opt/ace/panel/config.yml" - if !io.Exists(config) { - config = "config.yml" - } - - conf := koanf.New(".") - if err := conf.Load(file.Provider(config), yaml.Parser()); err != nil { +func NewConf() (*config.Config, error) { + conf, err := config.Load() + if err != nil { return nil, err } @@ -27,17 +18,17 @@ func NewConf() (*koanf.Koanf, error) { return conf, nil } -func initGlobal(conf *koanf.Koanf) { - app.Key = conf.MustString("app.key") +func initGlobal(conf *config.Config) { + app.Key = conf.App.Key if len(app.Key) != 32 { log.Fatalf("panel app key must be 32 characters") } app.Root = "/opt/ace" - app.Locale = conf.MustString("app.locale") + app.Locale = conf.App.Locale // 初始化时区 - loc, err := time.LoadLocation(conf.MustString("app.timezone")) + loc, err := time.LoadLocation(conf.App.Timezone) if err != nil { log.Fatalf("failed to load timezone: %v", err) } diff --git a/internal/bootstrap/cron.go b/internal/bootstrap/cron.go index 66772d20..97b6d248 100644 --- a/internal/bootstrap/cron.go +++ b/internal/bootstrap/cron.go @@ -3,15 +3,15 @@ package bootstrap import ( "log/slog" - "github.com/knadh/koanf/v2" "github.com/robfig/cron/v3" "github.com/acepanel/panel/internal/job" + "github.com/acepanel/panel/pkg/config" pkgcron "github.com/acepanel/panel/pkg/cron" ) -func NewCron(conf *koanf.Koanf, log *slog.Logger, jobs *job.Jobs) (*cron.Cron, error) { - logger := pkgcron.NewLogger(log, conf.Bool("app.debug")) +func NewCron(conf *config.Config, log *slog.Logger, jobs *job.Jobs) (*cron.Cron, error) { + logger := pkgcron.NewLogger(log, conf.App.Debug) c := cron.New( cron.WithParser(cron.NewParser( diff --git a/internal/bootstrap/db.go b/internal/bootstrap/db.go index fe055ac1..de38af03 100644 --- a/internal/bootstrap/db.go +++ b/internal/bootstrap/db.go @@ -4,29 +4,29 @@ import ( "log/slog" "path/filepath" + "github.com/DeRuina/timberjack" "github.com/go-gormigrate/gormigrate/v2" - "github.com/knadh/koanf/v2" _ "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/acepanel/panel/internal/app" "github.com/acepanel/panel/internal/migration" + "github.com/acepanel/panel/pkg/config" ) -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, +func NewDB(conf *config.Config) (*gorm.DB, error) { + tjLogger := &timberjack.Logger{ + Filename: filepath.Join(app.Root, "panel/storage/logs/db.log"), + MaxSize: 10, + MaxAge: 30, + Compression: "zstd", } - handler := slog.New(slog.NewJSONHandler(ljLogger, nil)).Handler() + handler := slog.New(slog.NewJSONHandler(tjLogger, nil)).Handler() options := []sloggorm.Option{sloggorm.WithHandler(handler)} - if conf.Bool("database.debug") { + if conf.Database.Debug { options = append(options, sloggorm.WithTraceAll()) } diff --git a/internal/bootstrap/http.go b/internal/bootstrap/http.go index 7704b9bb..eccc99b1 100644 --- a/internal/bootstrap/http.go +++ b/internal/bootstrap/http.go @@ -7,11 +7,11 @@ import ( "github.com/bddjr/hlfhr" "github.com/go-chi/chi/v5" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/acepanel/panel/internal/http/middleware" "github.com/acepanel/panel/internal/route" + "github.com/acepanel/panel/pkg/config" ) func NewRouter(t *gotext.Locale, middlewares *middleware.Middlewares, http *route.Http, ws *route.Ws) (*chi.Mux, error) { @@ -27,15 +27,15 @@ func NewRouter(t *gotext.Locale, middlewares *middleware.Middlewares, http *rout return r, nil } -func NewHttp(conf *koanf.Koanf, mux *chi.Mux) (*hlfhr.Server, error) { +func NewHttp(conf *config.Config, mux *chi.Mux) (*hlfhr.Server, error) { srv := hlfhr.New(&http.Server{ - Addr: fmt.Sprintf(":%d", conf.MustInt("http.port")), + Addr: fmt.Sprintf(":%d", conf.HTTP.Port), Handler: mux, MaxHeaderBytes: 2048 << 20, }) srv.Listen80RedirectTo443 = true - if conf.Bool("http.tls") { + if conf.HTTP.TLS { srv.TLSConfig = &tls.Config{ MinVersion: tls.VersionTLS12, } diff --git a/internal/bootstrap/logger.go b/internal/bootstrap/logger.go index 9177c615..191f57e0 100644 --- a/internal/bootstrap/logger.go +++ b/internal/bootstrap/logger.go @@ -4,26 +4,26 @@ import ( "log/slog" "path/filepath" - "github.com/knadh/koanf/v2" - "gopkg.in/natefinch/lumberjack.v2" + "github.com/DeRuina/timberjack" "github.com/acepanel/panel/internal/app" + "github.com/acepanel/panel/pkg/config" ) -func NewLog(conf *koanf.Koanf) *slog.Logger { - ljLogger := &lumberjack.Logger{ - Filename: filepath.Join(app.Root, "panel/storage/logs/app.log"), - MaxSize: 10, - MaxAge: 30, - Compress: true, +func NewLog(conf *config.Config) *slog.Logger { + tjLogger := &timberjack.Logger{ + Filename: filepath.Join(app.Root, "panel/storage/logs/app.log"), + MaxSize: 10, + MaxAge: 30, + Compression: "zstd", } level := slog.LevelInfo - if conf.Bool("app.debug") { + if conf.App.Debug { level = slog.LevelDebug } - log := slog.New(slog.NewJSONHandler(ljLogger, &slog.HandlerOptions{ + log := slog.New(slog.NewJSONHandler(tjLogger, &slog.HandlerOptions{ Level: level, })) slog.SetDefault(log) diff --git a/internal/bootstrap/session.go b/internal/bootstrap/session.go index 8535e760..438f5b0f 100644 --- a/internal/bootstrap/session.go +++ b/internal/bootstrap/session.go @@ -1,18 +1,18 @@ package bootstrap import ( - "github.com/knadh/koanf/v2" "github.com/libtnb/gormstore" "github.com/libtnb/sessions" "gorm.io/gorm" + + "github.com/acepanel/panel/pkg/config" ) -func NewSession(conf *koanf.Koanf, db *gorm.DB) (*sessions.Manager, error) { +func NewSession(conf *config.Config, db *gorm.DB) (*sessions.Manager, error) { // initialize session manager - lifetime := conf.Int("session.lifetime") manager, err := sessions.NewManager(&sessions.ManagerOptions{ - Key: conf.MustString("app.key"), - Lifetime: lifetime, + Key: conf.App.Key, + Lifetime: int(conf.Session.Lifetime), GcInterval: 5, DisableDefaultDriver: true, }) diff --git a/internal/bootstrap/t.go b/internal/bootstrap/t.go index 04822a58..533d1483 100644 --- a/internal/bootstrap/t.go +++ b/internal/bootstrap/t.go @@ -1,15 +1,14 @@ package bootstrap import ( - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/embed" ) -func NewT(conf *koanf.Koanf) (*gotext.Locale, error) { - locale := conf.String("app.locale") - l := gotext.NewLocaleFSWithPath(locale, embed.LocalesFS, "locales") +func NewT(conf *config.Config) (*gotext.Locale, error) { + l := gotext.NewLocaleFSWithPath(conf.App.Locale, embed.LocalesFS, "locales") l.AddDomain("backend") return l, nil diff --git a/internal/bootstrap/validator.go b/internal/bootstrap/validator.go index 00cea2b8..a87359dd 100644 --- a/internal/bootstrap/validator.go +++ b/internal/bootstrap/validator.go @@ -5,21 +5,23 @@ import ( "github.com/gookit/validate/locales/ruru" "github.com/gookit/validate/locales/zhcn" "github.com/gookit/validate/locales/zhtw" - "github.com/knadh/koanf/v2" "gorm.io/gorm" "github.com/acepanel/panel/internal/http/rule" + "github.com/acepanel/panel/pkg/config" ) // NewValidator just for register global rules -func NewValidator(conf *koanf.Koanf, db *gorm.DB) *validate.Validation { - if conf.String("app.locale") == "zh_CN" { +func NewValidator(conf *config.Config, db *gorm.DB) *validate.Validation { + switch conf.App.Locale { + case "zh_CN": zhcn.RegisterGlobal() - } else if conf.String("app.locale") == "zh_TW" { + case "zh_TW": zhtw.RegisterGlobal() - } else if conf.String("app.locale") == "ru_RU" { + case "ru_RU": ruru.RegisterGlobal() } + validate.Config(func(opt *validate.GlobalOption) { opt.StopOnError = false opt.SkipOnEmpty = true diff --git a/internal/data/app.go b/internal/data/app.go index e64048b6..4e182243 100644 --- a/internal/data/app.go +++ b/internal/data/app.go @@ -9,7 +9,6 @@ import ( "github.com/expr-lang/expr" "github.com/hashicorp/go-version" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/spf13/cast" "gorm.io/gorm" @@ -17,12 +16,13 @@ import ( "github.com/acepanel/panel/internal/app" "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/pkg/api" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/shell" ) type appRepo struct { t *gotext.Locale - conf *koanf.Koanf + conf *config.Config db *gorm.DB log *slog.Logger cache biz.CacheRepo @@ -30,7 +30,7 @@ type appRepo struct { api *api.API } -func NewAppRepo(t *gotext.Locale, conf *koanf.Koanf, db *gorm.DB, log *slog.Logger, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo { +func NewAppRepo(t *gotext.Locale, conf *config.Config, db *gorm.DB, log *slog.Logger, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo { return &appRepo{ t: t, conf: conf, @@ -172,7 +172,7 @@ func (r *appRepo) Install(channel, slug string) error { continue } if ch.Slug == channel { - if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") { + if vs.GreaterThan(panel) && !r.conf.App.Debug { return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version)) } shellUrl = ch.Install @@ -232,7 +232,7 @@ func (r *appRepo) UnInstall(slug string) error { continue } if ch.Slug == installed.Channel { - if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") { + if vs.GreaterThan(panel) && !r.conf.App.Debug { return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version)) } shellUrl = ch.Uninstall @@ -287,7 +287,7 @@ func (r *appRepo) Update(slug string) error { continue } if ch.Slug == installed.Channel { - if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") { + if vs.GreaterThan(panel) && !r.conf.App.Debug { return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version)) } shellUrl = ch.Update diff --git a/internal/data/setting.go b/internal/data/setting.go index 52f79378..6cb14132 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -6,32 +6,30 @@ import ( "path/filepath" "sync" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/spf13/cast" - "go.yaml.in/yaml/v3" "gorm.io/gorm" "github.com/acepanel/panel/internal/app" "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/cert" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/firewall" "github.com/acepanel/panel/pkg/io" "github.com/acepanel/panel/pkg/os" "github.com/acepanel/panel/pkg/systemctl" - "github.com/acepanel/panel/pkg/types" ) type settingRepo struct { t *gotext.Locale cache sync.Map db *gorm.DB - conf *koanf.Koanf + conf *config.Config task biz.TaskRepo } -func NewSettingRepo(t *gotext.Locale, db *gorm.DB, conf *koanf.Koanf, task biz.TaskRepo) biz.SettingRepo { +func NewSettingRepo(t *gotext.Locale, db *gorm.DB, conf *config.Config, task biz.TaskRepo) biz.SettingRepo { return &settingRepo{ t: t, db: db, @@ -236,19 +234,19 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { return &request.SettingPanel{ Name: name, Channel: channel, - Locale: r.conf.String("app.locale"), - Entrance: r.conf.String("http.entrance"), + Locale: r.conf.App.Locale, + Entrance: r.conf.HTTP.Entrance, OfflineMode: offlineMode, AutoUpdate: autoUpdate, - Lifetime: uint(r.conf.Int("session.lifetime")), - IPHeader: r.conf.String("http.ip_header"), - BindDomain: r.conf.Strings("http.bind_domain"), - BindIP: r.conf.Strings("http.bind_ip"), - BindUA: r.conf.Strings("http.bind_ua"), + Lifetime: r.conf.Session.Lifetime, + IPHeader: r.conf.HTTP.IPHeader, + BindDomain: r.conf.HTTP.BindDomain, + BindIP: r.conf.HTTP.BindIP, + BindUA: r.conf.HTTP.BindUA, WebsitePath: websitePath, BackupPath: backupPath, - Port: uint(r.conf.Int("http.port")), - HTTPS: r.conf.Bool("http.tls"), + Port: r.conf.HTTP.Port, + HTTPS: r.conf.HTTP.TLS, Cert: crt, Key: key, }, nil @@ -299,16 +297,12 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { } // 面板主配置 - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") + conf, err := config.Load() if err != nil { return false, err } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return false, err - } - if req.Port != config.HTTP.Port { + if req.Port != conf.HTTP.Port { if os.TCPPortInUse(req.Port) { return false, errors.New(r.t.Get("port is already in use")) } @@ -328,27 +322,24 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { } } - config.App.Locale = req.Locale - config.HTTP.Port = req.Port - config.HTTP.Entrance = req.Entrance - config.HTTP.TLS = req.HTTPS - config.HTTP.IPHeader = req.IPHeader - config.HTTP.BindDomain = req.BindDomain - config.HTTP.BindIP = req.BindIP - config.HTTP.BindUA = req.BindUA - config.Session.Lifetime = req.Lifetime + conf.App.Locale = req.Locale + conf.HTTP.Port = req.Port + conf.HTTP.Entrance = req.Entrance + conf.HTTP.TLS = req.HTTPS + conf.HTTP.IPHeader = req.IPHeader + conf.HTTP.BindDomain = req.BindDomain + conf.HTTP.BindIP = req.BindIP + conf.HTTP.BindUA = req.BindUA + conf.Session.Lifetime = req.Lifetime - encoded, err := yaml.Marshal(config) - if err != nil { - return false, err - } - if raw != string(encoded) { + // 检查配置是否有变更 + if same, _ := config.Check(conf); !same { if r.task.HasRunningTask() { return false, errors.New(r.t.Get("background task is running, modifying some settings is prohibited, please try again later")) } restartFlag = true } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0644); err != nil { + if err = config.Save(conf); err != nil { return false, err } diff --git a/internal/data/user_token.go b/internal/data/user_token.go index 04ff1105..4e845ac4 100644 --- a/internal/data/user_token.go +++ b/internal/data/user_token.go @@ -14,22 +14,22 @@ import ( "strings" "time" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/utils/str" "github.com/spf13/cast" "gorm.io/gorm" "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/pkg/config" ) type userTokenRepo struct { t *gotext.Locale - conf *koanf.Koanf + conf *config.Config db *gorm.DB } -func NewUserTokenRepo(t *gotext.Locale, conf *koanf.Koanf, db *gorm.DB) biz.UserTokenRepo { +func NewUserTokenRepo(t *gotext.Locale, conf *config.Config, db *gorm.DB) biz.UserTokenRepo { return &userTokenRepo{ t: t, conf: conf, @@ -144,7 +144,7 @@ func (r userTokenRepo) ValidateReq(req *http.Request) (uint, error) { // 步骤六:验证IP if len(userToken.IPs) > 0 { ip := req.RemoteAddr - ipHeader := r.conf.String("http.ip_header") + ipHeader := r.conf.HTTP.IPHeader if ipHeader != "" && req.Header.Get(ipHeader) != "" { ip = strings.Split(req.Header.Get(ipHeader), ",")[0] } diff --git a/internal/http/middleware/entrance.go b/internal/http/middleware/entrance.go index a6ad1c62..31be250f 100644 --- a/internal/http/middleware/entrance.go +++ b/internal/http/middleware/entrance.go @@ -7,16 +7,16 @@ import ( "strings" "github.com/go-chi/chi/v5" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/chix" "github.com/libtnb/sessions" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/punycode" ) // Entrance 确保通过正确的入口访问 -func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) func(next http.Handler) http.Handler { +func Entrance(t *gotext.Locale, conf *config.Config, session *sessions.Manager) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sess, err := session.GetSession(r) @@ -25,7 +25,7 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu return } - entrance := strings.TrimSuffix(conf.String("http.entrance"), "/") + entrance := strings.TrimSuffix(conf.HTTP.Entrance, "/") if !strings.HasPrefix(entrance, "/") { entrance = "/" + entrance } @@ -40,14 +40,14 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu host = decoded } } - if len(conf.Strings("http.bind_domain")) > 0 && !slices.Contains(conf.Strings("http.bind_domain"), host) { + if len(conf.HTTP.BindDomain) > 0 && !slices.Contains(conf.HTTP.BindDomain, host) { Abort(w, http.StatusTeapot, t.Get("invalid request domain: %s", r.Host)) return } // 取请求 IP ip := r.RemoteAddr - ipHeader := conf.String("http.ip_header") + ipHeader := conf.HTTP.IPHeader if ipHeader != "" && r.Header.Get(ipHeader) != "" { ip = strings.Split(r.Header.Get(ipHeader), ",")[0] } @@ -56,11 +56,11 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu ip = r.RemoteAddr } - if len(conf.Strings("http.bind_ip")) > 0 { + if len(conf.HTTP.BindIP) > 0 { allowed := false requestIP := net.ParseIP(ip) if requestIP != nil { - for _, allowedIP := range conf.Strings("http.bind_ip") { + for _, allowedIP := range conf.HTTP.BindIP { if strings.Contains(allowedIP, "/") { // CIDR if _, ipNet, err := net.ParseCIDR(allowedIP); err == nil && ipNet.Contains(requestIP) { @@ -81,7 +81,7 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu return } } - if len(conf.Strings("http.bind_ua")) > 0 && !slices.Contains(conf.Strings("http.bind_ua"), r.UserAgent()) { + if len(conf.HTTP.BindUA) > 0 && !slices.Contains(conf.HTTP.BindUA, r.UserAgent()) { Abort(w, http.StatusTeapot, t.Get("invalid request user agent: %s", r.UserAgent())) return } @@ -113,7 +113,7 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu } // 情况四:非调试模式且未通过验证的请求,返回错误 - if !conf.Bool("app.debug") && + if !conf.App.Debug && sess.Missing("verify_entrance") && r.URL.Path != "/robots.txt" { Abort(w, http.StatusTeapot, t.Get("invalid access entrance")) diff --git a/internal/http/middleware/middleware.go b/internal/http/middleware/middleware.go index 859e1192..fccc7d1a 100644 --- a/internal/http/middleware/middleware.go +++ b/internal/http/middleware/middleware.go @@ -6,43 +6,43 @@ import ( "net/http" "path/filepath" + "github.com/DeRuina/timberjack" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/httplog/v3" "github.com/google/wire" "github.com/klauspost/compress/gzip" "github.com/klauspost/compress/zstd" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/sessions" sessionmiddleware "github.com/libtnb/sessions/middleware" - "gopkg.in/natefinch/lumberjack.v2" "github.com/acepanel/panel/internal/app" "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/pkg/config" ) var ProviderSet = wire.NewSet(NewMiddlewares) type Middlewares struct { - conf *koanf.Koanf + conf *config.Config log *slog.Logger session *sessions.Manager appRepo biz.AppRepo userToken biz.UserTokenRepo } -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, +func NewMiddlewares(conf *config.Config, session *sessions.Manager, appRepo biz.AppRepo, userToken biz.UserTokenRepo) *Middlewares { + tjLogger := &timberjack.Logger{ + Filename: filepath.Join(app.Root, "panel/storage/logs/http.log"), + MaxSize: 10, + MaxAge: 30, + Compression: "zstd", } return &Middlewares{ conf: conf, - log: slog.New(slog.NewJSONHandler(ljLogger, &slog.HandlerOptions{Level: slog.LevelInfo})), + log: slog.New(slog.NewJSONHandler(tjLogger, &slog.HandlerOptions{Level: slog.LevelInfo})), session: session, appRepo: appRepo, userToken: userToken, diff --git a/internal/http/middleware/must_login.go b/internal/http/middleware/must_login.go index d7195f7e..39820a83 100644 --- a/internal/http/middleware/must_login.go +++ b/internal/http/middleware/must_login.go @@ -10,16 +10,16 @@ import ( "strings" "time" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/sessions" "github.com/spf13/cast" "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/pkg/config" ) // MustLogin 确保已登录 -func MustLogin(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager, userToken biz.UserTokenRepo) func(next http.Handler) http.Handler { +func MustLogin(t *gotext.Locale, conf *config.Config, session *sessions.Manager, userToken biz.UserTokenRepo) func(next http.Handler) http.Handler { // 白名单 whiteList := []string{ "/api/user/key", @@ -65,7 +65,7 @@ func MustLogin(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager, u if safeLogin { // 取请求 IP ip := r.RemoteAddr - ipHeader := conf.String("http.ip_header") + ipHeader := conf.HTTP.IPHeader if ipHeader != "" && r.Header.Get(ipHeader) != "" { ip = strings.Split(r.Header.Get(ipHeader), ",")[0] } @@ -94,7 +94,7 @@ func MustLogin(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager, u Expires: time.Now().Add(time.Duration(session.Lifetime) * time.Minute), Path: "/", HttpOnly: true, - Secure: conf.Bool("http.tls"), + Secure: conf.HTTP.TLS, SameSite: http.SameSiteLaxMode, }) } diff --git a/internal/route/http.go b/internal/route/http.go index 316e7b6a..fb180326 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -7,16 +7,16 @@ import ( "time" "github.com/go-chi/chi/v5" - "github.com/knadh/koanf/v2" "github.com/acepanel/panel/internal/http/middleware" "github.com/acepanel/panel/internal/service" "github.com/acepanel/panel/pkg/apploader" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/embed" ) type Http struct { - conf *koanf.Koanf + conf *config.Config user *service.UserService userToken *service.UserTokenService home *service.HomeService @@ -50,7 +50,7 @@ type Http struct { } func NewHttp( - conf *koanf.Koanf, + conf *config.Config, user *service.UserService, userToken *service.UserTokenService, home *service.HomeService, @@ -121,7 +121,7 @@ func (route *Http) Register(r *chi.Mux) { r.Route("/api", func(r chi.Router) { r.Route("/user", func(r chi.Router) { r.Get("/key", route.user.GetKey) - r.With(middleware.Throttle(route.conf.String("http.ip_header"), 5, time.Minute)).Post("/login", route.user.Login) + r.With(middleware.Throttle(route.conf.HTTP.IPHeader, 5, time.Minute)).Post("/login", route.user.Login) r.Post("/logout", route.user.Logout) r.Get("/is_login", route.user.IsLogin) r.Get("/is_2fa", route.user.IsTwoFA) diff --git a/internal/service/cli.go b/internal/service/cli.go index f3d764c6..7a1dae90 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -11,14 +11,12 @@ import ( "strings" "time" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/utils/collect" "github.com/libtnb/utils/hash" "github.com/libtnb/utils/str" "github.com/spf13/cast" "github.com/urfave/cli/v3" - "go.yaml.in/yaml/v3" "gorm.io/gorm" "github.com/acepanel/panel/internal/app" @@ -26,20 +24,20 @@ import ( "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/api" "github.com/acepanel/panel/pkg/cert" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/firewall" "github.com/acepanel/panel/pkg/io" "github.com/acepanel/panel/pkg/ntp" "github.com/acepanel/panel/pkg/os" "github.com/acepanel/panel/pkg/systemctl" "github.com/acepanel/panel/pkg/tools" - "github.com/acepanel/panel/pkg/types" ) type CliService struct { hr string t *gotext.Locale api *api.API - conf *koanf.Koanf + conf *config.Config db *gorm.DB appRepo biz.AppRepo cacheRepo biz.CacheRepo @@ -51,7 +49,7 @@ type CliService struct { hash hash.Hasher } -func NewCliService(t *gotext.Locale, conf *koanf.Koanf, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService { +func NewCliService(t *gotext.Locale, conf *config.Config, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService { return &CliService{ hr: `+----------------------------------------------------`, api: api.NewAPI(app.Version, app.Locale), @@ -147,15 +145,15 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error { } protocol := "http" - if s.conf.Bool("http.tls") { + if s.conf.HTTP.TLS { protocol = "https" } - port := s.conf.String("http.port") - if port == "" { + port := s.conf.HTTP.Port + if port == 0 { return errors.New(s.t.Get("Failed to get port")) } - entrance := s.conf.String("http.entrance") + entrance := s.conf.HTTP.Entrance if entrance == "" { return errors.New(s.t.Get("Failed to get entrance")) } @@ -307,23 +305,14 @@ func (s *CliService) UserTwoFA(ctx context.Context, cmd *cli.Command) error { } func (s *CliService) HTTPSOn(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.TLS = true - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.TLS = true + + if err = config.Save(conf); err != nil { return err } @@ -332,23 +321,14 @@ func (s *CliService) HTTPSOn(ctx context.Context, cmd *cli.Command) error { } func (s *CliService) HTTPSOff(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.TLS = false - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.TLS = false + + if err = config.Save(conf); err != nil { return err } @@ -388,49 +368,31 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error } func (s *CliService) EntranceOn(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.Entrance = "/" + str.Random(6) - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.Entrance = "/" + str.Random(6) + + if err = config.Save(conf); err != nil { return err } fmt.Println(s.t.Get("Entrance enabled")) - fmt.Println(s.t.Get("Entrance: %s", config.HTTP.Entrance)) + fmt.Println(s.t.Get("Entrance: %s", conf.HTTP.Entrance)) return s.Restart(ctx, cmd) } func (s *CliService) EntranceOff(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.Entrance = "/" - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.Entrance = "/" + + if err = config.Save(conf); err != nil { return err } @@ -439,23 +401,14 @@ func (s *CliService) EntranceOff(ctx context.Context, cmd *cli.Command) error { } func (s *CliService) BindDomainOff(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.BindDomain = nil - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.BindDomain = nil + + if err = config.Save(conf); err != nil { return err } @@ -464,23 +417,14 @@ func (s *CliService) BindDomainOff(ctx context.Context, cmd *cli.Command) error } func (s *CliService) BindIPOff(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.BindIP = nil - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.BindIP = nil + + if err = config.Save(conf); err != nil { return err } @@ -489,23 +433,14 @@ func (s *CliService) BindIPOff(ctx context.Context, cmd *cli.Command) error { } func (s *CliService) BindUAOff(ctx context.Context, cmd *cli.Command) error { - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") - if err != nil { - return err - } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - - config.HTTP.BindUA = nil - - encoded, err := yaml.Marshal(config) + conf, err := config.Load() if err != nil { return err } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + conf.HTTP.BindUA = nil + + if err = config.Save(conf); err != nil { return err } @@ -519,27 +454,18 @@ func (s *CliService) Port(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Port range error")) } - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") + conf, err := config.Load() if err != nil { return err } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - if port != config.HTTP.Port { + if port != conf.HTTP.Port { if os.TCPPortInUse(port) { return errors.New(s.t.Get("Port already in use")) } } - config.HTTP.Port = port - - encoded, err := yaml.Marshal(config) - if err != nil { - return err - } + conf.HTTP.Port = port // 放行端口 if ok, _ := systemctl.IsEnabled("firewalld"); ok { @@ -556,7 +482,7 @@ func (s *CliService) Port(ctx context.Context, cmd *cli.Command) error { } } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + if err = config.Save(conf); err != nil { return err } @@ -968,17 +894,13 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Initialization failed: %v", err)) } - config := new(types.PanelConfig) - raw, err := io.Read("/usr/local/etc/panel/config.yml") + conf, err := config.Load() if err != nil { return err } - if err = yaml.Unmarshal([]byte(raw), config); err != nil { - return err - } - config.App.Key = str.Random(32) - config.HTTP.Entrance = "/" + str.Random(6) + conf.App.Key = str.Random(32) + conf.HTTP.Entrance = "/" + str.Random(6) // 随机默认端口 checkPort: @@ -986,7 +908,7 @@ checkPort: if os.TCPPortInUse(port) { goto checkPort } - config.HTTP.Port = port + conf.HTTP.Port = port // 放行端口 fw := firewall.NewFirewall() @@ -998,11 +920,7 @@ checkPort: Strategy: firewall.StrategyAccept, }, firewall.OperationAdd) - encoded, err := yaml.Marshal(config) - if err != nil { - return err - } - if err = io.Write("/usr/local/etc/panel/config.yml", string(encoded), 0700); err != nil { + if err = config.Save(conf); err != nil { return err } diff --git a/internal/service/home.go b/internal/service/home.go index 23d053a7..059e84af 100644 --- a/internal/service/home.go +++ b/internal/service/home.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/hashicorp/go-version" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/chix" "github.com/libtnb/utils/collect" @@ -20,6 +19,7 @@ import ( "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/api" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/db" "github.com/acepanel/panel/pkg/shell" "github.com/acepanel/panel/pkg/tools" @@ -29,7 +29,7 @@ import ( type HomeService struct { t *gotext.Locale api *api.API - conf *koanf.Koanf + conf *config.Config taskRepo biz.TaskRepo websiteRepo biz.WebsiteRepo appRepo biz.AppRepo @@ -38,7 +38,7 @@ type HomeService struct { backupRepo biz.BackupRepo } -func NewHomeService(t *gotext.Locale, conf *koanf.Koanf, task biz.TaskRepo, website biz.WebsiteRepo, appRepo biz.AppRepo, setting biz.SettingRepo, cron biz.CronRepo, backupRepo biz.BackupRepo) *HomeService { +func NewHomeService(t *gotext.Locale, conf *config.Config, task biz.TaskRepo, website biz.WebsiteRepo, appRepo biz.AppRepo, setting biz.SettingRepo, cron biz.CronRepo, backupRepo biz.BackupRepo) *HomeService { return &HomeService{ t: t, api: api.NewAPI(app.Version, app.Locale), @@ -60,7 +60,7 @@ func (s *HomeService) Panel(w http.ResponseWriter, r *http.Request) { Success(w, chix.M{ "name": name, - "locale": s.conf.String("app.locale"), + "locale": s.conf.App.Locale, }) } diff --git a/internal/service/user.go b/internal/service/user.go index b0a80d66..57c2ae32 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -13,7 +13,6 @@ import ( "strings" "time" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" "github.com/libtnb/chix" "github.com/libtnb/sessions" @@ -22,17 +21,18 @@ import ( "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/rsacrypto" ) type UserService struct { t *gotext.Locale - conf *koanf.Koanf + conf *config.Config session *sessions.Manager userRepo biz.UserRepo } -func NewUserService(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager, user biz.UserRepo) *UserService { +func NewUserService(t *gotext.Locale, conf *config.Config, session *sessions.Manager, user biz.UserRepo) *UserService { gob.Register(rsa.PrivateKey{}) // 必须注册 rsa.PrivateKey 类型否则无法反序列化 session 中的 key return &UserService{ t: t, @@ -108,7 +108,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { // 安全登录下,将当前客户端与会话绑定 // 安全登录只在未启用面板 HTTPS 时生效 ip := r.RemoteAddr - ipHeader := s.conf.String("http.ip_header") + ipHeader := s.conf.HTTP.IPHeader if ipHeader != "" && r.Header.Get(ipHeader) != "" { ip = strings.Split(r.Header.Get(ipHeader), ",")[0] } @@ -117,7 +117,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { ip = r.RemoteAddr } - if req.SafeLogin && !s.conf.Bool("http.tls") { + if req.SafeLogin && !s.conf.HTTP.TLS { sess.Put("safe_login", true) sess.Put("safe_client", fmt.Sprintf("%x", sha256.Sum256([]byte(ip)))) } else { diff --git a/internal/service/ws.go b/internal/service/ws.go index fce18fa4..b4309877 100644 --- a/internal/service/ws.go +++ b/internal/service/ws.go @@ -7,24 +7,24 @@ import ( "net/http" "github.com/coder/websocket" - "github.com/knadh/koanf/v2" "github.com/leonelquinteros/gotext" stdssh "golang.org/x/crypto/ssh" "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/shell" "github.com/acepanel/panel/pkg/ssh" ) type WsService struct { t *gotext.Locale - conf *koanf.Koanf + conf *config.Config log *slog.Logger sshRepo biz.SSHRepo } -func NewWsService(t *gotext.Locale, conf *koanf.Koanf, log *slog.Logger, ssh biz.SSHRepo) *WsService { +func NewWsService(t *gotext.Locale, conf *config.Config, log *slog.Logger, ssh biz.SSHRepo) *WsService { return &WsService{ t: t, conf: conf, @@ -120,7 +120,7 @@ func (s *WsService) upgrade(w http.ResponseWriter, r *http.Request) (*websocket. } // debug 模式下不校验 origin,方便 vite 代理调试 - if s.conf.Bool("app.debug") { + if s.conf.App.Debug { opts.InsecureSkipVerify = true } diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 00000000..c284ac88 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,95 @@ +package config + +import ( + "os" + + "go.yaml.in/yaml/v4" + + "github.com/acepanel/panel/pkg/io" +) + +const configPath = "/opt/ace/panel/storage/config.yml" + +// Config 面板配置结构体 +type Config struct { + App AppConfig `yaml:"app"` + HTTP HTTPConfig `yaml:"http"` + Database DatabaseConfig `yaml:"database"` + Session SessionConfig `yaml:"session"` +} + +type AppConfig struct { + Debug bool `yaml:"debug"` + Key string `yaml:"key"` + Locale string `yaml:"locale"` + Timezone string `yaml:"timezone"` + Root string `yaml:"root"` +} + +type HTTPConfig struct { + Debug bool `yaml:"debug"` + Port uint `yaml:"port"` + Entrance string `yaml:"entrance"` + TLS bool `yaml:"tls"` + IPHeader string `yaml:"ip_header"` + BindDomain []string `yaml:"bind_domain"` + BindIP []string `yaml:"bind_ip"` + BindUA []string `yaml:"bind_ua"` +} + +type DatabaseConfig struct { + Debug bool `yaml:"debug"` +} + +type SessionConfig struct { + Lifetime uint `yaml:"lifetime"` +} + +func Load() (*Config, error) { + path := configPath + if !io.Exists(path) { + path = "config.yml" // For testing purpose + } + + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer func() { _ = file.Close() }() + + var conf Config + decoder := yaml.NewDecoder(file) + if err = decoder.Decode(&conf); err != nil { + return nil, err + } + + return &conf, nil +} + +func Save(conf *Config) error { + data, err := yaml.Marshal(conf) + if err != nil { + return err + } + + return os.WriteFile(configPath, data, 0600) +} + +func Check(conf *Config) (bool, error) { + currentConf, err := Load() + if err != nil { + return false, err + } + + currentData, err := yaml.Marshal(currentConf) + if err != nil { + return false, err + } + + newData, err := yaml.Marshal(conf) + if err != nil { + return false, err + } + + return string(currentData) == string(newData), nil +} diff --git a/pkg/types/config.go b/pkg/types/config.go deleted file mode 100644 index 4856f899..00000000 --- a/pkg/types/config.go +++ /dev/null @@ -1,36 +0,0 @@ -package types - -// PanelConfig 面板配置结构体 -type PanelConfig struct { - App PanelAppConfig `yaml:"app"` - HTTP PanelHTTPConfig `yaml:"http"` - Database PanelDatabaseConfig `yaml:"database"` - Session PanelSessionConfig `yaml:"session"` -} - -type PanelAppConfig struct { - Debug bool `yaml:"debug"` - Key string `yaml:"key"` - Locale string `yaml:"locale"` - Timezone string `yaml:"timezone"` - Root string `yaml:"root"` -} - -type PanelHTTPConfig struct { - Debug bool `yaml:"debug"` - Port uint `yaml:"port"` - Entrance string `yaml:"entrance"` - TLS bool `yaml:"tls"` - IPHeader string `yaml:"ip_header"` - BindDomain []string `yaml:"bind_domain"` - BindIP []string `yaml:"bind_ip"` - BindUA []string `yaml:"bind_ua"` -} - -type PanelDatabaseConfig struct { - Debug bool `yaml:"debug"` -} - -type PanelSessionConfig struct { - Lifetime uint `yaml:"lifetime"` -} diff --git a/pkg/webserver/nginx/vhost.go b/pkg/webserver/nginx/vhost.go index e0877529..80eee739 100644 --- a/pkg/webserver/nginx/vhost.go +++ b/pkg/webserver/nginx/vhost.go @@ -7,8 +7,9 @@ import ( "slices" "strings" - "github.com/acepanel/panel/pkg/webserver/types" "github.com/tufanbarisyildirim/gonginx/config" + + "github.com/acepanel/panel/pkg/webserver/types" ) // StaticVhost 纯静态虚拟主机