diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 76ae199a..e8257ca5 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -17,11 +17,21 @@ along with this program. If not, see . package main import ( + "os" _ "time/tzdata" - - "github.com/TheTNB/panel/internal/bootstrap" ) func main() { - bootstrap.BootCli() + if os.Geteuid() != 0 { + panic("panel must run as root") + } + + cli, err := initCli() + if err != nil { + panic(err) + } + + if err = cli.Run(); err != nil { + panic(err) + } } diff --git a/cmd/cli/wire.go b/cmd/cli/wire.go new file mode 100644 index 00000000..ffd2ef61 --- /dev/null +++ b/cmd/cli/wire.go @@ -0,0 +1,18 @@ +//go:build wireinject + +package main + +import ( + "github.com/google/wire" + + "github.com/TheTNB/panel/internal/app" + "github.com/TheTNB/panel/internal/bootstrap" + "github.com/TheTNB/panel/internal/data" + "github.com/TheTNB/panel/internal/route" + "github.com/TheTNB/panel/internal/service" +) + +// initCli init command line. +func initCli() (*app.Cli, error) { + panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, app.NewCli)) +} diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go new file mode 100644 index 00000000..bc040803 --- /dev/null +++ b/cmd/cli/wire_gen.go @@ -0,0 +1,52 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "github.com/TheTNB/panel/internal/app" + "github.com/TheTNB/panel/internal/bootstrap" + "github.com/TheTNB/panel/internal/data" + "github.com/TheTNB/panel/internal/route" + "github.com/TheTNB/panel/internal/service" +) + +import ( + _ "time/tzdata" +) + +// Injectors from wire.go: + +// initCli init command line. +func initCli() (*app.Cli, error) { + koanf, err := bootstrap.NewConf() + if err != nil { + return nil, err + } + logger := bootstrap.NewLog(koanf) + db, err := bootstrap.NewDB(koanf, logger) + if err != nil { + return nil, err + } + cacheRepo := data.NewCacheRepo(db) + queue := bootstrap.NewQueue() + taskRepo := data.NewTaskRepo(db, logger, queue) + appRepo := data.NewAppRepo(db, cacheRepo, taskRepo) + userRepo := data.NewUserRepo(db) + settingRepo := data.NewSettingRepo(db, koanf, taskRepo) + databaseServerRepo := data.NewDatabaseServerRepo(db, logger) + databaseUserRepo := data.NewDatabaseUserRepo(databaseServerRepo) + databaseRepo := data.NewDatabaseRepo(databaseServerRepo, databaseUserRepo) + certRepo := data.NewCertRepo(db) + certAccountRepo := data.NewCertAccountRepo(db, userRepo) + websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo) + backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo) + cliService := service.NewCliService(koanf, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo) + cli := route.NewCli(cliService) + command := bootstrap.NewCli(cli) + appCli := app.NewCli(command) + return appCli, nil +} diff --git a/cmd/web/main.go b/cmd/web/main.go index 96b58e19..2ade59b5 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -17,11 +17,21 @@ along with this program. If not, see . package main import ( + "os" _ "time/tzdata" - - "github.com/TheTNB/panel/internal/bootstrap" ) func main() { - bootstrap.BootWeb() + if os.Geteuid() != 0 { + panic("panel must run as root") + } + + web, err := initWeb() + if err != nil { + panic(err) + } + + if err = web.Run(); err != nil { + panic(err) + } } diff --git a/cmd/web/wire.go b/cmd/web/wire.go new file mode 100644 index 00000000..675ba564 --- /dev/null +++ b/cmd/web/wire.go @@ -0,0 +1,18 @@ +//go:build wireinject + +package main + +import ( + "github.com/google/wire" + + "github.com/TheTNB/panel/internal/app" + "github.com/TheTNB/panel/internal/bootstrap" + "github.com/TheTNB/panel/internal/data" + "github.com/TheTNB/panel/internal/route" + "github.com/TheTNB/panel/internal/service" +) + +// initWeb init application. +func initWeb() (*app.Web, error) { + panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, app.NewWeb)) +} diff --git a/cmd/web/wire_gen.go b/cmd/web/wire_gen.go new file mode 100644 index 00000000..6a92b743 --- /dev/null +++ b/cmd/web/wire_gen.go @@ -0,0 +1,101 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "github.com/TheTNB/panel/internal/app" + "github.com/TheTNB/panel/internal/bootstrap" + "github.com/TheTNB/panel/internal/data" + "github.com/TheTNB/panel/internal/route" + "github.com/TheTNB/panel/internal/service" +) + +import ( + _ "time/tzdata" +) + +// Injectors from wire.go: + +// initWeb init application. +func initWeb() (*app.Web, error) { + koanf, err := bootstrap.NewConf() + if err != nil { + return nil, err + } + logger := bootstrap.NewLog(koanf) + db, err := bootstrap.NewDB(koanf, logger) + if err != nil { + return nil, err + } + manager, err := bootstrap.NewSession(koanf, db) + if err != nil { + return nil, err + } + userRepo := data.NewUserRepo(db) + userService := service.NewUserService(koanf, manager, userRepo) + queue := bootstrap.NewQueue() + taskRepo := data.NewTaskRepo(db, logger, queue) + cacheRepo := data.NewCacheRepo(db) + databaseServerRepo := data.NewDatabaseServerRepo(db, logger) + databaseUserRepo := data.NewDatabaseUserRepo(databaseServerRepo) + databaseRepo := data.NewDatabaseRepo(databaseServerRepo, databaseUserRepo) + certRepo := data.NewCertRepo(db) + certAccountRepo := data.NewCertAccountRepo(db, userRepo) + websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo) + appRepo := data.NewAppRepo(db, cacheRepo, taskRepo) + settingRepo := data.NewSettingRepo(db, koanf, taskRepo) + cronRepo := data.NewCronRepo(db) + backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo) + dashboardService := service.NewDashboardService(koanf, taskRepo, websiteRepo, appRepo, settingRepo, cronRepo, backupRepo) + taskService := service.NewTaskService(taskRepo) + websiteService := service.NewWebsiteService(websiteRepo, settingRepo) + databaseService := service.NewDatabaseService(databaseRepo) + databaseServerService := service.NewDatabaseServerService(databaseServerRepo) + databaseUserService := service.NewDatabaseUserService(databaseUserRepo) + backupService := service.NewBackupService(backupRepo) + certService := service.NewCertService(certRepo) + certDNSRepo := data.NewCertDNSRepo(db) + certDNSService := service.NewCertDNSService(certDNSRepo) + certAccountService := service.NewCertAccountService(certAccountRepo) + appService := service.NewAppService(appRepo, cacheRepo, settingRepo) + cronService := service.NewCronService(cronRepo) + processService := service.NewProcessService() + safeRepo := data.NewSafeRepo() + safeService := service.NewSafeService(safeRepo) + firewallService := service.NewFirewallService() + sshRepo := data.NewSSHRepo(db) + sshService := service.NewSSHService(sshRepo) + containerRepo := data.NewContainerRepo() + containerService := service.NewContainerService(containerRepo) + containerNetworkRepo := data.NewContainerNetworkRepo() + containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo) + containerImageRepo := data.NewContainerImageRepo() + containerImageService := service.NewContainerImageService(containerImageRepo) + containerVolumeRepo := data.NewContainerVolumeRepo() + containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo) + fileService := service.NewFileService(taskRepo) + monitorRepo := data.NewMonitorRepo(db, settingRepo) + monitorService := service.NewMonitorService(settingRepo, monitorRepo) + settingService := service.NewSettingService(settingRepo) + systemctlService := service.NewSystemctlService() + http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService) + wsService := service.NewWsService(koanf, sshRepo) + ws := route.NewWs(wsService) + mux, err := bootstrap.NewRouter(koanf, db, logger, manager, http, ws) + if err != nil { + return nil, err + } + server, err := bootstrap.NewHttp(koanf, mux) + if err != nil { + return nil, err + } + gormigrate := bootstrap.NewMigrate(db) + cron := bootstrap.NewCron(koanf, logger) + validation := bootstrap.NewValidator(db) + web := app.NewWeb(koanf, mux, server, gormigrate, cron, validation) + return web, nil +} diff --git a/go.mod b/go.mod index b1aa3ea5..b8e7d4eb 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23 require ( github.com/bddjr/hlfhr v1.3.5 github.com/beevik/ntp v1.4.3 + github.com/cloudflare/tableflip v1.2.3 github.com/creack/pty v1.1.24 github.com/expr-lang/expr v1.16.9 github.com/glebarez/sqlite v1.11.0 @@ -18,6 +19,7 @@ require ( github.com/go-sql-driver/mysql v1.8.1 github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a github.com/gomodule/redigo v1.9.2 + github.com/google/wire v0.6.0 github.com/gookit/validate v1.5.3 github.com/gorilla/websocket v1.5.3 github.com/hashicorp/go-version v1.7.0 @@ -33,7 +35,6 @@ require ( github.com/mholt/acmez/v3 v3.0.0-20241214053340-45433dfc1161 github.com/orandin/slog-gorm v1.4.0 github.com/robfig/cron/v3 v3.0.1 - github.com/samber/do/v2 v2.0.0-beta.7 github.com/samber/lo v1.47.0 github.com/sethvargo/go-limiter v1.0.0 github.com/shirou/gopsutil v2.21.11+incompatible @@ -73,12 +74,10 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/samber/go-type-to-string v1.4.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.uber.org/goleak v1.3.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index f279c340..a1446083 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/bddjr/hlfhr v1.3.5 h1:nBbye1k7XQ+4KJIGKQADk6lIuDUV+fcXG+TUUsFihPk= github.com/bddjr/hlfhr v1.3.5/go.mod h1:oyIv4Q9JpCgZFdtH3KyTNWp7YYRWl4zl8k4ozrMAB4g= github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= +github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw= +github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E= 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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -54,14 +56,18 @@ github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a h1:BAyyIK6rc6Tq9 github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc= github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gookit/filter v1.2.1 h1:37XivkBm2E5qe1KaGdJ5ZfF5l9NYdGWfLEeQadJD8O4= @@ -125,10 +131,6 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/samber/do/v2 v2.0.0-beta.7 h1:tmdLOVSCbTA6uGWLU5poi/nZvMRh5QxXFJ9vHytU+Jk= -github.com/samber/do/v2 v2.0.0-beta.7/go.mod h1:+LpV3vu4L81Q1JMZNSkMvSkW9lt4e5eJoXoZHkeBS4c= -github.com/samber/go-type-to-string v1.4.0 h1:KXphToZgiFdnJQxryU25brhlh/CqY/cwJVeX2rfmow0= -github.com/samber/go-type-to-string v1.4.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/sethvargo/go-limiter v1.0.0 h1:JqW13eWEMn0VFv86OKn8wiYJY/m250WoXdrjRV0kLe4= @@ -151,32 +153,81 @@ github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjc github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/app/cli.go b/internal/app/cli.go new file mode 100644 index 00000000..e243af37 --- /dev/null +++ b/internal/app/cli.go @@ -0,0 +1,23 @@ +package app + +import ( + "context" + "os" + + "github.com/urfave/cli/v3" +) + +type Cli struct { + cmd *cli.Command +} + +func NewCli(cmd *cli.Command) *Cli { + IsCli = true + return &Cli{ + cmd: cmd, + } +} + +func (r *Cli) Run() error { + return r.cmd.Run(context.Background(), os.Args) +} diff --git a/internal/app/global.go b/internal/app/global.go index 21eb7f10..70d54cbe 100644 --- a/internal/app/global.go +++ b/internal/app/global.go @@ -1,27 +1,11 @@ package app import ( - "log/slog" - - "github.com/go-chi/chi/v5" - "github.com/go-rat/sessions" - "github.com/go-rat/utils/crypt" - "github.com/knadh/koanf/v2" - "github.com/robfig/cron/v3" "gorm.io/gorm" - - "github.com/TheTNB/panel/pkg/queue" ) var ( - Conf *koanf.Koanf - Http *chi.Mux - Orm *gorm.DB - Session *sessions.Manager - Cron *cron.Cron - Queue *queue.Queue - Logger *slog.Logger - Crypter crypt.Crypter + Orm *gorm.DB ) // 面板状态常量 diff --git a/internal/app/web.go b/internal/app/web.go new file mode 100644 index 00000000..80d76a4b --- /dev/null +++ b/internal/app/web.go @@ -0,0 +1,124 @@ +package app + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "runtime" + "syscall" + "time" + + "github.com/bddjr/hlfhr" + "github.com/cloudflare/tableflip" + "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" +) + +type Web struct { + conf *koanf.Koanf + router *chi.Mux + server *hlfhr.Server + migrator *gormigrate.Gormigrate + cron *cron.Cron +} + +func NewWeb(conf *koanf.Koanf, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, _ *validate.Validation) *Web { + return &Web{ + conf: conf, + router: router, + server: server, + migrator: migrator, + cron: cron, + } +} + +func (r *Web) Run() error { + // migrate database + if err := r.migrator.Migrate(); err != nil { + return err + } + fmt.Println("[DB] database migrated") + + // start cron scheduler + r.cron.Start() + fmt.Println("[CRON] cron scheduler started") + + // run http server + if runtime.GOOS != "windows" { + return r.runServer() + } + + return r.runServerFallback() +} + +// runServer graceful run server +func (r *Web) runServer() error { + upg, err := tableflip.New(tableflip.Options{}) + if err != nil { + return err + } + defer upg.Stop() + + // By prefixing PID to log, easy to interrupt from another process. + log.SetPrefix(fmt.Sprintf("[PID %d]", os.Getpid())) + + // Listen for the process signal to trigger the tableflip upgrade. + go func() { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGHUP) + for range sig { + if err = upg.Upgrade(); err != nil { + log.Println("[Graceful] upgrade failed:", err) + } + } + }() + + ln, err := upg.Listen("tcp", r.conf.MustString("http.address")) + if err != nil { + return err + } + defer ln.Close() + + fmt.Println("[HTTP] listening and serving on", r.conf.MustString("http.address")) + go func() { + if err = r.server.Serve(ln); !errors.Is(err, http.ErrServerClosed) { + log.Println("[HTTP] server error:", err) + } + }() + + // tableflip ready + if err = upg.Ready(); err != nil { + return err + } + + fmt.Println("[Graceful] ready for upgrade") + <-upg.Exit() + + // Make sure to set a deadline on exiting the process + // after upg.Exit() is closed. No new upgrades can be + // performed if the parent doesn't exit. + time.AfterFunc(60*time.Second, func() { + log.Println("[Graceful] shutdown timeout, force exit") + os.Exit(1) + }) + + // Wait for connections to drain. + return r.server.Shutdown(context.Background()) +} + +// runServerFallback fallback for windows +func (r *Web) runServerFallback() error { + fmt.Println("[HTTP] listening and serving on", r.conf.MustString("http.address")) + if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { + return err + } + + return nil +} diff --git a/internal/apps/fail2ban/service.go b/internal/apps/fail2ban/service.go index cecefbc9..b439a1e8 100644 --- a/internal/apps/fail2ban/service.go +++ b/internal/apps/fail2ban/service.go @@ -12,7 +12,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/service" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/shell" @@ -24,7 +23,7 @@ type Service struct { func NewService() *Service { return &Service{ - websiteRepo: data.NewWebsiteRepo(), + websiteRepo: nil, // TODO fixme } } diff --git a/internal/apps/mysql/service.go b/internal/apps/mysql/service.go index 0b3003c2..e46a21b3 100644 --- a/internal/apps/mysql/service.go +++ b/internal/apps/mysql/service.go @@ -10,7 +10,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/service" "github.com/TheTNB/panel/pkg/db" "github.com/TheTNB/panel/pkg/io" @@ -26,7 +25,7 @@ type Service struct { func NewService() *Service { return &Service{ - settingRepo: data.NewSettingRepo(), + settingRepo: nil, // TODO fixme } } diff --git a/internal/apps/php/service.go b/internal/apps/php/service.go index e2799a74..59e4f450 100644 --- a/internal/apps/php/service.go +++ b/internal/apps/php/service.go @@ -13,7 +13,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/service" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/shell" @@ -28,7 +27,7 @@ type Service struct { func NewService(version uint) *Service { return &Service{ version: version, - taskRepo: data.NewTaskRepo(), + taskRepo: nil, // TODO fixme } } diff --git a/internal/apps/s3fs/service.go b/internal/apps/s3fs/service.go index 98a5030a..8d0aec22 100644 --- a/internal/apps/s3fs/service.go +++ b/internal/apps/s3fs/service.go @@ -10,7 +10,6 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/service" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/shell" @@ -22,7 +21,7 @@ type Service struct { func NewService() *Service { return &Service{ - settingRepo: data.NewSettingRepo(), + settingRepo: nil, // TODO fixme } } diff --git a/internal/biz/backup.go b/internal/biz/backup.go index 04d327cb..e4f80643 100644 --- a/internal/biz/backup.go +++ b/internal/biz/backup.go @@ -21,4 +21,6 @@ type BackupRepo interface { ClearExpired(path, prefix string, save int) error CutoffLog(path, target string) error GetPath(typ BackupType) (string, error) + FixPanel() error + UpdatePanel(version, url, checksum string) error } diff --git a/internal/biz/database_server.go b/internal/biz/database_server.go index 67cec7b1..8b7d6b41 100644 --- a/internal/biz/database_server.go +++ b/internal/biz/database_server.go @@ -5,7 +5,6 @@ import ( "gorm.io/gorm" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/http/request" ) @@ -31,21 +30,23 @@ type DatabaseServer struct { } func (r *DatabaseServer) BeforeSave(tx *gorm.DB) error { - var err error + // TODO fix + /*var err error r.Password, err = app.Crypter.Encrypt([]byte(r.Password)) if err != nil { return err - } + }*/ return nil } func (r *DatabaseServer) AfterFind(tx *gorm.DB) error { - password, err := app.Crypter.Decrypt(r.Password) + // TODO fix + /*password, err := app.Crypter.Decrypt(r.Password) if err == nil { r.Password = string(password) - } + }*/ return nil } @@ -59,5 +60,6 @@ type DatabaseServerRepo interface { Update(req *request.DatabaseServerUpdate) error UpdateRemark(req *request.DatabaseServerUpdateRemark) error Delete(id uint) error + ClearUsers(id uint) error Sync(id uint) error } diff --git a/internal/biz/database_user.go b/internal/biz/database_user.go index b80ddd7e..ddccb152 100644 --- a/internal/biz/database_user.go +++ b/internal/biz/database_user.go @@ -5,7 +5,6 @@ import ( "gorm.io/gorm" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/http/request" ) @@ -32,21 +31,23 @@ type DatabaseUser struct { } func (r *DatabaseUser) BeforeSave(tx *gorm.DB) error { - var err error + // TODO fix + /*var err error r.Password, err = app.Crypter.Encrypt([]byte(r.Password)) if err != nil { return err - } + }*/ return nil } func (r *DatabaseUser) AfterFind(tx *gorm.DB) error { - password, err := app.Crypter.Decrypt(r.Password) + // TODO fix + /*password, err := app.Crypter.Decrypt(r.Password) if err == nil { r.Password = string(password) - } + }*/ return nil } @@ -60,5 +61,4 @@ type DatabaseUserRepo interface { UpdateRemark(req *request.DatabaseUserUpdateRemark) error Delete(id uint) error DeleteByNames(serverID uint, names []string) error - DeleteByServerID(serverID uint) error } diff --git a/internal/biz/setting.go b/internal/biz/setting.go index 72253956..bd4fa5c6 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -35,6 +35,4 @@ type SettingRepo interface { Delete(key SettingKey) error GetPanelSetting(ctx context.Context) (*request.PanelSetting, error) UpdatePanelSetting(ctx context.Context, setting *request.PanelSetting) (bool, error) - UpdatePanel(version, url, checksum string) error - FixPanel() error } diff --git a/internal/biz/ssh.go b/internal/biz/ssh.go index c944d3a8..e9e2422a 100644 --- a/internal/biz/ssh.go +++ b/internal/biz/ssh.go @@ -5,7 +5,6 @@ import ( "gorm.io/gorm" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/ssh" ) @@ -22,7 +21,8 @@ type SSH struct { } func (r *SSH) BeforeSave(tx *gorm.DB) error { - var err error + // TODO fix + /*var err error r.Config.Key, err = app.Crypter.Encrypt([]byte(r.Config.Key)) if err != nil { return err @@ -30,21 +30,22 @@ func (r *SSH) BeforeSave(tx *gorm.DB) error { r.Config.Password, err = app.Crypter.Encrypt([]byte(r.Config.Password)) if err != nil { return err - } + }*/ return nil } func (r *SSH) AfterFind(tx *gorm.DB) error { - key, err := app.Crypter.Decrypt(r.Config.Key) + // TODO fix + /*key, err := app.Crypter.Decrypt(r.Config.Key) if err == nil { r.Config.Key = string(key) } password, err := app.Crypter.Decrypt(r.Config.Password) if err == nil { r.Config.Password = string(password) - } + }*/ return nil } diff --git a/internal/bootstrap/app.go b/internal/bootstrap/app.go deleted file mode 100644 index 79286242..00000000 --- a/internal/bootstrap/app.go +++ /dev/null @@ -1,33 +0,0 @@ -package bootstrap - -import ( - "os" -) - -func boot() { - if os.Geteuid() != 0 { - panic("panel must run as root") - } - - initConf() - initGlobal() - initLogger() - initOrm() - runMigrate() - bootCrypter() -} - -func BootWeb() { - boot() - initSession() - initQueue() - initCron() - initHttp() - - select {} -} - -func BootCli() { - boot() - initCli() -} diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go new file mode 100644 index 00000000..372c3140 --- /dev/null +++ b/internal/bootstrap/bootstrap.go @@ -0,0 +1,6 @@ +package bootstrap + +import "github.com/google/wire" + +// ProviderSet is bootstrap providers. +var ProviderSet = wire.NewSet(NewConf, NewLog, NewCli, NewValidator, NewRouter, NewHttp, NewDB, NewMigrate, NewSession, NewCron, NewQueue, NewCrypter) diff --git a/internal/bootstrap/cli.go b/internal/bootstrap/cli.go index 59cd3ef9..9c538c5d 100644 --- a/internal/bootstrap/cli.go +++ b/internal/bootstrap/cli.go @@ -1,9 +1,6 @@ package bootstrap import ( - "context" - "fmt" - "os" "strings" "github.com/urfave/cli/v3" @@ -12,9 +9,7 @@ import ( "github.com/TheTNB/panel/internal/route" ) -func initCli() { - app.IsCli = true - +func NewCli(cmd *route.Cli) *cli.Command { cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "NAME", "名称") cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "USAGE", "用法") cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "VERSION", "版本") @@ -38,13 +33,10 @@ func initCli() { cli.RootCommandHelpTemplate += "\n论坛:https://bbs.haozi.net" cli.RootCommandHelpTemplate += "\nQ群:12370907\n" - cmd := &cli.Command{ + return &cli.Command{ Name: "panel-cli", Usage: "耗子面板命令行工具", Version: app.Version, - Commands: route.Cli(), - } - if err := cmd.Run(context.Background(), os.Args); err != nil { - fmt.Printf("|-%v\n", err) + Commands: cmd.Commands(), } } diff --git a/internal/bootstrap/conf.go b/internal/bootstrap/conf.go index 740d0604..599e670a 100644 --- a/internal/bootstrap/conf.go +++ b/internal/bootstrap/conf.go @@ -12,29 +12,32 @@ import ( "github.com/TheTNB/panel/pkg/io" ) -func initConf() { +func NewConf() (*koanf.Koanf, error) { config := "/usr/local/etc/panel/config.yml" if !io.Exists(config) { config = "config.yml" } - app.Conf = koanf.New(".") - if err := app.Conf.Load(file.Provider(config), yaml.Parser()); err != nil { - log.Fatalf("failed to load config: %v", err) + conf := koanf.New(".") + if err := conf.Load(file.Provider(config), yaml.Parser()); err != nil { + return nil, err } + + initGlobal(conf) + return conf, nil } -func initGlobal() { - app.Key = app.Conf.MustString("app.key") +func initGlobal(conf *koanf.Koanf) { + app.Key = conf.MustString("app.key") if len(app.Key) != 32 { log.Fatalf("app key must be 32 characters") } - app.Root = app.Conf.MustString("app.root") - app.Locale = app.Conf.MustString("app.locale") + app.Root = conf.MustString("app.root") + app.Locale = conf.MustString("app.locale") // 初始化时区 - loc, err := time.LoadLocation(app.Conf.MustString("app.timezone")) + loc, err := time.LoadLocation(conf.MustString("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 7b31d306..945da3a8 100644 --- a/internal/bootstrap/cron.go +++ b/internal/bootstrap/cron.go @@ -1,29 +1,22 @@ package bootstrap import ( - "log" + "log/slog" + "github.com/knadh/koanf/v2" "github.com/robfig/cron/v3" - "github.com/TheTNB/panel/internal/app" - "github.com/TheTNB/panel/internal/job" pkgcron "github.com/TheTNB/panel/pkg/cron" ) -func initCron() { - logger := pkgcron.NewLogger(app.Logger, app.Conf.Bool("app.debug")) - c := cron.New( +func NewCron(conf *koanf.Koanf, log *slog.Logger) *cron.Cron { + logger := pkgcron.NewLogger(log, conf.Bool("app.debug")) + + return cron.New( cron.WithParser(cron.NewParser( cron.SecondOptional|cron.Minute|cron.Hour|cron.Dom|cron.Month|cron.Dow|cron.Descriptor, )), cron.WithLogger(logger), cron.WithChain(cron.Recover(logger), cron.SkipIfStillRunning(logger)), ) - app.Cron = c - - if err := job.Boot(app.Cron); err != nil { - log.Fatalf("failed to boot cron jobs: %v", err) - } - - c.Start() } diff --git a/internal/bootstrap/crypter.go b/internal/bootstrap/crypter.go index 9aab50e2..a1be4526 100644 --- a/internal/bootstrap/crypter.go +++ b/internal/bootstrap/crypter.go @@ -1,18 +1,10 @@ package bootstrap import ( - "log" - "github.com/go-rat/utils/crypt" - - "github.com/TheTNB/panel/internal/app" + "github.com/knadh/koanf/v2" ) -func bootCrypter() { - crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key)) - if err != nil { - log.Fatalf("failed to create crypter: %v", err) - } - - app.Crypter = crypter +func NewCrypter(conf *koanf.Koanf) (crypt.Crypter, error) { + return crypt.NewXChacha20Poly1305([]byte(conf.MustString("app.key"))) } diff --git a/internal/bootstrap/db.go b/internal/bootstrap/db.go index ba826c84..0f5faacb 100644 --- a/internal/bootstrap/db.go +++ b/internal/bootstrap/db.go @@ -1,7 +1,8 @@ package bootstrap import ( - "log" + "github.com/knadh/koanf/v2" + "log/slog" "path/filepath" "github.com/glebarez/sqlite" @@ -13,23 +14,17 @@ import ( "github.com/TheTNB/panel/internal/migration" ) -func initOrm() { - db, err := gorm.Open(sqlite.Open(filepath.Join(app.Root, "panel/storage/app.db")), &gorm.Config{ - Logger: slogGorm.New(slogGorm.WithHandler(app.Logger.Handler())), +func NewDB(conf *koanf.Koanf, log *slog.Logger) (*gorm.DB, error) { + // You can use any other database, like MySQL or PostgreSQL. + return gorm.Open(sqlite.Open(filepath.Join(app.Root, "panel/storage/app.db")), &gorm.Config{ + Logger: slogGorm.New(slogGorm.WithHandler(log.Handler())), SkipDefaultTransaction: true, DisableForeignKeyConstraintWhenMigrating: true, }) - if err != nil { - log.Fatalf("failed to connect database: %v", err) - } - app.Orm = db } -func runMigrate() { - migrator := gormigrate.New(app.Orm, &gormigrate.Options{ +func NewMigrate(db *gorm.DB) *gormigrate.Gormigrate { + return gormigrate.New(db, &gormigrate.Options{ UseTransaction: true, // Note: MySQL not support DDL transaction }, migration.Migrations) - if err := migrator.Migrate(); err != nil { - log.Fatalf("failed to migrate database: %v", err) - } } diff --git a/internal/bootstrap/http.go b/internal/bootstrap/http.go index d3e3ca19..06a9532e 100644 --- a/internal/bootstrap/http.go +++ b/internal/bootstrap/http.go @@ -2,55 +2,46 @@ package bootstrap import ( "crypto/tls" - "fmt" - "log" - "net/http" - "path/filepath" - "github.com/bddjr/hlfhr" "github.com/go-chi/chi/v5" + "github.com/go-rat/sessions" + "github.com/knadh/koanf/v2" + "gorm.io/gorm" + "log/slog" + "net/http" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/http/middleware" "github.com/TheTNB/panel/internal/route" ) -func initHttp() { - app.Http = chi.NewRouter() +func NewRouter(conf *koanf.Koanf, db *gorm.DB, log *slog.Logger, session *sessions.Manager, http *route.Http, ws *route.Ws) (*chi.Mux, error) { + r := chi.NewRouter() // add middleware - app.Http.Use(middleware.GlobalMiddleware()...) + r.Use(middleware.GlobalMiddleware(r, conf, db, log, session)...) + // add http route + http.Register(r) + // add ws route + ws.Register(r) - // add route - route.Http(app.Http) - route.Ws(app.Http) + return r, nil +} +func NewHttp(conf *koanf.Koanf, r *chi.Mux) (*hlfhr.Server, error) { srv := hlfhr.New(&http.Server{ - Addr: fmt.Sprintf(":%d", app.Conf.MustInt("http.port")), - Handler: http.AllowQuerySemicolons(app.Http), + Addr: conf.MustString("http.address"), + Handler: http.AllowQuerySemicolons(r), MaxHeaderBytes: 2048 << 20, }) srv.HttpOnHttpsPortErrorHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { hlfhr.RedirectToHttps(w, r, http.StatusTemporaryRedirect) }) - if app.Conf.Bool("http.tls") { + if conf.Bool("http.tls") { srv.TLSConfig = &tls.Config{ MinVersion: tls.VersionTLS13, } - - cert := filepath.Join(app.Root, "panel/storage/cert.pem") - key := filepath.Join(app.Root, "panel/storage/cert.key") - go func() { - if err := srv.ListenAndServeTLS(cert, key); err != nil { - log.Fatalf("failed to start https server: %v", err) - } - }() - } else { - go func() { - if err := srv.ListenAndServe(); err != nil { - log.Fatalf("failed to start http server: %v", err) - } - }() } + + return srv, nil } diff --git a/internal/bootstrap/logger.go b/internal/bootstrap/logger.go index 83277f7b..98780839 100644 --- a/internal/bootstrap/logger.go +++ b/internal/bootstrap/logger.go @@ -1,6 +1,7 @@ package bootstrap import ( + "github.com/knadh/koanf/v2" "log/slog" "path/filepath" @@ -8,7 +9,7 @@ import ( "gopkg.in/natefinch/lumberjack.v2" ) -func initLogger() { +func NewLog(conf *koanf.Koanf) *slog.Logger { ljLogger := &lumberjack.Logger{ Filename: filepath.Join(app.Root, "panel/storage/logs/app.log"), MaxSize: 10, @@ -17,12 +18,14 @@ func initLogger() { } level := slog.LevelInfo - if app.Conf.Bool("app.debug") { + if conf.Bool("app.debug") { level = slog.LevelDebug } - app.Logger = slog.New(slog.NewJSONHandler(ljLogger, &slog.HandlerOptions{ + log := slog.New(slog.NewJSONHandler(ljLogger, &slog.HandlerOptions{ Level: level, })) - slog.SetDefault(app.Logger) + slog.SetDefault(log) + + return log } diff --git a/internal/bootstrap/queue.go b/internal/bootstrap/queue.go index 19d7ab06..199a0b77 100644 --- a/internal/bootstrap/queue.go +++ b/internal/bootstrap/queue.go @@ -1,13 +1,9 @@ package bootstrap import ( - "context" - - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/pkg/queue" ) -func initQueue() { - app.Queue = queue.New(100) - go app.Queue.Run(context.Background()) +func NewQueue() *queue.Queue { + return queue.New(100) } diff --git a/internal/bootstrap/session.go b/internal/bootstrap/session.go index 5a867da0..20dcfc20 100644 --- a/internal/bootstrap/session.go +++ b/internal/bootstrap/session.go @@ -1,31 +1,29 @@ package bootstrap import ( - "log" - "github.com/go-rat/gormstore" "github.com/go-rat/sessions" - - "github.com/TheTNB/panel/internal/app" + "github.com/knadh/koanf/v2" + "gorm.io/gorm" ) -func initSession() { +func NewSession(conf *koanf.Koanf, db *gorm.DB) (*sessions.Manager, error) { // initialize session manager manager, err := sessions.NewManager(&sessions.ManagerOptions{ - Key: app.Key, + Key: conf.MustString("app.key"), Lifetime: 120, GcInterval: 30, DisableDefaultDriver: true, }) if err != nil { - log.Fatalf("failed to initialize session manager: %v", err) + return nil, err } // extend gorm store driver - store := gormstore.New(app.Orm) + store := gormstore.New(db) if err = manager.Extend("default", store); err != nil { - log.Fatalf("failed to extend session manager: %v", err) + return nil, err } - app.Session = manager + return manager, nil } diff --git a/internal/bootstrap/validator.go b/internal/bootstrap/validator.go index 4d64fe19..d2a9fee4 100644 --- a/internal/bootstrap/validator.go +++ b/internal/bootstrap/validator.go @@ -3,13 +3,22 @@ package bootstrap import ( "github.com/gookit/validate" "github.com/gookit/validate/locales/zhcn" + "gorm.io/gorm" + + "github.com/TheTNB/panel/internal/http/rule" ) -func init() { +// NewValidator just for register global rules +func NewValidator(db *gorm.DB) *validate.Validation { zhcn.RegisterGlobal() validate.Config(func(opt *validate.GlobalOption) { opt.StopOnError = false opt.SkipOnEmpty = true opt.FieldTag = "form" }) + + // register global rules + rule.GlobalRules(db) + + return validate.NewEmpty() } diff --git a/internal/data/app.go b/internal/data/app.go index 046a0e9e..461d0f3a 100644 --- a/internal/data/app.go +++ b/internal/data/app.go @@ -4,12 +4,12 @@ import ( "encoding/json" "errors" "fmt" + "gorm.io/gorm" "slices" "github.com/expr-lang/expr" "github.com/go-rat/utils/collect" "github.com/hashicorp/go-version" - "github.com/samber/do/v2" "github.com/spf13/cast" "github.com/TheTNB/panel/internal/app" @@ -18,14 +18,22 @@ import ( "github.com/TheTNB/panel/pkg/shell" ) -type appRepo struct{} +type appRepo struct { + db *gorm.DB + cache biz.CacheRepo + task biz.TaskRepo +} -func NewAppRepo() biz.AppRepo { - return do.MustInvoke[biz.AppRepo](injector) +func NewAppRepo(db *gorm.DB, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo { + return &appRepo{ + db: db, + cache: cache, + task: task, + } } func (r *appRepo) All() api.Apps { - cached, err := NewCacheRepo().Get(biz.CacheKeyApps) + cached, err := r.cache.Get(biz.CacheKeyApps) if err != nil { return nil } @@ -69,7 +77,7 @@ func (r *appRepo) UpdateExist(slug string) bool { func (r *appRepo) Installed() ([]*biz.App, error) { var apps []*biz.App - if err := app.Orm.Find(&apps).Error; err != nil { + if err := r.db.Find(&apps).Error; err != nil { return nil, err } @@ -79,7 +87,7 @@ func (r *appRepo) Installed() ([]*biz.App, error) { func (r *appRepo) GetInstalled(slug string) (*biz.App, error) { installed := new(biz.App) - if err := app.Orm.Where("slug = ?", slug).First(installed).Error; err != nil { + if err := r.db.Where("slug = ?", slug).First(installed).Error; err != nil { return nil, err } @@ -88,7 +96,7 @@ func (r *appRepo) GetInstalled(slug string) (*biz.App, error) { func (r *appRepo) GetInstalledAll(query string, cond ...string) ([]*biz.App, error) { var apps []*biz.App - if err := app.Orm.Where(query, cond).Find(&apps).Error; err != nil { + if err := r.db.Where(query, cond).Find(&apps).Error; err != nil { return nil, err } @@ -97,7 +105,7 @@ func (r *appRepo) GetInstalledAll(query string, cond ...string) ([]*biz.App, err func (r *appRepo) GetHomeShow() ([]map[string]string, error) { var apps []*biz.App - if err := app.Orm.Where("show = ?", true).Order("show_order").Find(&apps).Error; err != nil { + if err := r.db.Where("show = ?", true).Order("show_order").Find(&apps).Error; err != nil { return nil, err } @@ -122,11 +130,11 @@ func (r *appRepo) GetHomeShow() ([]map[string]string, error) { func (r *appRepo) IsInstalled(query string, cond ...string) (bool, error) { var count int64 if len(cond) == 0 { - if err := app.Orm.Model(&biz.App{}).Where("slug = ?", query).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.App{}).Where("slug = ?", query).Count(&count).Error; err != nil { return false, err } } else { - if err := app.Orm.Model(&biz.App{}).Where(query, cond).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.App{}).Where(query, cond).Count(&count).Error; err != nil { return false, err } } @@ -182,7 +190,7 @@ func (r *appRepo) Install(channel, slug string) error { task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug) task.Log = "/tmp/" + item.Slug + ".log" - return NewTaskRepo().Push(task) + return r.task.Push(task) } func (r *appRepo) UnInstall(slug string) error { @@ -237,7 +245,7 @@ func (r *appRepo) UnInstall(slug string) error { task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug) task.Log = "/tmp/" + item.Slug + ".log" - return NewTaskRepo().Push(task) + return r.task.Push(task) } func (r *appRepo) Update(slug string) error { @@ -292,7 +300,7 @@ func (r *appRepo) Update(slug string) error { task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug) task.Log = "/tmp/" + item.Slug + ".log" - return NewTaskRepo().Push(task) + return r.task.Push(task) } func (r *appRepo) UpdateShow(slug string, show bool) error { @@ -303,7 +311,7 @@ func (r *appRepo) UpdateShow(slug string, show bool) error { item.Show = show - return app.Orm.Save(item).Error + return r.db.Save(item).Error } func (r *appRepo) preCheck(app *api.App) error { diff --git a/internal/data/backup.go b/internal/data/backup.go index 0c34401d..b0d5cb6d 100644 --- a/internal/data/backup.go +++ b/internal/data/backup.go @@ -3,13 +3,13 @@ package data import ( "errors" "fmt" + "gorm.io/gorm" "os" "path/filepath" "slices" "strings" "time" - "github.com/samber/do/v2" "github.com/shirou/gopsutil/disk" "github.com/TheTNB/panel/internal/app" @@ -21,10 +21,18 @@ import ( "github.com/TheTNB/panel/pkg/types" ) -type backupRepo struct{} +type backupRepo struct { + db *gorm.DB + setting biz.SettingRepo + website biz.WebsiteRepo +} -func NewBackupRepo() biz.BackupRepo { - return do.MustInvoke[biz.BackupRepo](injector) +func NewBackupRepo(db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo { + return &backupRepo{ + db: db, + setting: setting, + website: website, + } } // List 备份列表 @@ -192,7 +200,7 @@ func (r *backupRepo) ClearExpired(path, prefix string, save int) error { // GetPath 获取备份路径 func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) { - backupPath, err := NewSettingRepo().Get(biz.SettingKeyBackupPath) + backupPath, err := r.setting.Get(biz.SettingKeyBackupPath) if err != nil { return "", err } @@ -212,7 +220,7 @@ func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) { // createWebsite 创建网站备份 func (r *backupRepo) createWebsite(to string, name string) error { - website, err := NewWebsiteRepo().GetByName(name) + website, err := r.website.GetByName(name) if err != nil { return err } @@ -236,7 +244,7 @@ func (r *backupRepo) createWebsite(to string, name string) error { // createMySQL 创建 MySQL 备份 func (r *backupRepo) createMySQL(to string, name string) error { - rootPassword, err := NewSettingRepo().Get(biz.SettingKeyMySQLRootPassword) + rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword) if err != nil { return err } @@ -367,7 +375,7 @@ func (r *backupRepo) restoreWebsite(backup, target string) error { return errors.New("备份文件不存在") } - website, err := NewWebsiteRepo().GetByName(target) + website, err := r.website.GetByName(target) if err != nil { return err } @@ -394,7 +402,7 @@ func (r *backupRepo) restoreMySQL(backup, target string) error { return errors.New("备份文件不存在") } - rootPassword, err := NewSettingRepo().Get(biz.SettingKeyMySQLRootPassword) + rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword) if err != nil { return err } @@ -542,3 +550,265 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) { return backup, nil } + +func (r *backupRepo) FixPanel() error { + if app.IsCli { + fmt.Println("|-开始修复面板...") + } + + // 检查关键文件是否正常 + flag := false + if !io.Exists(filepath.Join(app.Root, "panel", "web")) { + flag = true + } + if !io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) { + flag = true + } + if io.Exists("/tmp/panel-storage.zip") { + flag = true + } + if !flag { + return fmt.Errorf("文件正常无需修复,请运行 panel-cli update 更新面板") + } + + // 再次确认是否需要修复 + if io.Exists("/tmp/panel-storage.zip") { + // 文件齐全情况下只移除临时文件 + if io.Exists(filepath.Join(app.Root, "panel", "web")) && + io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) && + io.Exists("/usr/local/etc/panel/config.yml") { + if err := io.Remove("/tmp/panel-storage.zip"); err != nil { + return fmt.Errorf("清理临时文件失败:%w", err) + } + if app.IsCli { + fmt.Println("|-已清理临时文件,请运行 panel-cli update 更新面板") + } + return nil + } + } + + // 从备份目录中找最新的备份文件 + list, err := r.List(biz.BackupTypePanel) + if err != nil { + return err + } + slices.SortFunc(list, func(a *types.BackupFile, b *types.BackupFile) int { + return int(b.Time.Unix() - a.Time.Unix()) + }) + if len(list) == 0 { + return fmt.Errorf("未找到备份文件,无法自动修复") + } + latest := list[0] + if app.IsCli { + fmt.Printf("|-使用备份文件:%s\n", latest.Name) + } + + // 解压备份文件 + if app.IsCli { + fmt.Println("|-解压备份文件...") + } + if err = io.Remove("/tmp/panel-fix"); err != nil { + return fmt.Errorf("清理临时目录失败:%w", err) + } + if err = io.UnCompress(latest.Path, "/tmp/panel-fix"); err != nil { + return fmt.Errorf("解压备份文件失败:%w", err) + } + + // 移动文件到对应位置 + if app.IsCli { + fmt.Println("|-移动备份文件...") + } + if io.Exists(filepath.Join("/tmp/panel-fix", "panel")) && io.IsDir(filepath.Join("/tmp/panel-fix", "panel")) { + if err = io.Remove(filepath.Join(app.Root, "panel")); err != nil { + return fmt.Errorf("删除目录失败:%w", err) + } + if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel"), filepath.Join(app.Root)); err != nil { + return fmt.Errorf("移动目录失败:%w", err) + } + } + if io.Exists(filepath.Join("/tmp/panel-fix", "config.yml")) { + if err = io.Mv(filepath.Join("/tmp/panel-fix", "config.yml"), "/usr/local/etc/panel/config.yml"); err != nil { + return fmt.Errorf("移动文件失败:%w", err) + } + } + if io.Exists(filepath.Join("/tmp/panel-fix", "panel-cli")) { + if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel-cli"), "/usr/local/sbin/panel-cli"); err != nil { + return fmt.Errorf("移动文件失败:%w", err) + } + } + + // tmp 目录下如果有 storage 备份,则解压回去 + if app.IsCli { + fmt.Println("|-恢复面板数据...") + } + if io.Exists("/tmp/panel-storage.zip") { + if err = io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel")); err != nil { + return fmt.Errorf("恢复面板数据失败:%w", err) + } + if err = io.Remove("/tmp/panel-storage.zip"); err != nil { + return fmt.Errorf("清理临时文件失败:%w", err) + } + } + + // 下载服务文件 + if !io.Exists("/etc/systemd/system/panel.service") { + if _, err = shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil { + return err + } + } + + // 处理权限 + if app.IsCli { + fmt.Println("|-设置关键文件权限...") + } + if err = io.Chmod("/usr/local/etc/panel/config.yml", 0600); err != nil { + return err + } + if err = io.Chmod("/etc/systemd/system/panel.service", 0700); err != nil { + return err + } + if err = io.Chmod("/usr/local/sbin/panel-cli", 0700); err != nil { + return err + } + if err = io.Chmod(filepath.Join(app.Root, "panel"), 0700); err != nil { + return err + } + + if app.IsCli { + fmt.Println("|-修复完成") + } + + tools.RestartPanel() + return nil +} + +func (r *backupRepo) UpdatePanel(version, url, checksum string) error { + // 预先优化数据库 + if err := r.db.Exec("VACUUM").Error; err != nil { + return err + } + if err := r.db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil { + return err + } + + name := filepath.Base(url) + if app.IsCli { + fmt.Printf("|-目标版本:%s\n", version) + fmt.Printf("|-下载链接:%s\n", url) + fmt.Printf("|-文件名:%s\n", name) + } + + if app.IsCli { + fmt.Println("|-正在下载...") + } + if _, err := shell.Execf("wget -T 120 -t 3 -O /tmp/%s %s", name, url); err != nil { + return fmt.Errorf("下载失败:%w", err) + } + if _, err := shell.Execf("wget -T 20 -t 3 -O /tmp/%s %s", name+".sha256", checksum); err != nil { + return fmt.Errorf("下载失败:%w", err) + } + if !io.Exists(filepath.Join("/tmp", name)) || !io.Exists(filepath.Join("/tmp", name+".sha256")) { + return errors.New("下载文件检查失败") + } + + if app.IsCli { + fmt.Println("|-校验下载文件...") + } + if check, err := shell.Execf("cd /tmp && sha256sum -c %s --ignore-missing", name+".sha256"); check != name+": OK" || err != nil { + return errors.New("下载文件校验失败") + } + if err := io.Remove(filepath.Join("/tmp", name+".sha256")); err != nil { + if app.IsCli { + fmt.Println("|-清理校验文件失败:", err) + } + return fmt.Errorf("清理校验文件失败:%w", err) + } + + if app.IsCli { + fmt.Println("|-前置检查...") + } + if io.Exists("/tmp/panel-storage.zip") { + return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败所致,请运行 panel-cli fix 修复后重试") + } + + if app.IsCli { + fmt.Println("|-备份面板数据...") + } + // 备份面板 + if err := r.Create(biz.BackupTypePanel, ""); err != nil { + if app.IsCli { + fmt.Println("|-备份面板失败:", err) + } + return fmt.Errorf("备份面板失败:%w", err) + } + if err := io.Compress(filepath.Join(app.Root, "panel/storage"), nil, "/tmp/panel-storage.zip"); err != nil { + if app.IsCli { + fmt.Println("|-备份面板数据失败:", err) + } + return fmt.Errorf("备份面板数据失败:%w", err) + } + if !io.Exists("/tmp/panel-storage.zip") { + return errors.New("已备份面板数据检查失败") + } + + if app.IsCli { + fmt.Println("|-清理旧版本...") + } + if _, err := shell.Execf("rm -rf %s/panel/*", app.Root); err != nil { + return fmt.Errorf("清理旧版本失败:%w", err) + } + + if app.IsCli { + fmt.Println("|-解压新版本...") + } + if err := io.UnCompress(filepath.Join("/tmp", name), filepath.Join(app.Root, "panel")); err != nil { + return fmt.Errorf("解压失败:%w", err) + } + if !io.Exists(filepath.Join(app.Root, "panel", "web")) { + return errors.New("解压失败,缺失文件") + } + + if app.IsCli { + fmt.Println("|-恢复面板数据...") + } + if err := io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel", "storage")); err != nil { + return fmt.Errorf("恢复面板数据失败:%w", err) + } + if !io.Exists(filepath.Join(app.Root, "panel/storage/app.db")) { + return errors.New("恢复面板数据失败") + } + + if app.IsCli { + fmt.Println("|-运行更新后脚本...") + } + if _, err := shell.Execf("curl -fsLm 10 https://dl.cdn.haozi.net/panel/auto_update.sh | bash"); err != nil { + return fmt.Errorf("运行面板更新后脚本失败:%w", err) + } + if _, err := shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil { + return fmt.Errorf("下载面板服务文件失败:%w", err) + } + if _, err := shell.Execf("panel-cli setting write version %s", version); err != nil { + return fmt.Errorf("写入面板版本号失败:%w", err) + } + if err := io.Mv(filepath.Join(app.Root, "panel/cli"), "/usr/local/sbin/panel-cli"); err != nil { + return fmt.Errorf("移动面板命令行工具失败:%w", err) + } + + if app.IsCli { + fmt.Println("|-设置关键文件权限...") + } + _ = io.Chmod("/usr/local/sbin/panel-cli", 0700) + _ = io.Chmod("/etc/systemd/system/panel.service", 0700) + _ = io.Chmod(filepath.Join(app.Root, "panel"), 0700) + + if app.IsCli { + fmt.Println("|-更新完成") + } + + _, _ = shell.Execf("systemctl daemon-reload") + _ = io.Remove("/tmp/panel-storage.zip") + _ = io.Remove(filepath.Join(app.Root, "panel/config.example.yml")) + tools.RestartPanel() + + return nil +} diff --git a/internal/data/cache.go b/internal/data/cache.go index 962442d3..72ae99af 100644 --- a/internal/data/cache.go +++ b/internal/data/cache.go @@ -5,7 +5,6 @@ import ( "errors" "slices" - "github.com/samber/do/v2" "gorm.io/gorm" "github.com/TheTNB/panel/internal/app" @@ -16,15 +15,19 @@ import ( type cacheRepo struct { api *api.API + db *gorm.DB } -func NewCacheRepo() biz.CacheRepo { - return do.MustInvoke[biz.CacheRepo](injector) +func NewCacheRepo(db *gorm.DB) biz.CacheRepo { + return &cacheRepo{ + api: api.NewAPI(app.Version), + db: db, + } } func (r *cacheRepo) Get(key biz.CacheKey, defaultValue ...string) (string, error) { cache := new(biz.Cache) - if err := app.Orm.Where("key = ?", key).First(cache).Error; err != nil { + if err := r.db.Where("key = ?", key).First(cache).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return "", err } @@ -39,12 +42,12 @@ func (r *cacheRepo) Get(key biz.CacheKey, defaultValue ...string) (string, error func (r *cacheRepo) Set(key biz.CacheKey, value string) error { cache := new(biz.Cache) - if err := app.Orm.Where(biz.Cache{Key: key}).FirstOrInit(cache).Error; err != nil { + if err := r.db.Where(biz.Cache{Key: key}).FirstOrInit(cache).Error; err != nil { return err } cache.Value = value - return app.Orm.Save(cache).Error + return r.db.Save(cache).Error } func (r *cacheRepo) UpdateApps() error { diff --git a/internal/data/cert.go b/internal/data/cert.go index 5b78d753..946a45f9 100644 --- a/internal/data/cert.go +++ b/internal/data/cert.go @@ -4,12 +4,11 @@ import ( "context" "errors" "fmt" + "gorm.io/gorm" "slices" "strings" "time" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" @@ -22,17 +21,20 @@ import ( ) type certRepo struct { + db *gorm.DB client *acme.Client } -func NewCertRepo() biz.CertRepo { - return do.MustInvoke[biz.CertRepo](injector) +func NewCertRepo(db *gorm.DB) biz.CertRepo { + return &certRepo{ + db: db, + } } func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) { var certs []*biz.Cert var total int64 - err := app.Orm.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certs).Error + err := r.db.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certs).Error list := make([]*types.CertList, 0) for cert := range slices.Values(certs) { @@ -64,13 +66,13 @@ func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) { func (r *certRepo) Get(id uint) (*biz.Cert, error) { cert := new(biz.Cert) - err := app.Orm.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Where("id = ?", id).First(cert).Error + err := r.db.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Where("id = ?", id).First(cert).Error return cert, err } func (r *certRepo) GetByWebsite(WebsiteID uint) (*biz.Cert, error) { cert := new(biz.Cert) - err := app.Orm.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Where("website_id = ?", WebsiteID).First(cert).Error + err := r.db.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Where("website_id = ?", WebsiteID).First(cert).Error return cert, err } @@ -89,7 +91,7 @@ func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) { Cert: req.Cert, Key: req.Key, } - if err = app.Orm.Create(cert).Error; err != nil { + if err = r.db.Create(cert).Error; err != nil { return nil, err } @@ -105,7 +107,7 @@ func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) { Domains: req.Domains, AutoRenew: req.AutoRenew, } - if err := app.Orm.Create(cert).Error; err != nil { + if err := r.db.Create(cert).Error; err != nil { return nil, err } return cert, nil @@ -117,7 +119,7 @@ func (r *certRepo) Update(req *request.CertUpdate) error { req.Domains = info.DNSNames } - return app.Orm.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{ + return r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{ ID: req.ID, AccountID: req.AccountID, WebsiteID: req.WebsiteID, @@ -131,7 +133,7 @@ func (r *certRepo) Update(req *request.CertUpdate) error { } func (r *certRepo) Delete(id uint) error { - return app.Orm.Model(&biz.Cert{}).Where("id = ?", id).Delete(&biz.Cert{}).Error + return r.db.Model(&biz.Cert{}).Where("id = ?", id).Delete(&biz.Cert{}).Error } func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) { @@ -169,7 +171,7 @@ func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) { cert.CertURL = ssl.URL cert.Cert = string(ssl.ChainPEM) cert.Key = string(ssl.PrivateKey) - if err = app.Orm.Save(cert).Error; err != nil { + if err = r.db.Save(cert).Error; err != nil { return nil, err } @@ -198,7 +200,7 @@ func (r *certRepo) ObtainManual(id uint) (*acme.Certificate, error) { cert.CertURL = ssl.URL cert.Cert = string(ssl.ChainPEM) cert.Key = string(ssl.PrivateKey) - if err = app.Orm.Save(cert).Error; err != nil { + if err = r.db.Save(cert).Error; err != nil { return nil, err } @@ -222,7 +224,7 @@ func (r *certRepo) ObtainSelfSigned(id uint) error { cert.Cert = string(crt) cert.Key = string(key) - if err = app.Orm.Save(cert).Error; err != nil { + if err = r.db.Save(cert).Error; err != nil { return err } @@ -272,7 +274,7 @@ func (r *certRepo) Renew(id uint) (*acme.Certificate, error) { cert.CertURL = ssl.URL cert.Cert = string(ssl.ChainPEM) cert.Key = string(ssl.PrivateKey) - if err = app.Orm.Save(cert).Error; err != nil { + if err = r.db.Save(cert).Error; err != nil { return nil, err } @@ -319,8 +321,8 @@ func (r *certRepo) Deploy(ID, WebsiteID uint) error { return errors.New("this certificate has not been signed successfully and cannot be deployed") } - website, err := NewWebsiteRepo().Get(WebsiteID) - if err != nil { + website := new(biz.Website) + if err = r.db.Where("id", WebsiteID).First(website).Error; err != nil { return err } diff --git a/internal/data/cert_account.go b/internal/data/cert_account.go index 1a616564..83ae4bb7 100644 --- a/internal/data/cert_account.go +++ b/internal/data/cert_account.go @@ -4,39 +4,43 @@ import ( "context" "errors" "fmt" + "gorm.io/gorm" "time" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/acme" "github.com/TheTNB/panel/pkg/cert" + "github.com/go-resty/resty/v2" ) -type certAccountRepo struct{} +type certAccountRepo struct { + db *gorm.DB + user biz.UserRepo +} -func NewCertAccountRepo() biz.CertAccountRepo { - return do.MustInvoke[biz.CertAccountRepo](injector) +func NewCertAccountRepo(db *gorm.DB, user biz.UserRepo) biz.CertAccountRepo { + return &certAccountRepo{ + db: db, + user: user, + } } func (r certAccountRepo) List(page, limit uint) ([]*biz.CertAccount, int64, error) { var accounts []*biz.CertAccount var total int64 - err := app.Orm.Model(&biz.CertAccount{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&accounts).Error + err := r.db.Model(&biz.CertAccount{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&accounts).Error return accounts, total, err } func (r certAccountRepo) GetDefault(userID uint) (*biz.CertAccount, error) { - user, err := NewUserRepo().Get(userID) + user, err := r.user.Get(userID) if err != nil { return nil, err } account := new(biz.CertAccount) - if err = app.Orm.Model(&biz.CertAccount{}).Where("ca = ?", "googlecn").Where("email = ?", user.Email).First(account).Error; err == nil { + if err = r.db.Model(&biz.CertAccount{}).Where("ca = ?", "googlecn").Where("email = ?", user.Email).First(account).Error; err == nil { return account, nil } @@ -51,7 +55,7 @@ func (r certAccountRepo) GetDefault(userID uint) (*biz.CertAccount, error) { func (r certAccountRepo) Get(id uint) (*biz.CertAccount, error) { account := new(biz.CertAccount) - err := app.Orm.Model(&biz.CertAccount{}).Where("id = ?", id).First(account).Error + err := r.db.Model(&biz.CertAccount{}).Where("id = ?", id).First(account).Error return account, err } @@ -104,7 +108,7 @@ func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccoun } account.PrivateKey = string(privateKey) - if err = app.Orm.Create(account).Error; err != nil { + if err = r.db.Create(account).Error; err != nil { return nil, err } @@ -163,11 +167,11 @@ func (r certAccountRepo) Update(req *request.CertAccountUpdate) error { } account.PrivateKey = string(privateKey) - return app.Orm.Save(account).Error + return r.db.Save(account).Error } func (r certAccountRepo) Delete(id uint) error { - return app.Orm.Model(&biz.CertAccount{}).Where("id = ?", id).Delete(&biz.CertAccount{}).Error + return r.db.Model(&biz.CertAccount{}).Where("id = ?", id).Delete(&biz.CertAccount{}).Error } // getGoogleEAB 获取 Google EAB diff --git a/internal/data/cert_dns.go b/internal/data/cert_dns.go index 2bcf8a52..60f11ab2 100644 --- a/internal/data/cert_dns.go +++ b/internal/data/cert_dns.go @@ -1,29 +1,32 @@ package data import ( - "github.com/samber/do/v2" + "gorm.io/gorm" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" ) -type certDNSRepo struct{} +type certDNSRepo struct { + db *gorm.DB +} -func NewCertDNSRepo() biz.CertDNSRepo { - return do.MustInvoke[biz.CertDNSRepo](injector) +func NewCertDNSRepo(db *gorm.DB) biz.CertDNSRepo { + return &certDNSRepo{ + db: db, + } } func (r certDNSRepo) List(page, limit uint) ([]*biz.CertDNS, int64, error) { var certDNS []*biz.CertDNS var total int64 - err := app.Orm.Model(&biz.CertDNS{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certDNS).Error + err := r.db.Model(&biz.CertDNS{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certDNS).Error return certDNS, total, err } func (r certDNSRepo) Get(id uint) (*biz.CertDNS, error) { certDNS := new(biz.CertDNS) - err := app.Orm.Model(&biz.CertDNS{}).Where("id = ?", id).First(certDNS).Error + err := r.db.Model(&biz.CertDNS{}).Where("id = ?", id).First(certDNS).Error return certDNS, err } @@ -34,7 +37,7 @@ func (r certDNSRepo) Create(req *request.CertDNSCreate) (*biz.CertDNS, error) { Data: req.Data, } - if err := app.Orm.Create(certDNS).Error; err != nil { + if err := r.db.Create(certDNS).Error; err != nil { return nil, err } @@ -51,9 +54,9 @@ func (r certDNSRepo) Update(req *request.CertDNSUpdate) error { cert.Type = req.Type cert.Data = req.Data - return app.Orm.Save(cert).Error + return r.db.Save(cert).Error } func (r certDNSRepo) Delete(id uint) error { - return app.Orm.Model(&biz.CertDNS{}).Where("id = ?", id).Delete(&biz.CertDNS{}).Error + return r.db.Model(&biz.CertDNS{}).Where("id = ?", id).Delete(&biz.CertDNS{}).Error } diff --git a/internal/data/container.go b/internal/data/container.go index 8a25e232..320a1187 100644 --- a/internal/data/container.go +++ b/internal/data/container.go @@ -6,14 +6,12 @@ import ( "strings" "time" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/types" "github.com/TheTNB/panel/pkg/types/docker/container" + "github.com/go-resty/resty/v2" ) type containerRepo struct { @@ -21,7 +19,9 @@ type containerRepo struct { } func NewContainerRepo() biz.ContainerRepo { - return do.MustInvoke[biz.ContainerRepo](injector) + return &containerRepo{ + client: getDockerClient("/var/run/docker.sock"), + } } // ListAll 列出所有容器 diff --git a/internal/data/container_image.go b/internal/data/container_image.go index 1a1010ac..959eea47 100644 --- a/internal/data/container_image.go +++ b/internal/data/container_image.go @@ -6,15 +6,13 @@ import ( "strings" "time" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/pkg/types" "github.com/TheTNB/panel/pkg/types/docker/image" + "github.com/go-resty/resty/v2" ) type containerImageRepo struct { @@ -22,7 +20,9 @@ type containerImageRepo struct { } func NewContainerImageRepo() biz.ContainerImageRepo { - return do.MustInvoke[biz.ContainerImageRepo](injector) + return &containerImageRepo{ + client: getDockerClient("/var/run/docker.sock"), + } } // List 列出镜像 diff --git a/internal/data/container_network.go b/internal/data/container_network.go index 195eaf1e..f6f7057d 100644 --- a/internal/data/container_network.go +++ b/internal/data/container_network.go @@ -6,14 +6,12 @@ import ( "strings" "time" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/types" "github.com/TheTNB/panel/pkg/types/docker/network" + "github.com/go-resty/resty/v2" ) type containerNetworkRepo struct { @@ -21,7 +19,9 @@ type containerNetworkRepo struct { } func NewContainerNetworkRepo() biz.ContainerNetworkRepo { - return do.MustInvoke[biz.ContainerNetworkRepo](injector) + return &containerNetworkRepo{ + client: getDockerClient("/var/run/docker.sock"), + } } // List 列出网络 diff --git a/internal/data/container_volume.go b/internal/data/container_volume.go index d2615a95..7084d0da 100644 --- a/internal/data/container_volume.go +++ b/internal/data/container_volume.go @@ -6,15 +6,13 @@ import ( "strings" "time" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/pkg/types" "github.com/TheTNB/panel/pkg/types/docker/volume" + "github.com/go-resty/resty/v2" ) type containerVolumeRepo struct { @@ -22,7 +20,9 @@ type containerVolumeRepo struct { } func NewContainerVolumeRepo() biz.ContainerVolumeRepo { - return do.MustInvoke[biz.ContainerVolumeRepo](injector) + return &containerVolumeRepo{ + client: getDockerClient("/var/run/docker.sock"), + } } // List 列出存储卷 diff --git a/internal/data/cron.go b/internal/data/cron.go index 62237411..c54068ae 100644 --- a/internal/data/cron.go +++ b/internal/data/cron.go @@ -3,13 +3,11 @@ package data import ( "errors" "fmt" + "gorm.io/gorm" "path/filepath" "strconv" "time" - "github.com/go-rat/utils/str" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" @@ -17,17 +15,22 @@ import ( "github.com/TheTNB/panel/pkg/os" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/systemctl" + "github.com/go-rat/utils/str" ) -type cronRepo struct{} +type cronRepo struct { + db *gorm.DB +} -func NewCronRepo() biz.CronRepo { - return do.MustInvoke[biz.CronRepo](injector) +func NewCronRepo(db *gorm.DB) biz.CronRepo { + return &cronRepo{ + db: db, + } } func (r *cronRepo) Count() (int64, error) { var count int64 - if err := app.Orm.Model(&biz.Cron{}).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.Cron{}).Count(&count).Error; err != nil { return 0, err } @@ -37,13 +40,13 @@ func (r *cronRepo) Count() (int64, error) { func (r *cronRepo) List(page, limit uint) ([]*biz.Cron, int64, error) { var cron []*biz.Cron var total int64 - err := app.Orm.Model(&biz.Cron{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&cron).Error + err := r.db.Model(&biz.Cron{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&cron).Error return cron, total, err } func (r *cronRepo) Get(id uint) (*biz.Cron, error) { cron := new(biz.Cron) - if err := app.Orm.Where("id = ?", id).First(cron).Error; err != nil { + if err := r.db.Where("id = ?", id).First(cron).Error; err != nil { return nil, err } @@ -113,7 +116,7 @@ panel-cli cutoff clear -t website -f %s -s %d -p %s cron.Shell = shellDir + shellFile + ".sh" cron.Log = shellLogDir + shellFile + ".log" - if err := app.Orm.Create(cron).Error; err != nil { + if err := r.db.Create(cron).Error; err != nil { return err } if err := r.addToSystem(cron); err != nil { @@ -135,7 +138,7 @@ func (r *cronRepo) Update(req *request.CronUpdate) error { cron.Time = req.Time cron.Name = req.Name - if err = app.Orm.Save(cron).Error; err != nil { + if err = r.db.Save(cron).Error; err != nil { return err } @@ -171,7 +174,7 @@ func (r *cronRepo) Delete(id uint) error { return err } - return app.Orm.Delete(cron).Error + return r.db.Delete(cron).Error } func (r *cronRepo) Status(id uint, status bool) error { @@ -189,7 +192,7 @@ func (r *cronRepo) Status(id uint, status bool) error { cron.Status = status - return app.Orm.Save(cron).Error + return r.db.Save(cron).Error } // addToSystem 添加到系统 diff --git a/internal/data/data.go b/internal/data/data.go new file mode 100644 index 00000000..03895ed0 --- /dev/null +++ b/internal/data/data.go @@ -0,0 +1,28 @@ +package data + +import "github.com/google/wire" + +// ProviderSet is data providers. +var ProviderSet = wire.NewSet( + NewAppRepo, + NewBackupRepo, + NewCacheRepo, + NewCertRepo, + NewCertAccountRepo, + NewCertDNSRepo, + NewContainerRepo, + NewContainerImageRepo, + NewContainerNetworkRepo, + NewContainerVolumeRepo, + NewCronRepo, + NewDatabaseRepo, + NewDatabaseServerRepo, + NewDatabaseUserRepo, + NewMonitorRepo, + NewSafeRepo, + NewSettingRepo, + NewSSHRepo, + NewTaskRepo, + NewUserRepo, + NewWebsiteRepo, +) diff --git a/internal/data/database.go b/internal/data/database.go index 74ba8a11..8161ada2 100644 --- a/internal/data/database.go +++ b/internal/data/database.go @@ -5,18 +5,19 @@ import ( "fmt" "slices" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/db" ) -type databaseRepo struct{} +type databaseRepo struct { + server biz.DatabaseServerRepo + user biz.DatabaseUserRepo +} -func NewDatabaseRepo() biz.DatabaseRepo { - return do.MustInvoke[biz.DatabaseRepo](injector) +func NewDatabaseRepo(server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo { + return &databaseRepo{server: server, user: user} } func (r databaseRepo) List(page, limit uint) ([]*biz.Database, int64, error) { @@ -68,7 +69,7 @@ func (r databaseRepo) List(page, limit uint) ([]*biz.Database, int64, error) { } func (r databaseRepo) Create(req *request.DatabaseCreate) error { - server, err := NewDatabaseServerRepo().Get(req.ServerID) + server, err := r.server.Get(req.ServerID) if err != nil { return err } @@ -81,7 +82,7 @@ func (r databaseRepo) Create(req *request.DatabaseCreate) error { } defer mysql.Close() if req.CreateUser { - if err = NewDatabaseUserRepo().Create(&request.DatabaseUserCreate{ + if err = r.user.Create(&request.DatabaseUserCreate{ ServerID: req.ServerID, Username: req.Username, Password: req.Password, @@ -105,7 +106,7 @@ func (r databaseRepo) Create(req *request.DatabaseCreate) error { } defer postgres.Close() if req.CreateUser { - if err = NewDatabaseUserRepo().Create(&request.DatabaseUserCreate{ + if err = r.user.Create(&request.DatabaseUserCreate{ ServerID: req.ServerID, Username: req.Username, Password: req.Password, @@ -131,7 +132,7 @@ func (r databaseRepo) Create(req *request.DatabaseCreate) error { } func (r databaseRepo) Delete(serverID uint, name string) error { - server, err := NewDatabaseServerRepo().Get(serverID) + server, err := r.server.Get(serverID) if err != nil { return err } @@ -157,7 +158,7 @@ func (r databaseRepo) Delete(serverID uint, name string) error { } func (r databaseRepo) Comment(req *request.DatabaseComment) error { - server, err := NewDatabaseServerRepo().Get(req.ServerID) + server, err := r.server.Get(req.ServerID) if err != nil { return err } diff --git a/internal/data/database_server.go b/internal/data/database_server.go index cd6f563d..c183bb66 100644 --- a/internal/data/database_server.go +++ b/internal/data/database_server.go @@ -2,26 +2,31 @@ package data import ( "fmt" + "github.com/TheTNB/panel/internal/app" + "gorm.io/gorm" "log/slog" "slices" - "github.com/samber/do/v2" - - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/db" ) -type databaseServerRepo struct{} +type databaseServerRepo struct { + db *gorm.DB + log *slog.Logger +} -func NewDatabaseServerRepo() biz.DatabaseServerRepo { - return do.MustInvoke[biz.DatabaseServerRepo](injector) +func NewDatabaseServerRepo(db *gorm.DB, log *slog.Logger) biz.DatabaseServerRepo { + return &databaseServerRepo{ + db: db, + log: log, + } } func (r databaseServerRepo) Count() (int64, error) { var count int64 - if err := app.Orm.Model(&biz.DatabaseServer{}).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.DatabaseServer{}).Count(&count).Error; err != nil { return 0, err } @@ -31,7 +36,7 @@ func (r databaseServerRepo) Count() (int64, error) { func (r databaseServerRepo) List(page, limit uint) ([]*biz.DatabaseServer, int64, error) { var databaseServer []*biz.DatabaseServer var total int64 - err := app.Orm.Model(&biz.DatabaseServer{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&databaseServer).Error + err := r.db.Model(&biz.DatabaseServer{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&databaseServer).Error for server := range slices.Values(databaseServer) { r.checkServer(server) @@ -42,7 +47,7 @@ func (r databaseServerRepo) List(page, limit uint) ([]*biz.DatabaseServer, int64 func (r databaseServerRepo) Get(id uint) (*biz.DatabaseServer, error) { databaseServer := new(biz.DatabaseServer) - if err := app.Orm.Where("id = ?", id).First(databaseServer).Error; err != nil { + if err := r.db.Where("id = ?", id).First(databaseServer).Error; err != nil { return nil, err } @@ -53,7 +58,7 @@ func (r databaseServerRepo) Get(id uint) (*biz.DatabaseServer, error) { func (r databaseServerRepo) GetByName(name string) (*biz.DatabaseServer, error) { databaseServer := new(biz.DatabaseServer) - if err := app.Orm.Where("name = ?", name).First(databaseServer).Error; err != nil { + if err := r.db.Where("name = ?", name).First(databaseServer).Error; err != nil { return nil, err } @@ -77,7 +82,7 @@ func (r databaseServerRepo) Create(req *request.DatabaseServerCreate) error { return fmt.Errorf("check server connection failed") } - return app.Orm.Create(databaseServer).Error + return r.db.Create(databaseServer).Error } func (r databaseServerRepo) Update(req *request.DatabaseServerUpdate) error { @@ -97,20 +102,24 @@ func (r databaseServerRepo) Update(req *request.DatabaseServerUpdate) error { return fmt.Errorf("check server connection failed") } - return app.Orm.Save(server).Error + return r.db.Save(server).Error } func (r databaseServerRepo) UpdateRemark(req *request.DatabaseServerUpdateRemark) error { - return app.Orm.Model(&biz.DatabaseServer{}).Where("id = ?", req.ID).Update("remark", req.Remark).Error + return r.db.Model(&biz.DatabaseServer{}).Where("id = ?", req.ID).Update("remark", req.Remark).Error } func (r databaseServerRepo) Delete(id uint) error { - // 删除服务器下的所有用户 - if err := NewDatabaseUserRepo().DeleteByServerID(id); err != nil { + if err := r.ClearUsers(id); err != nil { return err } - return app.Orm.Where("id = ?", id).Delete(&biz.DatabaseServer{}).Error + return r.db.Where("id = ?", id).Delete(&biz.DatabaseServer{}).Error +} + +// ClearUsers 删除指定服务器的所有用户,只是删除面板记录,不会实际删除 +func (r databaseServerRepo) ClearUsers(serverID uint) error { + return app.Orm.Where("server_id = ?", serverID).Delete(&biz.DatabaseUser{}).Error } func (r databaseServerRepo) Sync(id uint) error { @@ -120,7 +129,7 @@ func (r databaseServerRepo) Sync(id uint) error { } users := make([]*biz.DatabaseUser, 0) - if err = app.Orm.Where("server_id = ?", id).Find(&users).Error; err != nil { + if err = r.db.Where("server_id = ?", id).Find(&users).Error; err != nil { return err } @@ -145,8 +154,8 @@ func (r databaseServerRepo) Sync(id uint) error { Host: user.Host, Remark: fmt.Sprintf("sync from server %s", server.Name), } - if err = app.Orm.Create(newUser).Error; err != nil { - app.Logger.Warn("sync database user failed", slog.Any("err", err)) + if err = r.db.Create(newUser).Error; err != nil { + r.log.Warn("sync database user failed", slog.Any("err", err)) } } } @@ -169,7 +178,7 @@ func (r databaseServerRepo) Sync(id uint) error { Username: user.Role, Remark: fmt.Sprintf("sync from server %s", server.Name), } - app.Orm.Create(newUser) + r.db.Create(newUser) } } } diff --git a/internal/data/database_user.go b/internal/data/database_user.go index 333f1d33..93417eff 100644 --- a/internal/data/database_user.go +++ b/internal/data/database_user.go @@ -4,18 +4,18 @@ import ( "fmt" "slices" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/db" ) -type databaseUserRepo struct{} +type databaseUserRepo struct { + server biz.DatabaseServerRepo +} -func NewDatabaseUserRepo() biz.DatabaseUserRepo { - return do.MustInvoke[biz.DatabaseUserRepo](injector) +func NewDatabaseUserRepo(server biz.DatabaseServerRepo) biz.DatabaseUserRepo { + return &databaseUserRepo{server: server} } func (r databaseUserRepo) Count() (int64, error) { @@ -51,7 +51,7 @@ func (r databaseUserRepo) Get(id uint) (*biz.DatabaseUser, error) { } func (r databaseUserRepo) Create(req *request.DatabaseUserCreate) error { - server, err := NewDatabaseServerRepo().Get(req.ServerID) + server, err := r.server.Get(req.ServerID) if err != nil { return err } @@ -113,7 +113,7 @@ func (r databaseUserRepo) Update(req *request.DatabaseUserUpdate) error { return err } - server, err := NewDatabaseServerRepo().Get(user.ServerID) + server, err := r.server.Get(user.ServerID) if err != nil { return err } @@ -176,7 +176,7 @@ func (r databaseUserRepo) Delete(id uint) error { return err } - server, err := NewDatabaseServerRepo().Get(user.ServerID) + server, err := r.server.Get(user.ServerID) if err != nil { return err } @@ -202,7 +202,7 @@ func (r databaseUserRepo) Delete(id uint) error { } func (r databaseUserRepo) DeleteByNames(serverID uint, names []string) error { - server, err := NewDatabaseServerRepo().Get(serverID) + server, err := r.server.Get(serverID) if err != nil { return err } @@ -242,13 +242,8 @@ func (r databaseUserRepo) DeleteByNames(serverID uint, names []string) error { return app.Orm.Where("server_id = ? AND username IN ?", serverID, names).Delete(&biz.DatabaseUser{}).Error } -// DeleteByServerID 删除指定服务器的所有用户,只是删除面板记录,不会实际删除 -func (r databaseUserRepo) DeleteByServerID(serverID uint) error { - return app.Orm.Where("server_id = ?", serverID).Delete(&biz.DatabaseUser{}).Error -} - func (r databaseUserRepo) fillUser(user *biz.DatabaseUser) { - server, err := NewDatabaseServerRepo().Get(user.ServerID) + server, err := r.server.Get(user.ServerID) if err == nil { switch server.Type { case biz.DatabaseTypeMysql: diff --git a/internal/data/helper.go b/internal/data/helper.go new file mode 100644 index 00000000..fb9f4fb3 --- /dev/null +++ b/internal/data/helper.go @@ -0,0 +1,23 @@ +package data + +import ( + "context" + "net" + "net/http" + "time" + + "github.com/go-resty/resty/v2" +) + +func getDockerClient(sock string) *resty.Client { + client := resty.New() + client.SetTimeout(1 * time.Minute) + client.SetRetryCount(2) + client.SetTransport(&http.Transport{ + DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) { + return (&net.Dialer{}).DialContext(ctx, "unix", sock) + }, + }) + client.SetBaseURL("http://d/v1.40") + return client +} diff --git a/internal/data/init.go b/internal/data/init.go deleted file mode 100644 index ac56e759..00000000 --- a/internal/data/init.go +++ /dev/null @@ -1,120 +0,0 @@ -package data - -import ( - "context" - "net" - "net/http" - "time" - - "github.com/go-rat/utils/hash" - "github.com/go-resty/resty/v2" - "github.com/samber/do/v2" - - "github.com/TheTNB/panel/internal/app" - "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/pkg/api" - "github.com/TheTNB/panel/pkg/os" -) - -var injector = do.New() - -func init() { - do.Provide(injector, func(i do.Injector) (biz.AppRepo, error) { - return &appRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.BackupRepo, error) { - return &backupRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.CacheRepo, error) { - return &cacheRepo{ - api: api.NewAPI(app.Version), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.CertRepo, error) { - return &certRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.CertAccountRepo, error) { - return &certAccountRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.CertDNSRepo, error) { - return &certDNSRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.ContainerRepo, error) { - return &containerRepo{ - client: getDockerClient("/var/run/docker.sock"), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.ContainerImageRepo, error) { - return &containerImageRepo{ - client: getDockerClient("/var/run/docker.sock"), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.ContainerNetworkRepo, error) { - return &containerNetworkRepo{ - client: getDockerClient("/var/run/docker.sock"), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.ContainerVolumeRepo, error) { - return &containerVolumeRepo{ - client: getDockerClient("/var/run/docker.sock"), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.CronRepo, error) { - return &cronRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.DatabaseServerRepo, error) { - return &databaseServerRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.DatabaseUserRepo, error) { - return &databaseUserRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.DatabaseRepo, error) { - return &databaseRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.MonitorRepo, error) { - return &monitorRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.SafeRepo, error) { - var ssh string - if os.IsRHEL() { - ssh = "sshd" - } else { - ssh = "ssh" - } - return &safeRepo{ - ssh: ssh, - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.SettingRepo, error) { - return &settingRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.SSHRepo, error) { - return &sshRepo{}, nil - }) - do.Provide(injector, func(i do.Injector) (biz.TaskRepo, error) { - task := &taskRepo{} - task.DispatchWaiting() - return task, nil - }) - do.Provide(injector, func(i do.Injector) (biz.UserRepo, error) { - return &userRepo{ - hasher: hash.NewArgon2id(), - }, nil - }) - do.Provide(injector, func(i do.Injector) (biz.WebsiteRepo, error) { - return &websiteRepo{}, nil - }) -} - -func getDockerClient(sock string) *resty.Client { - client := resty.New() - client.SetTimeout(1 * time.Minute) - client.SetRetryCount(2) - client.SetTransport(&http.Transport{ - DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) { - return (&net.Dialer{}).DialContext(ctx, "unix", sock) - }, - }) - client.SetBaseURL("http://d/v1.40") - return client -} diff --git a/internal/data/monitor.go b/internal/data/monitor.go index edf31904..1410c3a2 100644 --- a/internal/data/monitor.go +++ b/internal/data/monitor.go @@ -2,9 +2,9 @@ package data import ( "errors" + "gorm.io/gorm" "time" - "github.com/samber/do/v2" "github.com/spf13/cast" "github.com/TheTNB/panel/internal/app" @@ -12,19 +12,24 @@ import ( "github.com/TheTNB/panel/internal/http/request" ) -type monitorRepo struct{} +type monitorRepo struct { + db *gorm.DB + setting biz.SettingRepo +} -func NewMonitorRepo() biz.MonitorRepo { - return do.MustInvoke[biz.MonitorRepo](injector) +func NewMonitorRepo(db *gorm.DB, setting biz.SettingRepo) biz.MonitorRepo { + return &monitorRepo{ + db: db, + setting: setting, + } } func (r monitorRepo) GetSetting() (*request.MonitorSetting, error) { - repo := NewSettingRepo() - monitor, err := repo.Get(biz.SettingKeyMonitor) + monitor, err := r.setting.Get(biz.SettingKeyMonitor) if err != nil { return nil, err } - monitorDays, err := repo.Get(biz.SettingKeyMonitorDays) + monitorDays, err := r.setting.Get(biz.SettingKeyMonitorDays) if err != nil { return nil, err } @@ -37,11 +42,10 @@ func (r monitorRepo) GetSetting() (*request.MonitorSetting, error) { } func (r monitorRepo) UpdateSetting(setting *request.MonitorSetting) error { - repo := NewSettingRepo() - if err := repo.Set(biz.SettingKeyMonitor, cast.ToString(setting.Enabled)); err != nil { + if err := r.setting.Set(biz.SettingKeyMonitor, cast.ToString(setting.Enabled)); err != nil { return err } - if err := repo.Set(biz.SettingKeyMonitorDays, cast.ToString(setting.Days)); err != nil { + if err := r.setting.Set(biz.SettingKeyMonitorDays, cast.ToString(setting.Days)); err != nil { return err } diff --git a/internal/data/safe.go b/internal/data/safe.go index 3c4ef66b..ba4f5a21 100644 --- a/internal/data/safe.go +++ b/internal/data/safe.go @@ -2,9 +2,9 @@ package data import ( "fmt" + "github.com/TheTNB/panel/pkg/os" "strings" - "github.com/samber/do/v2" "github.com/spf13/cast" "github.com/TheTNB/panel/internal/biz" @@ -18,7 +18,15 @@ type safeRepo struct { } func NewSafeRepo() biz.SafeRepo { - return do.MustInvoke[biz.SafeRepo](injector) + var ssh string + if os.IsRHEL() { + ssh = "sshd" + } else { + ssh = "ssh" + } + return &safeRepo{ + ssh: ssh, + } } func (r *safeRepo) GetSSH() (uint, bool, error) { diff --git a/internal/data/setting.go b/internal/data/setting.go index 36fa56e0..533999ef 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -4,14 +4,12 @@ import ( "context" "errors" "fmt" - "path/filepath" - "slices" - "github.com/go-rat/utils/hash" - "github.com/samber/do/v2" + "github.com/knadh/koanf/v2" "github.com/spf13/cast" "gopkg.in/yaml.v3" "gorm.io/gorm" + "path/filepath" "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" @@ -20,20 +18,26 @@ import ( "github.com/TheTNB/panel/pkg/firewall" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/os" - "github.com/TheTNB/panel/pkg/shell" - "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/pkg/types" ) -type settingRepo struct{} +type settingRepo struct { + db *gorm.DB + conf *koanf.Koanf + task biz.TaskRepo +} -func NewSettingRepo() biz.SettingRepo { - return do.MustInvoke[biz.SettingRepo](injector) +func NewSettingRepo(db *gorm.DB, conf *koanf.Koanf, task biz.TaskRepo) biz.SettingRepo { + return &settingRepo{ + db: db, + conf: conf, + task: task, + } } func (r *settingRepo) Get(key biz.SettingKey, defaultValue ...string) (string, error) { setting := new(biz.Setting) - if err := app.Orm.Where("key = ?", key).First(setting).Error; err != nil { + if err := r.db.Where("key = ?", key).First(setting).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return "", err } @@ -48,7 +52,7 @@ func (r *settingRepo) Get(key biz.SettingKey, defaultValue ...string) (string, e func (r *settingRepo) GetBool(key biz.SettingKey, defaultValue ...bool) (bool, error) { setting := new(biz.Setting) - if err := app.Orm.Where("key = ?", key).First(setting).Error; err != nil { + if err := r.db.Where("key = ?", key).First(setting).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return false, err } @@ -63,7 +67,7 @@ func (r *settingRepo) GetBool(key biz.SettingKey, defaultValue ...bool) (bool, e func (r *settingRepo) Set(key biz.SettingKey, value string) error { setting := new(biz.Setting) - if err := app.Orm.Where("key = ?", key).First(setting).Error; err != nil { + if err := r.db.Where("key = ?", key).First(setting).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return err } @@ -71,12 +75,12 @@ func (r *settingRepo) Set(key biz.SettingKey, value string) error { setting.Key = key setting.Value = value - return app.Orm.Save(setting).Error + return r.db.Save(setting).Error } func (r *settingRepo) Delete(key biz.SettingKey) error { setting := new(biz.Setting) - if err := app.Orm.Where("key = ?", key).Delete(setting).Error; err != nil { + if err := r.db.Where("key = ?", key).Delete(setting).Error; err != nil { return err } @@ -103,7 +107,7 @@ func (r *settingRepo) GetPanelSetting(ctx context.Context) (*request.PanelSettin userID := cast.ToUint(ctx.Value("user_id")) user := new(biz.User) - if err := app.Orm.Where("id = ?", userID).First(user).Error; err != nil { + if err := r.db.Where("id = ?", userID).First(user).Error; err != nil { return nil, err } @@ -118,15 +122,15 @@ func (r *settingRepo) GetPanelSetting(ctx context.Context) (*request.PanelSettin return &request.PanelSetting{ Name: name, - Locale: app.Conf.String("app.locale"), - Entrance: app.Conf.String("http.entrance"), + Locale: r.conf.String("app.locale"), + Entrance: r.conf.String("http.entrance"), OfflineMode: cast.ToBool(offlineMode), WebsitePath: websitePath, BackupPath: backupPath, Username: user.Username, Email: user.Email, - Port: uint(app.Conf.Int("http.port")), - HTTPS: app.Conf.Bool("http.tls"), + Port: uint(r.conf.Int("http.port")), + HTTPS: r.conf.Bool("http.tls"), Cert: crt, Key: key, }, nil @@ -149,7 +153,7 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P // 用户 user := new(biz.User) userID := cast.ToUint(ctx.Value("user_id")) - if err := app.Orm.Where("id = ?", userID).First(user).Error; err != nil { + if err := r.db.Where("id = ?", userID).First(user).Error; err != nil { return false, err } @@ -162,18 +166,17 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P } user.Password = value } - if err := app.Orm.Save(user).Error; err != nil { + if err := r.db.Save(user).Error; err != nil { return false, err } // 下面是需要需要重启的设置 - task := NewTaskRepo() // 面板HTTPS restartFlag := false oldCert, _ := io.Read(filepath.Join(app.Root, "panel/storage/cert.pem")) oldKey, _ := io.Read(filepath.Join(app.Root, "panel/storage/cert.key")) if oldCert != setting.Cert || oldKey != setting.Key { - if task.HasRunningTask() { + if r.task.HasRunningTask() { return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试") } restartFlag = true @@ -216,8 +219,8 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P fw := firewall.NewFirewall() err = fw.Port(firewall.FireInfo{ Type: firewall.TypeNormal, - PortStart: uint(config.HTTP.Port), - PortEnd: uint(config.HTTP.Port), + PortStart: config.HTTP.Port, + PortEnd: config.HTTP.Port, Direction: firewall.DirectionIn, Strategy: firewall.StrategyAccept, }, firewall.OperationAdd) @@ -230,7 +233,7 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P return false, err } if raw != string(encoded) { - if task.HasRunningTask() { + if r.task.HasRunningTask() { return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试") } restartFlag = true @@ -241,267 +244,3 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P return restartFlag, nil } - -func (r *settingRepo) UpdatePanel(version, url, checksum string) error { - // 预先优化数据库 - if err := app.Orm.Exec("VACUUM").Error; err != nil { - return err - } - if err := app.Orm.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil { - return err - } - - name := filepath.Base(url) - if app.IsCli { - fmt.Printf("|-目标版本:%s\n", version) - fmt.Printf("|-下载链接:%s\n", url) - fmt.Printf("|-文件名:%s\n", name) - } - - if app.IsCli { - fmt.Println("|-正在下载...") - } - if _, err := shell.Execf("wget -T 120 -t 3 -O /tmp/%s %s", name, url); err != nil { - return fmt.Errorf("下载失败:%w", err) - } - if _, err := shell.Execf("wget -T 20 -t 3 -O /tmp/%s %s", name+".sha256", checksum); err != nil { - return fmt.Errorf("下载失败:%w", err) - } - if !io.Exists(filepath.Join("/tmp", name)) || !io.Exists(filepath.Join("/tmp", name+".sha256")) { - return errors.New("下载文件检查失败") - } - - if app.IsCli { - fmt.Println("|-校验下载文件...") - } - if check, err := shell.Execf("cd /tmp && sha256sum -c %s --ignore-missing", name+".sha256"); check != name+": OK" || err != nil { - return errors.New("下载文件校验失败") - } - if err := io.Remove(filepath.Join("/tmp", name+".sha256")); err != nil { - if app.IsCli { - fmt.Println("|-清理校验文件失败:", err) - } - return fmt.Errorf("清理校验文件失败:%w", err) - } - - if app.IsCli { - fmt.Println("|-前置检查...") - } - if io.Exists("/tmp/panel-storage.zip") { - return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败所致,请运行 panel-cli fix 修复后重试") - } - - if app.IsCli { - fmt.Println("|-备份面板数据...") - } - // 备份面板 - backup := NewBackupRepo() - if err := backup.Create(biz.BackupTypePanel, ""); err != nil { - if app.IsCli { - fmt.Println("|-备份面板失败:", err) - } - return fmt.Errorf("备份面板失败:%w", err) - } - if err := io.Compress(filepath.Join(app.Root, "panel/storage"), nil, "/tmp/panel-storage.zip"); err != nil { - if app.IsCli { - fmt.Println("|-备份面板数据失败:", err) - } - return fmt.Errorf("备份面板数据失败:%w", err) - } - if !io.Exists("/tmp/panel-storage.zip") { - return errors.New("已备份面板数据检查失败") - } - - if app.IsCli { - fmt.Println("|-清理旧版本...") - } - if _, err := shell.Execf("rm -rf %s/panel/*", app.Root); err != nil { - return fmt.Errorf("清理旧版本失败:%w", err) - } - - if app.IsCli { - fmt.Println("|-解压新版本...") - } - if err := io.UnCompress(filepath.Join("/tmp", name), filepath.Join(app.Root, "panel")); err != nil { - return fmt.Errorf("解压失败:%w", err) - } - if !io.Exists(filepath.Join(app.Root, "panel", "web")) { - return errors.New("解压失败,缺失文件") - } - - if app.IsCli { - fmt.Println("|-恢复面板数据...") - } - if err := io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel", "storage")); err != nil { - return fmt.Errorf("恢复面板数据失败:%w", err) - } - if !io.Exists(filepath.Join(app.Root, "panel/storage/app.db")) { - return errors.New("恢复面板数据失败") - } - - if app.IsCli { - fmt.Println("|-运行更新后脚本...") - } - if _, err := shell.Execf("curl -fsLm 10 https://dl.cdn.haozi.net/panel/auto_update.sh | bash"); err != nil { - return fmt.Errorf("运行面板更新后脚本失败:%w", err) - } - if _, err := shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil { - return fmt.Errorf("下载面板服务文件失败:%w", err) - } - if _, err := shell.Execf("panel-cli setting write version %s", version); err != nil { - return fmt.Errorf("写入面板版本号失败:%w", err) - } - if err := io.Mv(filepath.Join(app.Root, "panel/cli"), "/usr/local/sbin/panel-cli"); err != nil { - return fmt.Errorf("移动面板命令行工具失败:%w", err) - } - - if app.IsCli { - fmt.Println("|-设置关键文件权限...") - } - _ = io.Chmod("/usr/local/sbin/panel-cli", 0700) - _ = io.Chmod("/etc/systemd/system/panel.service", 0700) - _ = io.Chmod(filepath.Join(app.Root, "panel"), 0700) - - if app.IsCli { - fmt.Println("|-更新完成") - } - - _, _ = shell.Execf("systemctl daemon-reload") - _ = io.Remove("/tmp/panel-storage.zip") - _ = io.Remove(filepath.Join(app.Root, "panel/config.example.yml")) - tools.RestartPanel() - - return nil -} - -func (r *settingRepo) FixPanel() error { - if app.IsCli { - fmt.Println("|-开始修复面板...") - } - - // 检查关键文件是否正常 - flag := false - if !io.Exists(filepath.Join(app.Root, "panel", "web")) { - flag = true - } - if !io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) { - flag = true - } - if io.Exists("/tmp/panel-storage.zip") { - flag = true - } - if !flag { - return fmt.Errorf("文件正常无需修复,请运行 panel-cli update 更新面板") - } - - // 再次确认是否需要修复 - if io.Exists("/tmp/panel-storage.zip") { - // 文件齐全情况下只移除临时文件 - if io.Exists(filepath.Join(app.Root, "panel", "web")) && - io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) && - io.Exists("/usr/local/etc/panel/config.yml") { - if err := io.Remove("/tmp/panel-storage.zip"); err != nil { - return fmt.Errorf("清理临时文件失败:%w", err) - } - if app.IsCli { - fmt.Println("|-已清理临时文件,请运行 panel-cli update 更新面板") - } - return nil - } - } - - // 从备份目录中找最新的备份文件 - backup := NewBackupRepo() - list, err := backup.List(biz.BackupTypePanel) - if err != nil { - return err - } - slices.SortFunc(list, func(a *types.BackupFile, b *types.BackupFile) int { - return int(b.Time.Unix() - a.Time.Unix()) - }) - if len(list) == 0 { - return fmt.Errorf("未找到备份文件,无法自动修复") - } - latest := list[0] - if app.IsCli { - fmt.Printf("|-使用备份文件:%s\n", latest.Name) - } - - // 解压备份文件 - if app.IsCli { - fmt.Println("|-解压备份文件...") - } - if err = io.Remove("/tmp/panel-fix"); err != nil { - return fmt.Errorf("清理临时目录失败:%w", err) - } - if err = io.UnCompress(latest.Path, "/tmp/panel-fix"); err != nil { - return fmt.Errorf("解压备份文件失败:%w", err) - } - - // 移动文件到对应位置 - if app.IsCli { - fmt.Println("|-移动备份文件...") - } - if io.Exists(filepath.Join("/tmp/panel-fix", "panel")) && io.IsDir(filepath.Join("/tmp/panel-fix", "panel")) { - if err = io.Remove(filepath.Join(app.Root, "panel")); err != nil { - return fmt.Errorf("删除目录失败:%w", err) - } - if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel"), filepath.Join(app.Root)); err != nil { - return fmt.Errorf("移动目录失败:%w", err) - } - } - if io.Exists(filepath.Join("/tmp/panel-fix", "config.yml")) { - if err = io.Mv(filepath.Join("/tmp/panel-fix", "config.yml"), "/usr/local/etc/panel/config.yml"); err != nil { - return fmt.Errorf("移动文件失败:%w", err) - } - } - if io.Exists(filepath.Join("/tmp/panel-fix", "panel-cli")) { - if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel-cli"), "/usr/local/sbin/panel-cli"); err != nil { - return fmt.Errorf("移动文件失败:%w", err) - } - } - - // tmp 目录下如果有 storage 备份,则解压回去 - if app.IsCli { - fmt.Println("|-恢复面板数据...") - } - if io.Exists("/tmp/panel-storage.zip") { - if err = io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel")); err != nil { - return fmt.Errorf("恢复面板数据失败:%w", err) - } - if err = io.Remove("/tmp/panel-storage.zip"); err != nil { - return fmt.Errorf("清理临时文件失败:%w", err) - } - } - - // 下载服务文件 - if !io.Exists("/etc/systemd/system/panel.service") { - if _, err = shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil { - return err - } - } - - // 处理权限 - if app.IsCli { - fmt.Println("|-设置关键文件权限...") - } - if err = io.Chmod("/usr/local/etc/panel/config.yml", 0600); err != nil { - return err - } - if err = io.Chmod("/etc/systemd/system/panel.service", 0700); err != nil { - return err - } - if err = io.Chmod("/usr/local/sbin/panel-cli", 0700); err != nil { - return err - } - if err = io.Chmod(filepath.Join(app.Root, "panel"), 0700); err != nil { - return err - } - - if app.IsCli { - fmt.Println("|-修复完成") - } - - tools.RestartPanel() - return nil -} diff --git a/internal/data/ssh.go b/internal/data/ssh.go index 27b05dc2..d0792b55 100644 --- a/internal/data/ssh.go +++ b/internal/data/ssh.go @@ -2,31 +2,33 @@ package data import ( "fmt" + "gorm.io/gorm" - "github.com/samber/do/v2" - - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" pkgssh "github.com/TheTNB/panel/pkg/ssh" ) -type sshRepo struct{} +type sshRepo struct { + db *gorm.DB +} -func NewSSHRepo() biz.SSHRepo { - return do.MustInvoke[biz.SSHRepo](injector) +func NewSSHRepo(db *gorm.DB) biz.SSHRepo { + return &sshRepo{ + db: db, + } } func (r *sshRepo) List(page, limit uint) ([]*biz.SSH, int64, error) { var ssh []*biz.SSH var total int64 - err := app.Orm.Model(&biz.SSH{}).Omit("Hosts").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&ssh).Error + err := r.db.Model(&biz.SSH{}).Omit("Hosts").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&ssh).Error return ssh, total, err } func (r *sshRepo) Get(id uint) (*biz.SSH, error) { ssh := new(biz.SSH) - if err := app.Orm.Where("id = ?", id).First(ssh).Error; err != nil { + if err := r.db.Where("id = ?", id).First(ssh).Error; err != nil { return nil, err } @@ -54,7 +56,7 @@ func (r *sshRepo) Create(req *request.SSHCreate) error { Remark: req.Remark, } - return app.Orm.Create(ssh).Error + return r.db.Create(ssh).Error } func (r *sshRepo) Update(req *request.SSHUpdate) error { @@ -79,9 +81,9 @@ func (r *sshRepo) Update(req *request.SSHUpdate) error { Remark: req.Remark, } - return app.Orm.Model(ssh).Where("id = ?", req.ID).Select("*").Updates(ssh).Error + return r.db.Model(ssh).Where("id = ?", req.ID).Select("*").Updates(ssh).Error } func (r *sshRepo) Delete(id uint) error { - return app.Orm.Delete(&biz.SSH{}, id).Error + return r.db.Delete(&biz.SSH{}, id).Error } diff --git a/internal/data/task.go b/internal/data/task.go index 3e5a4c68..1c6e1fcc 100644 --- a/internal/data/task.go +++ b/internal/data/task.go @@ -2,88 +2,97 @@ package data import ( "fmt" + "github.com/TheTNB/panel/pkg/queue" + "gorm.io/gorm" "log/slog" - "github.com/samber/do/v2" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/queuejob" ) -type taskRepo struct{} +type taskRepo struct { + db *gorm.DB + log *slog.Logger + queue *queue.Queue +} -func NewTaskRepo() biz.TaskRepo { - return do.MustInvoke[biz.TaskRepo](injector) +func NewTaskRepo(db *gorm.DB, log *slog.Logger, queue *queue.Queue) biz.TaskRepo { + return &taskRepo{ + db: db, + log: log, + queue: queue, + } } func (r *taskRepo) HasRunningTask() bool { var count int64 - app.Orm.Model(&biz.Task{}).Where("status = ?", biz.TaskStatusRunning).Or("status = ?", biz.TaskStatusWaiting).Count(&count) + r.db.Model(&biz.Task{}).Where("status = ?", biz.TaskStatusRunning).Or("status = ?", biz.TaskStatusWaiting).Count(&count) return count > 0 } func (r *taskRepo) List(page, limit uint) ([]*biz.Task, int64, error) { var tasks []*biz.Task var total int64 - err := app.Orm.Model(&biz.Task{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&tasks).Error + err := r.db.Model(&biz.Task{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&tasks).Error return tasks, total, err } func (r *taskRepo) Get(id uint) (*biz.Task, error) { task := new(biz.Task) - err := app.Orm.Model(&biz.Task{}).Where("id = ?", id).First(task).Error + err := r.db.Model(&biz.Task{}).Where("id = ?", id).First(task).Error return task, err } func (r *taskRepo) Delete(id uint) error { - return app.Orm.Model(&biz.Task{}).Where("id = ?", id).Delete(&biz.Task{}).Error + return r.db.Model(&biz.Task{}).Where("id = ?", id).Delete(&biz.Task{}).Error } func (r *taskRepo) UpdateStatus(id uint, status biz.TaskStatus) error { - return app.Orm.Model(&biz.Task{}).Where("id = ?", id).Update("status", status).Error + return r.db.Model(&biz.Task{}).Where("id = ?", id).Update("status", status).Error } func (r *taskRepo) Push(task *biz.Task) error { var count int64 - if err := app.Orm.Model(&biz.Task{}).Where("shell = ? and (status = ? or status = ?)", task.Shell, biz.TaskStatusWaiting, biz.TaskStatusRunning).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.Task{}).Where("shell = ? and (status = ? or status = ?)", task.Shell, biz.TaskStatusWaiting, biz.TaskStatusRunning).Count(&count).Error; err != nil { return err } if count > 0 { return fmt.Errorf("duplicate submission, please wait for the previous task to end") } - if err := app.Orm.Create(task).Error; err != nil { + if err := r.db.Create(task).Error; err != nil { return err } - return app.Queue.Push(queuejob.NewProcessTask(r), []any{ + return r.queue.Push(queuejob.NewProcessTask(r.log, r), []any{ task.ID, }) } +// TODO fix func (r *taskRepo) DispatchWaiting() { // cli下不处理 if app.IsCli { return } - if err := app.Orm.Model(&biz.Task{}).Where("status = ?", biz.TaskStatusRunning).Update("status", biz.TaskStatusFailed).Error; err != nil { - app.Logger.Warn("failed to mark running tasks as failed", slog.Any("err", err)) + if err := r.db.Model(&biz.Task{}).Where("status = ?", biz.TaskStatusRunning).Update("status", biz.TaskStatusFailed).Error; err != nil { + r.log.Warn("failed to mark running tasks as failed", slog.Any("err", err)) return } var tasks []biz.Task - if err := app.Orm.Where("status = ?", biz.TaskStatusWaiting).Find(&tasks).Error; err != nil { - app.Logger.Warn("failed to get pending tasks", slog.Any("err", err)) + if err := r.db.Where("status = ?", biz.TaskStatusWaiting).Find(&tasks).Error; err != nil { + r.log.Warn("failed to get pending tasks", slog.Any("err", err)) return } for _, task := range tasks { - if err := app.Queue.Push(queuejob.NewProcessTask(r), []any{ + if err := r.queue.Push(queuejob.NewProcessTask(r.log, r), []any{ task.ID, }); err != nil { - app.Logger.Warn("failed to push task", slog.Any("err", err)) + r.log.Warn("failed to push task", slog.Any("err", err)) return } } diff --git a/internal/data/user.go b/internal/data/user.go index e2419978..65d5e572 100644 --- a/internal/data/user.go +++ b/internal/data/user.go @@ -4,19 +4,21 @@ import ( "errors" "github.com/go-rat/utils/hash" - "github.com/samber/do/v2" "gorm.io/gorm" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" ) type userRepo struct { + db *gorm.DB hasher hash.Hasher } -func NewUserRepo() biz.UserRepo { - return do.MustInvoke[biz.UserRepo](injector) +func NewUserRepo(db *gorm.DB) biz.UserRepo { + return &userRepo{ + db: db, + hasher: hash.NewArgon2id(), + } } func (r *userRepo) Create(username, password string) (*biz.User, error) { @@ -29,7 +31,7 @@ func (r *userRepo) Create(username, password string) (*biz.User, error) { Username: username, Password: value, } - if err = app.Orm.Create(user).Error; err != nil { + if err = r.db.Create(user).Error; err != nil { return nil, err } @@ -38,7 +40,7 @@ func (r *userRepo) Create(username, password string) (*biz.User, error) { func (r *userRepo) CheckPassword(username, password string) (*biz.User, error) { user := new(biz.User) - if err := app.Orm.Where("username = ?", username).First(user).Error; err != nil { + if err := r.db.Where("username = ?", username).First(user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("用户名或密码错误") } else { @@ -55,7 +57,7 @@ func (r *userRepo) CheckPassword(username, password string) (*biz.User, error) { func (r *userRepo) Get(id uint) (*biz.User, error) { user := new(biz.User) - if err := app.Orm.First(user, id).Error; err != nil { + if err := r.db.First(user, id).Error; err != nil { return nil, err } @@ -63,5 +65,5 @@ func (r *userRepo) Get(id uint) (*biz.User, error) { } func (r *userRepo) Save(user *biz.User) error { - return app.Orm.Save(user).Error + return r.db.Save(user).Error } diff --git a/internal/data/website.go b/internal/data/website.go index 9f456861..6b7853f6 100644 --- a/internal/data/website.go +++ b/internal/data/website.go @@ -5,12 +5,12 @@ import ( "encoding/json" "errors" "fmt" + "gorm.io/gorm" "path/filepath" "slices" "strings" "time" - "github.com/samber/do/v2" "github.com/samber/lo" "github.com/spf13/cast" @@ -29,14 +29,30 @@ import ( "github.com/TheTNB/panel/pkg/types" ) -type websiteRepo struct{} +type websiteRepo struct { + db *gorm.DB + cache biz.CacheRepo + database biz.DatabaseRepo + databaseServer biz.DatabaseServerRepo + databaseUser biz.DatabaseUserRepo + cert biz.CertRepo + certAccount biz.CertAccountRepo +} -func NewWebsiteRepo() biz.WebsiteRepo { - return do.MustInvoke[biz.WebsiteRepo](injector) +func NewWebsiteRepo(db *gorm.DB, cache biz.CacheRepo, database biz.DatabaseRepo, databaseServer biz.DatabaseServerRepo, databaseUser biz.DatabaseUserRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo) biz.WebsiteRepo { + return &websiteRepo{ + db: db, + cache: cache, + database: database, + databaseServer: databaseServer, + databaseUser: databaseUser, + cert: cert, + certAccount: certAccount, + } } func (r *websiteRepo) GetRewrites() (map[string]string, error) { - cached, err := NewCacheRepo().Get(biz.CacheKeyRewrites) + cached, err := r.cache.Get(biz.CacheKeyRewrites) if err != nil { return nil, err } @@ -67,7 +83,7 @@ func (r *websiteRepo) UpdateDefaultConfig(req *request.WebsiteDefaultConfig) err func (r *websiteRepo) Count() (int64, error) { var count int64 - if err := app.Orm.Model(&biz.Website{}).Count(&count).Error; err != nil { + if err := r.db.Model(&biz.Website{}).Count(&count).Error; err != nil { return 0, err } @@ -76,7 +92,7 @@ func (r *websiteRepo) Count() (int64, error) { func (r *websiteRepo) Get(id uint) (*types.WebsiteSetting, error) { website := new(biz.Website) - if err := app.Orm.Where("id", id).First(website).Error; err != nil { + if err := r.db.Where("id", id).First(website).Error; err != nil { return nil, err } // 解析nginx配置 @@ -176,7 +192,7 @@ func (r *websiteRepo) Get(id uint) (*types.WebsiteSetting, error) { func (r *websiteRepo) GetByName(name string) (*types.WebsiteSetting, error) { website := new(biz.Website) - if err := app.Orm.Where("name", name).First(website).Error; err != nil { + if err := r.db.Where("name", name).First(website).Error; err != nil { return nil, err } @@ -188,11 +204,11 @@ func (r *websiteRepo) List(page, limit uint) ([]*biz.Website, int64, error) { var websites []*biz.Website var total int64 - if err := app.Orm.Model(&biz.Website{}).Count(&total).Error; err != nil { + if err := r.db.Model(&biz.Website{}).Count(&total).Error; err != nil { return nil, 0, err } - if err := app.Orm.Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&websites).Error; err != nil { + if err := r.db.Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&websites).Error; err != nil { return nil, 0, err } @@ -312,7 +328,7 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) { Https: false, Remark: req.Remark, } - if err = app.Orm.Create(w).Error; err != nil { + if err = r.db.Create(w).Error; err != nil { return nil, err } @@ -324,11 +340,11 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) { // 创建数据库 name := "local_" + req.DBType if req.DB { - server, err := NewDatabaseServerRepo().GetByName(name) + server, err := r.databaseServer.GetByName(name) if err != nil { return nil, fmt.Errorf(`create database: can't find %s database server, please add it first`, name) } - if err = NewDatabaseRepo().Create(&request.DatabaseCreate{ + if err = r.database.Create(&request.DatabaseCreate{ ServerID: server.ID, Name: req.DBName, CreateUser: true, @@ -346,7 +362,7 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) { func (r *websiteRepo) Update(req *request.WebsiteUpdate) error { website := new(biz.Website) - if err := app.Orm.Where("id", req.ID).First(website).Error; err != nil { + if err := r.db.Where("id", req.ID).First(website).Error; err != nil { return err } if !website.Status { @@ -494,7 +510,7 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error { return err } - if err = app.Orm.Save(website).Error; err != nil { + if err = r.db.Save(website).Error; err != nil { return err } @@ -508,7 +524,7 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error { func (r *websiteRepo) Delete(req *request.WebsiteDelete) error { website := new(biz.Website) - if err := app.Orm.Preload("Cert").Where("id", req.ID).First(website).Error; err != nil { + if err := r.db.Preload("Cert").Where("id", req.ID).First(website).Error; err != nil { return err } if website.Cert != nil { @@ -527,18 +543,17 @@ func (r *websiteRepo) Delete(req *request.WebsiteDelete) error { _ = io.Remove(website.Path) } if req.DB { - repo := NewDatabaseServerRepo() - if mysql, err := repo.GetByName("local_mysql"); err == nil { - _ = NewDatabaseUserRepo().DeleteByNames(mysql.ID, []string{website.Name}) - _ = NewDatabaseRepo().Delete(mysql.ID, website.Name) + if mysql, err := r.databaseServer.GetByName("local_mysql"); err == nil { + _ = r.databaseUser.DeleteByNames(mysql.ID, []string{website.Name}) + _ = r.database.Delete(mysql.ID, website.Name) } - if postgres, err := repo.GetByName("local_postgresql"); err == nil { - _ = NewDatabaseUserRepo().DeleteByNames(postgres.ID, []string{website.Name}) - _ = NewDatabaseRepo().Delete(postgres.ID, website.Name) + if postgres, err := r.databaseServer.GetByName("local_postgresql"); err == nil { + _ = r.databaseUser.DeleteByNames(postgres.ID, []string{website.Name}) + _ = r.database.Delete(postgres.ID, website.Name) } } - if err := app.Orm.Delete(website).Error; err != nil { + if err := r.db.Delete(website).Error; err != nil { return err } @@ -552,7 +567,7 @@ func (r *websiteRepo) Delete(req *request.WebsiteDelete) error { func (r *websiteRepo) ClearLog(id uint) error { website := new(biz.Website) - if err := app.Orm.Where("id", id).First(website).Error; err != nil { + if err := r.db.Where("id", id).First(website).Error; err != nil { return err } @@ -562,17 +577,17 @@ func (r *websiteRepo) ClearLog(id uint) error { func (r *websiteRepo) UpdateRemark(id uint, remark string) error { website := new(biz.Website) - if err := app.Orm.Where("id", id).First(website).Error; err != nil { + if err := r.db.Where("id", id).First(website).Error; err != nil { return err } website.Remark = remark - return app.Orm.Save(website).Error + return r.db.Save(website).Error } func (r *websiteRepo) ResetConfig(id uint) error { website := new(biz.Website) - if err := app.Orm.Where("id", id).First(&website).Error; err != nil { + if err := r.db.Where("id", id).First(&website).Error; err != nil { return err } @@ -617,7 +632,7 @@ func (r *websiteRepo) ResetConfig(id uint) error { website.Status = true website.Https = false - if err = app.Orm.Save(website).Error; err != nil { + if err = r.db.Save(website).Error; err != nil { return err } @@ -631,7 +646,7 @@ func (r *websiteRepo) ResetConfig(id uint) error { func (r *websiteRepo) UpdateStatus(id uint, status bool) error { website := new(biz.Website) - if err := app.Orm.Where("id", id).First(&website).Error; err != nil { + if err := r.db.Where("id", id).First(&website).Error; err != nil { return err } @@ -694,7 +709,7 @@ func (r *websiteRepo) UpdateStatus(id uint, status bool) error { } website.Status = status - if err = app.Orm.Save(website).Error; err != nil { + if err = r.db.Save(website).Error; err != nil { return err } @@ -715,15 +730,14 @@ func (r *websiteRepo) ObtainCert(ctx context.Context, id uint) error { return errors.New("cannot one-key obtain wildcard certificate") } - account, err := NewCertAccountRepo().GetDefault(cast.ToUint(ctx.Value("user_id"))) + account, err := r.certAccount.GetDefault(cast.ToUint(ctx.Value("user_id"))) if err != nil { return err } - cRepo := NewCertRepo() - newCert, err := cRepo.GetByWebsite(website.ID) + newCert, err := r.cert.GetByWebsite(website.ID) if err != nil { - newCert, err = cRepo.Create(&request.CertCreate{ + newCert, err = r.cert.Create(&request.CertCreate{ Type: string(acme.KeyEC256), Domains: website.Domains, AutoRenew: true, @@ -735,14 +749,14 @@ func (r *websiteRepo) ObtainCert(ctx context.Context, id uint) error { } } newCert.Domains = website.Domains - if err = app.Orm.Save(newCert).Error; err != nil { + if err = r.db.Save(newCert).Error; err != nil { return err } - _, err = cRepo.ObtainAuto(newCert.ID) + _, err = r.cert.ObtainAuto(newCert.ID) if err != nil { return err } - return cRepo.Deploy(newCert.ID, website.ID) + return r.cert.Deploy(newCert.ID, website.ID) } diff --git a/internal/http/middleware/entrance.go b/internal/http/middleware/entrance.go index 8748f066..110a0876 100644 --- a/internal/http/middleware/entrance.go +++ b/internal/http/middleware/entrance.go @@ -1,46 +1,48 @@ package middleware import ( + "github.com/go-rat/sessions" + "github.com/knadh/koanf/v2" "net/http" "strings" "github.com/go-rat/chix" "github.com/spf13/cast" - - "github.com/TheTNB/panel/internal/app" ) // Entrance 确保通过正确的入口访问 -func Entrance(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - sess, err := app.Session.GetSession(r) - if err != nil { - render := chix.NewRender(w) - render.Status(http.StatusInternalServerError) - render.JSON(chix.M{ - "message": err.Error(), - }) - } +func Entrance(conf *koanf.Koanf, 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) + if err != nil { + render := chix.NewRender(w) + render.Status(http.StatusInternalServerError) + render.JSON(chix.M{ + "message": err.Error(), + }) + } - entrance := app.Conf.String("http.entrance") - if strings.TrimSuffix(r.URL.Path, "/") == strings.TrimSuffix(entrance, "/") { - sess.Put("verify_entrance", true) - render := chix.NewRender(w, r) - render.Redirect("/login") - return - } + entrance := conf.String("http.entrance") + if strings.TrimSuffix(r.URL.Path, "/") == strings.TrimSuffix(entrance, "/") { + sess.Put("verify_entrance", true) + render := chix.NewRender(w, r) + render.Redirect("/login") + return + } - if !app.Conf.Bool("app.debug") && - !cast.ToBool(sess.Get("verify_entrance", false)) && - r.URL.Path != "/robots.txt" { - render := chix.NewRender(w) - render.Status(http.StatusTeapot) - render.JSON(chix.M{ - "message": "请通过正确的入口访问", - }) - return - } + if !conf.Bool("app.debug") && + !cast.ToBool(sess.Get("verify_entrance", false)) && + r.URL.Path != "/robots.txt" { + render := chix.NewRender(w) + render.Status(http.StatusTeapot) + render.JSON(chix.M{ + "message": "请通过正确的入口访问", + }) + return + } - next.ServeHTTP(w, r) - }) + next.ServeHTTP(w, r) + }) + } } diff --git a/internal/http/middleware/middleware.go b/internal/http/middleware/middleware.go index a4242948..3d97ceda 100644 --- a/internal/http/middleware/middleware.go +++ b/internal/http/middleware/middleware.go @@ -1,32 +1,34 @@ package middleware import ( + "github.com/go-chi/chi/v5" + "github.com/go-rat/sessions" + "github.com/knadh/koanf/v2" + "gorm.io/gorm" "log/slog" "net/http" "github.com/go-chi/chi/v5/middleware" sessionmiddleware "github.com/go-rat/sessions/middleware" "github.com/golang-cz/httplog" - - "github.com/TheTNB/panel/internal/app" ) // GlobalMiddleware is a collection of global middleware that will be applied to every request. -func GlobalMiddleware() []func(http.Handler) http.Handler { +func GlobalMiddleware(r *chi.Mux, conf *koanf.Koanf, db *gorm.DB, log *slog.Logger, session *sessions.Manager) []func(http.Handler) http.Handler { return []func(http.Handler) http.Handler{ - sessionmiddleware.StartSession(app.Session), - //middleware.SupressNotFound(app.Http),// bug https://github.com/go-chi/chi/pull/940 + sessionmiddleware.StartSession(session), + //middleware.SupressNotFound(r),// bug https://github.com/go-chi/chi/pull/940 middleware.CleanPath, middleware.StripSlashes, middleware.Compress(5), - httplog.RequestLogger(app.Logger, &httplog.Options{ + httplog.RequestLogger(log, &httplog.Options{ Level: slog.LevelInfo, LogRequestHeaders: []string{"User-Agent"}, }), middleware.Recoverer, Status, - Entrance, - MustLogin, - MustInstall, + Entrance(conf, session), + MustLogin(session), + MustInstall(db), } } diff --git a/internal/http/middleware/must_install.go b/internal/http/middleware/must_install.go index 5767e3b5..9920735e 100644 --- a/internal/http/middleware/must_install.go +++ b/internal/http/middleware/must_install.go @@ -2,51 +2,53 @@ package middleware import ( "fmt" + "github.com/TheTNB/panel/internal/data" + "gorm.io/gorm" "net/http" "strings" "github.com/go-rat/chix" - - "github.com/TheTNB/panel/internal/data" ) // MustInstall 确保已安装应用 -func MustInstall(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var slugs []string - if strings.HasPrefix(r.URL.Path, "/api/website") { - slugs = append(slugs, "nginx") - } else if strings.HasPrefix(r.URL.Path, "/api/container") { - slugs = append(slugs, "podman", "docker") - } else if strings.HasPrefix(r.URL.Path, "/api/apps/") { - pathArr := strings.Split(r.URL.Path, "/") - if len(pathArr) < 4 { +func MustInstall(db *gorm.DB) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var slugs []string + if strings.HasPrefix(r.URL.Path, "/api/website") { + slugs = append(slugs, "nginx") + } else if strings.HasPrefix(r.URL.Path, "/api/container") { + slugs = append(slugs, "podman", "docker") + } else if strings.HasPrefix(r.URL.Path, "/api/apps/") { + pathArr := strings.Split(r.URL.Path, "/") + if len(pathArr) < 4 { + render := chix.NewRender(w) + render.Status(http.StatusForbidden) + render.JSON(chix.M{ + "message": "应用不存在", + }) + return + } + slugs = append(slugs, pathArr[3]) + } + + flag := false + for _, s := range slugs { + if installed, _ := data.NewAppRepo(db, nil, nil).IsInstalled("slug = ?", s); installed { // TODO 优化实现 + flag = true + break + } + } + if !flag && len(slugs) > 0 { render := chix.NewRender(w) render.Status(http.StatusForbidden) render.JSON(chix.M{ - "message": "应用不存在", + "message": fmt.Sprintf("应用 %s 未安装", slugs), }) return } - slugs = append(slugs, pathArr[3]) - } - flag := false - for _, s := range slugs { - if installed, _ := data.NewAppRepo().IsInstalled("slug = ?", s); installed { - flag = true - break - } - } - if !flag && len(slugs) > 0 { - render := chix.NewRender(w) - render.Status(http.StatusForbidden) - render.JSON(chix.M{ - "message": fmt.Sprintf("应用 %s 未安装", slugs), - }) - return - } - - next.ServeHTTP(w, r) - }) + next.ServeHTTP(w, r) + }) + } } diff --git a/internal/http/middleware/must_login.go b/internal/http/middleware/must_login.go index 473ab21c..02e64257 100644 --- a/internal/http/middleware/must_login.go +++ b/internal/http/middleware/must_login.go @@ -4,6 +4,7 @@ import ( "context" "crypto/sha256" "fmt" + "github.com/go-rat/sessions" "net" "net/http" "slices" @@ -11,12 +12,10 @@ import ( "github.com/go-rat/chix" "github.com/spf13/cast" - - "github.com/TheTNB/panel/internal/app" ) // MustLogin 确保已登录 -func MustLogin(next http.Handler) http.Handler { +func MustLogin(session *sessions.Manager) func(next http.Handler) http.Handler { // 白名单 whiteList := []string{ "/api/user/key", @@ -25,58 +24,59 @@ func MustLogin(next http.Handler) http.Handler { "/api/user/isLogin", "/api/dashboard/panel", } + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + sess, err := session.GetSession(r) + if err != nil { + render := chix.NewRender(w) + render.Status(http.StatusInternalServerError) + render.JSON(chix.M{ + "message": err.Error(), + }) + } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - sess, err := app.Session.GetSession(r) - if err != nil { - render := chix.NewRender(w) - render.Status(http.StatusInternalServerError) - render.JSON(chix.M{ - "message": err.Error(), - }) - } + // 对白名单和非 API 请求放行 + if slices.Contains(whiteList, r.URL.Path) || !strings.HasPrefix(r.URL.Path, "/api") { + next.ServeHTTP(w, r) + return + } - // 对白名单和非 API 请求放行 - if slices.Contains(whiteList, r.URL.Path) || !strings.HasPrefix(r.URL.Path, "/api") { - next.ServeHTTP(w, r) - return - } - - if sess.Missing("user_id") { - render := chix.NewRender(w) - render.Status(http.StatusUnauthorized) - render.JSON(chix.M{ - "message": "会话已过期,请重新登录", - }) - return - } - - userID := cast.ToUint(sess.Get("user_id")) - if userID == 0 { - render := chix.NewRender(w) - render.Status(http.StatusUnauthorized) - render.JSON(chix.M{ - "message": "会话无效,请重新登录", - }) - return - } - - safeLogin := cast.ToBool(sess.Get("safe_login")) - if safeLogin { - safeClientHash := cast.ToString(sess.Get("safe_client")) - ip, _, _ := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)) - clientHash := fmt.Sprintf("%x", sha256.Sum256([]byte(ip))) - if safeClientHash != clientHash || safeClientHash == "" { + if sess.Missing("user_id") { render := chix.NewRender(w) render.Status(http.StatusUnauthorized) render.JSON(chix.M{ - "message": "客户端IP/UA变化,请重新登录", + "message": "会话已过期,请重新登录", }) return } - } - r = r.WithContext(context.WithValue(r.Context(), "user_id", userID)) // nolint:staticcheck - next.ServeHTTP(w, r) - }) + userID := cast.ToUint(sess.Get("user_id")) + if userID == 0 { + render := chix.NewRender(w) + render.Status(http.StatusUnauthorized) + render.JSON(chix.M{ + "message": "会话无效,请重新登录", + }) + return + } + + safeLogin := cast.ToBool(sess.Get("safe_login")) + if safeLogin { + safeClientHash := cast.ToString(sess.Get("safe_client")) + ip, _, _ := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)) + clientHash := fmt.Sprintf("%x", sha256.Sum256([]byte(ip))) + if safeClientHash != clientHash || safeClientHash == "" { + render := chix.NewRender(w) + render.Status(http.StatusUnauthorized) + render.JSON(chix.M{ + "message": "客户端IP/UA变化,请重新登录", + }) + return + } + } + + r = r.WithContext(context.WithValue(r.Context(), "user_id", userID)) // nolint:staticcheck + next.ServeHTTP(w, r) + }) + } } diff --git a/internal/job/cert_renew.go b/internal/job/cert_renew.go index a5740c2f..21c9dacd 100644 --- a/internal/job/cert_renew.go +++ b/internal/job/cert_renew.go @@ -1,23 +1,27 @@ package job import ( + "gorm.io/gorm" "log/slog" "time" "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" pkgcert "github.com/TheTNB/panel/pkg/cert" ) // CertRenew 证书续签 type CertRenew struct { + db *gorm.DB + log *slog.Logger certRepo biz.CertRepo } -func NewCertRenew() *CertRenew { +func NewCertRenew(db *gorm.DB, log *slog.Logger, cert biz.CertRepo) *CertRenew { return &CertRenew{ - certRepo: data.NewCertRepo(), + db: db, + log: log, + certRepo: cert, } } @@ -27,8 +31,8 @@ func (r *CertRenew) Run() { } var certs []biz.Cert - if err := app.Orm.Preload("Website").Preload("Account").Preload("DNS").Find(&certs).Error; err != nil { - app.Logger.Warn("获取证书失败", slog.Any("err", err)) + if err := r.db.Preload("Website").Preload("Account").Preload("DNS").Find(&certs).Error; err != nil { + r.log.Warn("获取证书失败", slog.Any("err", err)) return } @@ -50,7 +54,7 @@ func (r *CertRenew) Run() { _, err = r.certRepo.Renew(cert.ID) if err != nil { - app.Logger.Warn("续签证书失败", slog.Any("err", err)) + r.log.Warn("续签证书失败", slog.Any("err", err)) } } } diff --git a/internal/job/init.go b/internal/job/init.go index b1b812c3..143165d0 100644 --- a/internal/job/init.go +++ b/internal/job/init.go @@ -4,16 +4,18 @@ import ( "github.com/robfig/cron/v3" ) +// TODO fix me func Boot(c *cron.Cron) error { - if _, err := c.AddJob("* * * * *", NewMonitoring()); err != nil { + /*if _, err := c.AddJob("* * * * *", NewMonitoring()); err != nil { return err } if _, err := c.AddJob("0 4 * * *", NewCertRenew()); err != nil { return err } + if _, err := c.AddJob("0 2 * * *", NewPanelTask()); err != nil { return err - } + }*/ return nil } diff --git a/internal/job/monitoring.go b/internal/job/monitoring.go index 8c3ebf6d..5e8e7b3b 100644 --- a/internal/job/monitoring.go +++ b/internal/job/monitoring.go @@ -1,6 +1,7 @@ package job import ( + "gorm.io/gorm" "log/slog" "time" @@ -8,18 +9,21 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/pkg/tools" ) // Monitoring 系统监控 type Monitoring struct { + db *gorm.DB + log *slog.Logger settingRepo biz.SettingRepo } -func NewMonitoring() *Monitoring { +func NewMonitoring(db *gorm.DB, log *slog.Logger, setting biz.SettingRepo) *Monitoring { return &Monitoring{ - settingRepo: data.NewSettingRepo(), + db: db, + log: log, + settingRepo: setting, } } @@ -47,8 +51,8 @@ func (r *Monitoring) Run() { return } - if err = app.Orm.Create(&biz.Monitor{Info: info}).Error; err != nil { - app.Logger.Warn("记录系统监控失败", slog.Any("err", err)) + if err = r.db.Create(&biz.Monitor{Info: info}).Error; err != nil { + r.log.Warn("记录系统监控失败", slog.Any("err", err)) return } @@ -61,8 +65,8 @@ func (r *Monitoring) Run() { if day <= 0 || app.Status != app.StatusNormal { return } - if err = app.Orm.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format(time.DateTime)).Delete(&biz.Monitor{}).Error; err != nil { - app.Logger.Warn("删除过期系统监控失败", slog.Any("err", err)) + if err = r.db.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format(time.DateTime)).Delete(&biz.Monitor{}).Error; err != nil { + r.log.Warn("删除过期系统监控失败", slog.Any("err", err)) return } } diff --git a/internal/job/panel_task.go b/internal/job/panel_task.go index ddcb4fa8..00213390 100644 --- a/internal/job/panel_task.go +++ b/internal/job/panel_task.go @@ -1,6 +1,7 @@ package job import ( + "gorm.io/gorm" "log/slog" "math/rand/v2" "runtime" @@ -9,21 +10,24 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" ) // PanelTask 面板每日任务 type PanelTask struct { + db *gorm.DB + log *slog.Logger backupRepo biz.BackupRepo cacheRepo biz.CacheRepo settingRepo biz.SettingRepo } -func NewPanelTask() *PanelTask { +func NewPanelTask(db *gorm.DB, log *slog.Logger, backup biz.BackupRepo, cache biz.CacheRepo, setting biz.SettingRepo) *PanelTask { return &PanelTask{ - backupRepo: data.NewBackupRepo(), - cacheRepo: data.NewCacheRepo(), - settingRepo: data.NewSettingRepo(), + db: db, + log: log, + backupRepo: backup, + cacheRepo: cache, + settingRepo: setting, } } @@ -31,25 +35,25 @@ func (r *PanelTask) Run() { app.Status = app.StatusMaintain // 优化数据库 - if err := app.Orm.Exec("VACUUM").Error; err != nil { + if err := r.db.Exec("VACUUM").Error; err != nil { app.Status = app.StatusFailed - app.Logger.Warn("优化面板数据库失败", slog.Any("err", err)) + r.log.Warn("优化面板数据库失败", slog.Any("err", err)) } - if err := app.Orm.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil { + if err := r.db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil { app.Status = app.StatusFailed - app.Logger.Warn("优化面板数据库失败", slog.Any("err", err)) + r.log.Warn("优化面板数据库失败", slog.Any("err", err)) } // 备份面板 if err := r.backupRepo.Create(biz.BackupTypePanel, ""); err != nil { - app.Logger.Warn("备份面板失败", slog.Any("err", err)) + r.log.Warn("备份面板失败", slog.Any("err", err)) } // 清理备份 path, err := r.backupRepo.GetPath("panel") if err == nil { if err = r.backupRepo.ClearExpired(path, "panel_", 10); err != nil { - app.Logger.Warn("清理面板备份失败", slog.Any("err", err)) + r.log.Warn("清理面板备份失败", slog.Any("err", err)) } } @@ -57,7 +61,7 @@ func (r *PanelTask) Run() { time.AfterFunc(time.Duration(rand.IntN(300))*time.Second, func() { if offline, err := r.settingRepo.GetBool(biz.SettingKeyOfflineMode); err == nil && !offline { if err = r.cacheRepo.UpdateApps(); err != nil { - app.Logger.Warn("更新商店缓存失败", slog.Any("err", err)) + r.log.Warn("更新商店缓存失败", slog.Any("err", err)) } } }) @@ -66,7 +70,7 @@ func (r *PanelTask) Run() { time.AfterFunc(time.Duration(rand.IntN(300))*time.Second, func() { if offline, err := r.settingRepo.GetBool(biz.SettingKeyOfflineMode); err == nil && !offline { if err = r.cacheRepo.UpdateRewrites(); err != nil { - app.Logger.Warn("更新伪静态缓存失败", slog.Any("err", err)) + r.log.Warn("更新伪静态缓存失败", slog.Any("err", err)) } } }) diff --git a/internal/queuejob/process_task.go b/internal/queuejob/process_task.go index cd78adfd..7db9a22e 100644 --- a/internal/queuejob/process_task.go +++ b/internal/queuejob/process_task.go @@ -4,20 +4,21 @@ import ( "errors" "log/slog" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/pkg/shell" ) // ProcessTask 处理面板任务 type ProcessTask struct { + log *slog.Logger taskRepo biz.TaskRepo taskID uint } // NewProcessTask 实例化 ProcessTask -func NewProcessTask(taskRepo biz.TaskRepo) *ProcessTask { +func NewProcessTask(log *slog.Logger, taskRepo biz.TaskRepo) *ProcessTask { return &ProcessTask{ + log: log, taskRepo: taskRepo, } } @@ -50,6 +51,6 @@ func (r *ProcessTask) Handle(args ...any) error { } func (r *ProcessTask) ErrHandle(err error) { - app.Logger.Warn("background task failed", slog.Any("task_id", r.taskID), slog.Any("err", err)) + r.log.Warn("background task failed", slog.Any("task_id", r.taskID), slog.Any("err", err)) _ = r.taskRepo.UpdateStatus(r.taskID, biz.TaskStatusFailed) } diff --git a/internal/route/cli.go b/internal/route/cli.go index db4c72d4..15d8eff4 100644 --- a/internal/route/cli.go +++ b/internal/route/cli.go @@ -6,43 +6,52 @@ import ( "github.com/TheTNB/panel/internal/service" ) -func Cli() []*cli.Command { - cliService := service.NewCliService() +type Cli struct { + cli *service.CliService +} + +func NewCli(cli *service.CliService) *Cli { + return &Cli{ + cli: cli, + } +} + +func (route *Cli) Commands() []*cli.Command { return []*cli.Command{ { Name: "restart", Usage: "重启面板服务", - Action: cliService.Restart, + Action: route.cli.Restart, }, { Name: "stop", Usage: "停止面板服务", - Action: cliService.Stop, + Action: route.cli.Stop, }, { Name: "start", Usage: "启动面板服务", - Action: cliService.Start, + Action: route.cli.Start, }, { Name: "update", Usage: "更新面板", - Action: cliService.Update, + Action: route.cli.Update, }, { Name: "sync", Usage: "同步数据", - Action: cliService.Sync, + Action: route.cli.Sync, }, { Name: "fix", Usage: "修复面板", - Action: cliService.Fix, + Action: route.cli.Fix, }, { Name: "info", Usage: "输出面板基本信息", - Action: cliService.Info, + Action: route.cli.Info, }, { Name: "user", @@ -51,17 +60,17 @@ func Cli() []*cli.Command { { Name: "list", Usage: "列出所有用户", - Action: cliService.UserList, + Action: route.cli.UserList, }, { Name: "username", Usage: "修改用户名", - Action: cliService.UserName, + Action: route.cli.UserName, }, { Name: "password", Usage: "修改用户密码", - Action: cliService.UserPassword, + Action: route.cli.UserPassword, }, }, }, @@ -72,17 +81,17 @@ func Cli() []*cli.Command { { Name: "on", Usage: "开启HTTPS", - Action: cliService.HTTPSOn, + Action: route.cli.HTTPSOn, }, { Name: "off", Usage: "关闭HTTPS", - Action: cliService.HTTPSOff, + Action: route.cli.HTTPSOff, }, { Name: "generate", Usage: "生成HTTPS证书", - Action: cliService.HTTPSGenerate, + Action: route.cli.HTTPSGenerate, }, }, }, @@ -93,19 +102,19 @@ func Cli() []*cli.Command { { Name: "on", Usage: "开启访问入口", - Action: cliService.EntranceOn, + Action: route.cli.EntranceOn, }, { Name: "off", Usage: "关闭访问入口", - Action: cliService.EntranceOff, + Action: route.cli.EntranceOff, }, }, }, { Name: "port", Usage: "修改面板端口", - Action: cliService.Port, + Action: route.cli.Port, }, { Name: "website", @@ -114,7 +123,7 @@ func Cli() []*cli.Command { { Name: "create", Usage: "创建新网站", - Action: cliService.WebsiteCreate, + Action: route.cli.WebsiteCreate, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -147,7 +156,7 @@ func Cli() []*cli.Command { { Name: "remove", Usage: "移除网站", - Action: cliService.WebsiteRemove, + Action: route.cli.WebsiteRemove, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -160,7 +169,7 @@ func Cli() []*cli.Command { { Name: "delete", Usage: "删除网站(包括网站目录、同名数据库)", - Action: cliService.WebsiteDelete, + Action: route.cli.WebsiteDelete, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -174,7 +183,7 @@ func Cli() []*cli.Command { Name: "write", Usage: "写入网站数据(仅限指导下使用)", Hidden: true, - Action: cliService.WebsiteWrite, + Action: route.cli.WebsiteWrite, }, }, }, @@ -185,7 +194,7 @@ func Cli() []*cli.Command { { Name: "add-server", Usage: "添加数据库服务器", - Action: cliService.DatabaseAddServer, + Action: route.cli.DatabaseAddServer, Flags: []cli.Flag{ &cli.StringFlag{ Name: "type", @@ -224,7 +233,7 @@ func Cli() []*cli.Command { { Name: "delete-server", Usage: "删除数据库服务器", - Action: cliService.DatabaseDeleteServer, + Action: route.cli.DatabaseDeleteServer, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -243,7 +252,7 @@ func Cli() []*cli.Command { { Name: "website", Usage: "备份网站", - Action: cliService.BackupWebsite, + Action: route.cli.BackupWebsite, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -261,7 +270,7 @@ func Cli() []*cli.Command { { Name: "database", Usage: "备份数据库", - Action: cliService.BackupDatabase, + Action: route.cli.BackupDatabase, Flags: []cli.Flag{ &cli.StringFlag{ Name: "type", @@ -285,7 +294,7 @@ func Cli() []*cli.Command { { Name: "panel", Usage: "备份面板", - Action: cliService.BackupPanel, + Action: route.cli.BackupPanel, Flags: []cli.Flag{ &cli.StringFlag{ Name: "path", @@ -297,7 +306,7 @@ func Cli() []*cli.Command { { Name: "clear", Usage: "清理备份", - Action: cliService.BackupClear, + Action: route.cli.BackupClear, Flags: []cli.Flag{ &cli.StringFlag{ Name: "type", @@ -333,7 +342,7 @@ func Cli() []*cli.Command { { Name: "website", Usage: "网站", - Action: cliService.CutoffWebsite, + Action: route.cli.CutoffWebsite, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -352,7 +361,7 @@ func Cli() []*cli.Command { { Name: "clear", Usage: "清理切割的日志", - Action: cliService.CutoffClear, + Action: route.cli.CutoffClear, Flags: []cli.Flag{ &cli.StringFlag{ Name: "type", @@ -388,29 +397,29 @@ func Cli() []*cli.Command { { Name: "install", Usage: "安装应用", - Action: cliService.AppInstall, + Action: route.cli.AppInstall, }, { Name: "uninstall", Usage: "卸载应用", - Action: cliService.AppUnInstall, + Action: route.cli.AppUnInstall, }, { Name: "update", Usage: "更新应用", - Action: cliService.AppUpdate, + Action: route.cli.AppUpdate, }, { Name: "write", Usage: "添加面板应用标记(仅限指导下使用)", Hidden: true, - Action: cliService.AppWrite, + Action: route.cli.AppWrite, }, { Name: "remove", Usage: "移除面板应用标记(仅限指导下使用)", Hidden: true, - Action: cliService.AppRemove, + Action: route.cli.AppRemove, }, }, }, @@ -423,38 +432,38 @@ func Cli() []*cli.Command { Name: "get", Usage: "获取面板设置(仅限指导下使用)", Hidden: true, - Action: cliService.GetSetting, + Action: route.cli.GetSetting, }, { Name: "write", Usage: "写入面板设置(仅限指导下使用)", Hidden: true, - Action: cliService.WriteSetting, + Action: route.cli.WriteSetting, }, { Name: "remove", Usage: "移除面板设置(仅限指导下使用)", Hidden: true, - Action: cliService.RemoveSetting, + Action: route.cli.RemoveSetting, }, }, }, { Name: "sync-time", Usage: "同步系统时间", - Action: cliService.SyncTime, + Action: route.cli.SyncTime, }, { Name: "clear-task", Usage: "清理面板任务队列(仅限指导下使用)", Hidden: true, - Action: cliService.ClearTask, + Action: route.cli.ClearTask, }, { Name: "init", Usage: "初始化面板(仅限指导下使用)", Hidden: true, - Action: cliService.Init, + Action: route.cli.Init, }, } } diff --git a/internal/route/http.go b/internal/route/http.go index e1baa884..3d11f876 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -14,274 +14,333 @@ import ( "github.com/TheTNB/panel/internal/service" ) -func Http(r chi.Router) { +type Http struct { + user *service.UserService + dashboard *service.DashboardService + task *service.TaskService + website *service.WebsiteService + database *service.DatabaseService + databaseServer *service.DatabaseServerService + databaseUser *service.DatabaseUserService + backup *service.BackupService + cert *service.CertService + certDNS *service.CertDNSService + certAccount *service.CertAccountService + app *service.AppService + cron *service.CronService + process *service.ProcessService + safe *service.SafeService + firewall *service.FirewallService + ssh *service.SSHService + container *service.ContainerService + containerNetwork *service.ContainerNetworkService + containerImage *service.ContainerImageService + containerVolume *service.ContainerVolumeService + file *service.FileService + monitor *service.MonitorService + setting *service.SettingService + systemctl *service.SystemctlService +} + +func NewHttp( + user *service.UserService, + dashboard *service.DashboardService, + task *service.TaskService, + website *service.WebsiteService, + database *service.DatabaseService, + databaseServer *service.DatabaseServerService, + databaseUser *service.DatabaseUserService, + backup *service.BackupService, + cert *service.CertService, + certDNS *service.CertDNSService, + certAccount *service.CertAccountService, + app *service.AppService, + cron *service.CronService, + process *service.ProcessService, + safe *service.SafeService, + firewall *service.FirewallService, + ssh *service.SSHService, + container *service.ContainerService, + containerNetwork *service.ContainerNetworkService, + containerImage *service.ContainerImageService, + containerVolume *service.ContainerVolumeService, + file *service.FileService, + monitor *service.MonitorService, + setting *service.SettingService, + systemctl *service.SystemctlService, +) *Http { + return &Http{ + user: user, + dashboard: dashboard, + task: task, + website: website, + database: database, + databaseServer: databaseServer, + databaseUser: databaseUser, + backup: backup, + cert: cert, + certDNS: certDNS, + certAccount: certAccount, + app: app, + cron: cron, + process: process, + safe: safe, + firewall: firewall, + ssh: ssh, + container: container, + containerNetwork: containerNetwork, + containerImage: containerImage, + containerVolume: containerVolume, + file: file, + monitor: monitor, + setting: setting, + systemctl: systemctl, + } +} + +func (route *Http) Register(r *chi.Mux) { r.Route("/api", func(r chi.Router) { r.Route("/user", func(r chi.Router) { - user := service.NewUserService() - r.Get("/key", user.GetKey) - r.With(middleware.Throttle(5, time.Minute)).Post("/login", user.Login) - r.Post("/logout", user.Logout) - r.Get("/isLogin", user.IsLogin) - r.Get("/info", user.Info) + r.Get("/key", route.user.GetKey) + r.With(middleware.Throttle(5, time.Minute)).Post("/login", route.user.Login) + r.Post("/logout", route.user.Logout) + r.Get("/isLogin", route.user.IsLogin) + r.Get("/info", route.user.Info) }) r.Route("/dashboard", func(r chi.Router) { - dashboard := service.NewDashboardService() - r.Get("/panel", dashboard.Panel) - r.Get("/homeApps", dashboard.HomeApps) - r.Post("/current", dashboard.Current) - r.Get("/systemInfo", dashboard.SystemInfo) - r.Get("/countInfo", dashboard.CountInfo) - r.Get("/installedDbAndPhp", dashboard.InstalledDbAndPhp) - r.Get("/checkUpdate", dashboard.CheckUpdate) - r.Get("/updateInfo", dashboard.UpdateInfo) - r.Post("/update", dashboard.Update) - r.Post("/restart", dashboard.Restart) + r.Get("/panel", route.dashboard.Panel) + r.Get("/homeApps", route.dashboard.HomeApps) + r.Post("/current", route.dashboard.Current) + r.Get("/systemInfo", route.dashboard.SystemInfo) + r.Get("/countInfo", route.dashboard.CountInfo) + r.Get("/installedDbAndPhp", route.dashboard.InstalledDbAndPhp) + r.Get("/checkUpdate", route.dashboard.CheckUpdate) + r.Get("/updateInfo", route.dashboard.UpdateInfo) + r.Post("/update", route.dashboard.Update) + r.Post("/restart", route.dashboard.Restart) }) r.Route("/task", func(r chi.Router) { - task := service.NewTaskService() - r.Get("/status", task.Status) - r.Get("/", task.List) - r.Get("/{id}", task.Get) - r.Delete("/{id}", task.Delete) + r.Get("/status", route.task.Status) + r.Get("/", route.task.List) + r.Get("/{id}", route.task.Get) + r.Delete("/{id}", route.task.Delete) }) r.Route("/website", func(r chi.Router) { - website := service.NewWebsiteService() - r.Get("/rewrites", website.GetRewrites) - r.Get("/defaultConfig", website.GetDefaultConfig) - r.Post("/defaultConfig", website.UpdateDefaultConfig) - r.Get("/", website.List) - r.Post("/", website.Create) - r.Get("/{id}", website.Get) - r.Put("/{id}", website.Update) - r.Delete("/{id}", website.Delete) - r.Delete("/{id}/log", website.ClearLog) - r.Post("/{id}/updateRemark", website.UpdateRemark) - r.Post("/{id}/resetConfig", website.ResetConfig) - r.Post("/{id}/status", website.UpdateStatus) - r.Post("/{id}/obtainCert", website.ObtainCert) + r.Get("/rewrites", route.website.GetRewrites) + r.Get("/defaultConfig", route.website.GetDefaultConfig) + r.Post("/defaultConfig", route.website.UpdateDefaultConfig) + r.Get("/", route.website.List) + r.Post("/", route.website.Create) + r.Get("/{id}", route.website.Get) + r.Put("/{id}", route.website.Update) + r.Delete("/{id}", route.website.Delete) + r.Delete("/{id}/log", route.website.ClearLog) + r.Post("/{id}/updateRemark", route.website.UpdateRemark) + r.Post("/{id}/resetConfig", route.website.ResetConfig) + r.Post("/{id}/status", route.website.UpdateStatus) + r.Post("/{id}/obtainCert", route.website.ObtainCert) }) r.Route("/database", func(r chi.Router) { - database := service.NewDatabaseService() - r.Get("/", database.List) - r.Post("/", database.Create) - r.Delete("/", database.Delete) - r.Post("/comment", database.Comment) + r.Get("/", route.database.List) + r.Post("/", route.database.Create) + r.Delete("/", route.database.Delete) + r.Post("/comment", route.database.Comment) }) r.Route("/databaseServer", func(r chi.Router) { - server := service.NewDatabaseServerService() - r.Get("/", server.List) - r.Post("/", server.Create) - r.Get("/{id}", server.Get) - r.Put("/{id}", server.Update) - r.Put("/{id}/remark", server.UpdateRemark) - r.Delete("/{id}", server.Delete) - r.Post("/{id}/sync", server.Sync) + r.Get("/", route.databaseServer.List) + r.Post("/", route.databaseServer.Create) + r.Get("/{id}", route.databaseServer.Get) + r.Put("/{id}", route.databaseServer.Update) + r.Put("/{id}/remark", route.databaseServer.UpdateRemark) + r.Delete("/{id}", route.databaseServer.Delete) + r.Post("/{id}/sync", route.databaseServer.Sync) }) r.Route("/databaseUser", func(r chi.Router) { - user := service.NewDatabaseUserService() - r.Get("/", user.List) - r.Post("/", user.Create) - r.Get("/{id}", user.Get) - r.Put("/{id}", user.Update) - r.Put("/{id}/remark", user.UpdateRemark) - r.Delete("/{id}", user.Delete) + r.Get("/", route.databaseUser.List) + r.Post("/", route.databaseUser.Create) + r.Get("/{id}", route.databaseUser.Get) + r.Put("/{id}", route.databaseUser.Update) + r.Put("/{id}/remark", route.databaseUser.UpdateRemark) + r.Delete("/{id}", route.databaseUser.Delete) }) r.Route("/backup", func(r chi.Router) { - backup := service.NewBackupService() - r.Get("/{type}", backup.List) - r.Post("/{type}", backup.Create) - r.Post("/{type}/upload", backup.Upload) - r.Delete("/{type}/delete", backup.Delete) - r.Post("/{type}/restore", backup.Restore) + r.Get("/{type}", route.backup.List) + r.Post("/{type}", route.backup.Create) + r.Post("/{type}/upload", route.backup.Upload) + r.Delete("/{type}/delete", route.backup.Delete) + r.Post("/{type}/restore", route.backup.Restore) }) r.Route("/cert", func(r chi.Router) { - cert := service.NewCertService() - r.Get("/caProviders", cert.CAProviders) - r.Get("/dnsProviders", cert.DNSProviders) - r.Get("/algorithms", cert.Algorithms) + r.Get("/caProviders", route.cert.CAProviders) + r.Get("/dnsProviders", route.cert.DNSProviders) + r.Get("/algorithms", route.cert.Algorithms) r.Route("/cert", func(r chi.Router) { - r.Get("/", cert.List) - r.Post("/", cert.Create) - r.Post("/upload", cert.Upload) - r.Put("/{id}", cert.Update) - r.Get("/{id}", cert.Get) - r.Delete("/{id}", cert.Delete) - r.Post("/{id}/obtainAuto", cert.ObtainAuto) - r.Post("/{id}/obtainManual", cert.ObtainManual) - r.Post("/{id}/obtainSelfSigned", cert.ObtainSelfSigned) - r.Post("/{id}/renew", cert.Renew) - r.Post("/{id}/manualDNS", cert.ManualDNS) - r.Post("/{id}/deploy", cert.Deploy) + r.Get("/", route.cert.List) + r.Post("/", route.cert.Create) + r.Post("/upload", route.cert.Upload) + r.Put("/{id}", route.cert.Update) + r.Get("/{id}", route.cert.Get) + r.Delete("/{id}", route.cert.Delete) + r.Post("/{id}/obtainAuto", route.cert.ObtainAuto) + r.Post("/{id}/obtainManual", route.cert.ObtainManual) + r.Post("/{id}/obtainSelfSigned", route.cert.ObtainSelfSigned) + r.Post("/{id}/renew", route.cert.Renew) + r.Post("/{id}/manualDNS", route.cert.ManualDNS) + r.Post("/{id}/deploy", route.cert.Deploy) }) r.Route("/dns", func(r chi.Router) { - certDNS := service.NewCertDNSService() - r.Get("/", certDNS.List) - r.Post("/", certDNS.Create) - r.Put("/{id}", certDNS.Update) - r.Get("/{id}", certDNS.Get) - r.Delete("/{id}", certDNS.Delete) + r.Get("/", route.certDNS.List) + r.Post("/", route.certDNS.Create) + r.Put("/{id}", route.certDNS.Update) + r.Get("/{id}", route.certDNS.Get) + r.Delete("/{id}", route.certDNS.Delete) }) r.Route("/account", func(r chi.Router) { - certAccount := service.NewCertAccountService() - r.Get("/", certAccount.List) - r.Post("/", certAccount.Create) - r.Put("/{id}", certAccount.Update) - r.Get("/{id}", certAccount.Get) - r.Delete("/{id}", certAccount.Delete) + r.Get("/", route.certAccount.List) + r.Post("/", route.certAccount.Create) + r.Put("/{id}", route.certAccount.Update) + r.Get("/{id}", route.certAccount.Get) + r.Delete("/{id}", route.certAccount.Delete) }) }) r.Route("/app", func(r chi.Router) { - app := service.NewAppService() - r.Get("/list", app.List) - r.Post("/install", app.Install) - r.Post("/uninstall", app.Uninstall) - r.Post("/update", app.Update) - r.Post("/updateShow", app.UpdateShow) - r.Get("/isInstalled", app.IsInstalled) - r.Get("/updateCache", app.UpdateCache) + r.Get("/list", route.app.List) + r.Post("/install", route.app.Install) + r.Post("/uninstall", route.app.Uninstall) + r.Post("/update", route.app.Update) + r.Post("/updateShow", route.app.UpdateShow) + r.Get("/isInstalled", route.app.IsInstalled) + r.Get("/updateCache", route.app.UpdateCache) }) r.Route("/cron", func(r chi.Router) { - cron := service.NewCronService() - r.Get("/", cron.List) - r.Post("/", cron.Create) - r.Put("/{id}", cron.Update) - r.Get("/{id}", cron.Get) - r.Delete("/{id}", cron.Delete) - r.Post("/{id}/status", cron.Status) + r.Get("/", route.cron.List) + r.Post("/", route.cron.Create) + r.Put("/{id}", route.cron.Update) + r.Get("/{id}", route.cron.Get) + r.Delete("/{id}", route.cron.Delete) + r.Post("/{id}/status", route.cron.Status) }) r.Route("/process", func(r chi.Router) { - process := service.NewProcessService() - r.Get("/", process.List) - r.Post("/kill", process.Kill) + r.Get("/", route.process.List) + r.Post("/kill", route.process.Kill) }) r.Route("/safe", func(r chi.Router) { - safe := service.NewSafeService() - r.Get("/ssh", safe.GetSSH) - r.Post("/ssh", safe.UpdateSSH) - r.Get("/ping", safe.GetPingStatus) - r.Post("/ping", safe.UpdatePingStatus) + r.Get("/ssh", route.safe.GetSSH) + r.Post("/ssh", route.safe.UpdateSSH) + r.Get("/ping", route.safe.GetPingStatus) + r.Post("/ping", route.safe.UpdatePingStatus) }) r.Route("/firewall", func(r chi.Router) { - firewall := service.NewFirewallService() - r.Get("/status", firewall.GetStatus) - r.Post("/status", firewall.UpdateStatus) - r.Get("/rule", firewall.GetRules) - r.Post("/rule", firewall.CreateRule) - r.Delete("/rule", firewall.DeleteRule) - r.Get("/ipRule", firewall.GetIPRules) - r.Post("/ipRule", firewall.CreateIPRule) - r.Delete("/ipRule", firewall.DeleteIPRule) - r.Get("/forward", firewall.GetForwards) - r.Post("/forward", firewall.CreateForward) - r.Delete("/forward", firewall.DeleteForward) + r.Get("/status", route.firewall.GetStatus) + r.Post("/status", route.firewall.UpdateStatus) + r.Get("/rule", route.firewall.GetRules) + r.Post("/rule", route.firewall.CreateRule) + r.Delete("/rule", route.firewall.DeleteRule) + r.Get("/ipRule", route.firewall.GetIPRules) + r.Post("/ipRule", route.firewall.CreateIPRule) + r.Delete("/ipRule", route.firewall.DeleteIPRule) + r.Get("/forward", route.firewall.GetForwards) + r.Post("/forward", route.firewall.CreateForward) + r.Delete("/forward", route.firewall.DeleteForward) }) r.Route("/ssh", func(r chi.Router) { - ssh := service.NewSSHService() - r.Get("/", ssh.List) - r.Post("/", ssh.Create) - r.Put("/{id}", ssh.Update) - r.Get("/{id}", ssh.Get) - r.Delete("/{id}", ssh.Delete) + r.Get("/", route.ssh.List) + r.Post("/", route.ssh.Create) + r.Put("/{id}", route.ssh.Update) + r.Get("/{id}", route.ssh.Get) + r.Delete("/{id}", route.ssh.Delete) }) r.Route("/container", func(r chi.Router) { r.Route("/container", func(r chi.Router) { - container := service.NewContainerService() - r.Get("/", container.List) - r.Get("/search", container.Search) - r.Post("/", container.Create) - r.Delete("/{id}", container.Remove) - r.Post("/{id}/start", container.Start) - r.Post("/{id}/stop", container.Stop) - r.Post("/{id}/restart", container.Restart) - r.Post("/{id}/pause", container.Pause) - r.Post("/{id}/unpause", container.Unpause) - r.Post("/{id}/kill", container.Kill) - r.Post("/{id}/rename", container.Rename) - r.Get("/{id}/logs", container.Logs) - r.Post("/prune", container.Prune) + r.Get("/", route.container.List) + r.Get("/search", route.container.Search) + r.Post("/", route.container.Create) + r.Delete("/{id}", route.container.Remove) + r.Post("/{id}/start", route.container.Start) + r.Post("/{id}/stop", route.container.Stop) + r.Post("/{id}/restart", route.container.Restart) + r.Post("/{id}/pause", route.container.Pause) + r.Post("/{id}/unpause", route.container.Unpause) + r.Post("/{id}/kill", route.container.Kill) + r.Post("/{id}/rename", route.container.Rename) + r.Get("/{id}/logs", route.container.Logs) + r.Post("/prune", route.container.Prune) }) r.Route("/network", func(r chi.Router) { - containerNetwork := service.NewContainerNetworkService() - r.Get("/", containerNetwork.List) - r.Post("/", containerNetwork.Create) - r.Delete("/{id}", containerNetwork.Remove) - r.Post("/prune", containerNetwork.Prune) + r.Get("/", route.containerNetwork.List) + r.Post("/", route.containerNetwork.Create) + r.Delete("/{id}", route.containerNetwork.Remove) + r.Post("/prune", route.containerNetwork.Prune) }) r.Route("/image", func(r chi.Router) { - containerImage := service.NewContainerImageService() - r.Get("/", containerImage.List) - r.Post("/", containerImage.Pull) - r.Delete("/{id}", containerImage.Remove) - r.Post("/prune", containerImage.Prune) + r.Get("/", route.containerImage.List) + r.Post("/", route.containerImage.Pull) + r.Delete("/{id}", route.containerImage.Remove) + r.Post("/prune", route.containerImage.Prune) }) r.Route("/volume", func(r chi.Router) { - containerVolume := service.NewContainerVolumeService() - r.Get("/", containerVolume.List) - r.Post("/", containerVolume.Create) - r.Delete("/{id}", containerVolume.Remove) - r.Post("/prune", containerVolume.Prune) + r.Get("/", route.containerVolume.List) + r.Post("/", route.containerVolume.Create) + r.Delete("/{id}", route.containerVolume.Remove) + r.Post("/prune", route.containerVolume.Prune) }) }) r.Route("/file", func(r chi.Router) { - file := service.NewFileService() - r.Post("/create", file.Create) - r.Get("/content", file.Content) - r.Post("/save", file.Save) - r.Post("/delete", file.Delete) - r.Post("/upload", file.Upload) - r.Post("/exist", file.Exist) - r.Post("/move", file.Move) - r.Post("/copy", file.Copy) - r.Get("/download", file.Download) - r.Post("/remoteDownload", file.RemoteDownload) - r.Get("/info", file.Info) - r.Post("/permission", file.Permission) - r.Post("/compress", file.Compress) - r.Post("/unCompress", file.UnCompress) - r.Get("/search", file.Search) - r.Get("/list", file.List) + r.Post("/create", route.file.Create) + r.Get("/content", route.file.Content) + r.Post("/save", route.file.Save) + r.Post("/delete", route.file.Delete) + r.Post("/upload", route.file.Upload) + r.Post("/exist", route.file.Exist) + r.Post("/move", route.file.Move) + r.Post("/copy", route.file.Copy) + r.Get("/download", route.file.Download) + r.Post("/remoteDownload", route.file.RemoteDownload) + r.Get("/info", route.file.Info) + r.Post("/permission", route.file.Permission) + r.Post("/compress", route.file.Compress) + r.Post("/unCompress", route.file.UnCompress) + r.Get("/search", route.file.Search) + r.Get("/list", route.file.List) }) r.Route("/monitor", func(r chi.Router) { - monitor := service.NewMonitorService() - r.Get("/setting", monitor.GetSetting) - r.Post("/setting", monitor.UpdateSetting) - r.Post("/clear", monitor.Clear) - r.Get("/list", monitor.List) + r.Get("/setting", route.monitor.GetSetting) + r.Post("/setting", route.monitor.UpdateSetting) + r.Post("/clear", route.monitor.Clear) + r.Get("/list", route.monitor.List) }) r.Route("/setting", func(r chi.Router) { - setting := service.NewSettingService() - r.Get("/", setting.Get) - r.Post("/", setting.Update) + r.Get("/", route.setting.Get) + r.Post("/", route.setting.Update) }) r.Route("/systemctl", func(r chi.Router) { - systemctl := service.NewSystemctlService() - r.Get("/status", systemctl.Status) - r.Get("/isEnabled", systemctl.IsEnabled) - r.Post("/enable", systemctl.Enable) - r.Post("/disable", systemctl.Disable) - r.Post("/restart", systemctl.Restart) - r.Post("/reload", systemctl.Reload) - r.Post("/start", systemctl.Start) - r.Post("/stop", systemctl.Stop) + r.Get("/status", route.systemctl.Status) + r.Get("/isEnabled", route.systemctl.IsEnabled) + r.Post("/enable", route.systemctl.Enable) + r.Post("/disable", route.systemctl.Disable) + r.Post("/restart", route.systemctl.Restart) + r.Post("/reload", route.systemctl.Reload) + r.Post("/start", route.systemctl.Start) + r.Post("/stop", route.systemctl.Stop) }) r.Route("/apps", func(r chi.Router) { diff --git a/internal/route/route.go b/internal/route/route.go new file mode 100644 index 00000000..b639d4a0 --- /dev/null +++ b/internal/route/route.go @@ -0,0 +1,6 @@ +package route + +import "github.com/google/wire" + +// ProviderSet is route providers. +var ProviderSet = wire.NewSet(NewCli, NewHttp, NewWs) diff --git a/internal/route/ws.go b/internal/route/ws.go index 6022a99b..4b1b53e7 100644 --- a/internal/route/ws.go +++ b/internal/route/ws.go @@ -3,15 +3,22 @@ package route import ( "github.com/go-chi/chi/v5" - "github.com/TheTNB/panel/internal/http/middleware" "github.com/TheTNB/panel/internal/service" ) -func Ws(r chi.Router) { +type Ws struct { + ws *service.WsService +} + +func NewWs(ws *service.WsService) *Ws { + return &Ws{ + ws: ws, + } +} + +func (route *Ws) Register(r *chi.Mux) { r.Route("/api/ws", func(r chi.Router) { - r.Use(middleware.MustLogin) - ws := service.NewWsService() - r.Get("/ssh", ws.Session) - r.Get("/exec", ws.Exec) + r.Get("/ssh", route.ws.Session) + r.Get("/exec", route.ws.Exec) }) } diff --git a/internal/service/app.go b/internal/service/app.go index d43e205a..88078b1d 100644 --- a/internal/service/app.go +++ b/internal/service/app.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/types" ) @@ -17,11 +16,11 @@ type AppService struct { settingRepo biz.SettingRepo } -func NewAppService() *AppService { +func NewAppService(app biz.AppRepo, cache biz.CacheRepo, setting biz.SettingRepo) *AppService { return &AppService{ - appRepo: data.NewAppRepo(), - cacheRepo: data.NewCacheRepo(), - settingRepo: data.NewSettingRepo(), + appRepo: app, + cacheRepo: cache, + settingRepo: setting, } } diff --git a/internal/service/backup.go b/internal/service/backup.go index fd5b5d46..07bddcef 100644 --- a/internal/service/backup.go +++ b/internal/service/backup.go @@ -9,7 +9,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/io" ) @@ -18,9 +17,9 @@ type BackupService struct { backupRepo biz.BackupRepo } -func NewBackupService() *BackupService { +func NewBackupService(backup biz.BackupRepo) *BackupService { return &BackupService{ - backupRepo: data.NewBackupRepo(), + backupRepo: backup, } } diff --git a/internal/service/cert.go b/internal/service/cert.go index 609b7249..f30d6e78 100644 --- a/internal/service/cert.go +++ b/internal/service/cert.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/acme" "github.com/TheTNB/panel/pkg/types" @@ -16,9 +15,9 @@ type CertService struct { certRepo biz.CertRepo } -func NewCertService() *CertService { +func NewCertService(cert biz.CertRepo) *CertService { return &CertService{ - certRepo: data.NewCertRepo(), + certRepo: cert, } } diff --git a/internal/service/cert_account.go b/internal/service/cert_account.go index 41a9ca33..88619a5a 100644 --- a/internal/service/cert_account.go +++ b/internal/service/cert_account.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type CertAccountService struct { certAccountRepo biz.CertAccountRepo } -func NewCertAccountService() *CertAccountService { +func NewCertAccountService(certAccount biz.CertAccountRepo) *CertAccountService { return &CertAccountService{ - certAccountRepo: data.NewCertAccountRepo(), + certAccountRepo: certAccount, } } diff --git a/internal/service/cert_dns.go b/internal/service/cert_dns.go index 43b8f16c..1ac92f61 100644 --- a/internal/service/cert_dns.go +++ b/internal/service/cert_dns.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type CertDNSService struct { certDNSRepo biz.CertDNSRepo } -func NewCertDNSService() *CertDNSService { +func NewCertDNSService(certDNS biz.CertDNSRepo) *CertDNSService { return &CertDNSService{ - certDNSRepo: data.NewCertDNSRepo(), + certDNSRepo: certDNS, } } diff --git a/internal/service/cli.go b/internal/service/cli.go index 5c1257c3..4557538e 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/knadh/koanf/v2" "path/filepath" "time" @@ -17,7 +18,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/api" "github.com/TheTNB/panel/pkg/cert" @@ -33,6 +33,7 @@ import ( type CliService struct { hr string api *api.API + conf *koanf.Koanf appRepo biz.AppRepo cacheRepo biz.CacheRepo userRepo biz.UserRepo @@ -43,17 +44,18 @@ type CliService struct { hash hash.Hasher } -func NewCliService() *CliService { +func NewCliService(conf *koanf.Koanf, 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), - appRepo: data.NewAppRepo(), - cacheRepo: data.NewCacheRepo(), - userRepo: data.NewUserRepo(), - settingRepo: data.NewSettingRepo(), - backupRepo: data.NewBackupRepo(), - websiteRepo: data.NewWebsiteRepo(), - databaseServerRepo: data.NewDatabaseServerRepo(), + conf: conf, + appRepo: appRepo, + cacheRepo: cache, + userRepo: user, + settingRepo: setting, + backupRepo: backup, + websiteRepo: website, + databaseServerRepo: databaseServer, hash: hash.NewArgon2id(), } } @@ -97,7 +99,7 @@ func (s *CliService) Update(ctx context.Context, cmd *cli.Command) error { } ver, url, checksum := panel.Version, download.URL, download.Checksum - return s.settingRepo.UpdatePanel(ver, url, checksum) + return s.backupRepo.UpdatePanel(ver, url, checksum) } func (s *CliService) Sync(ctx context.Context, cmd *cli.Command) error { @@ -113,7 +115,7 @@ func (s *CliService) Sync(ctx context.Context, cmd *cli.Command) error { } func (s *CliService) Fix(ctx context.Context, cmd *cli.Command) error { - return s.settingRepo.FixPanel() + return s.backupRepo.FixPanel() } func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error { @@ -138,15 +140,15 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error { } protocol := "http" - if app.Conf.Bool("http.tls") { + if s.conf.Bool("http.tls") { protocol = "https" } - port := app.Conf.String("http.port") + port := s.conf.String("http.port") if port == "" { return fmt.Errorf("端口获取失败") } - entrance := app.Conf.String("http.entrance") + entrance := s.conf.String("http.entrance") if entrance == "" { return fmt.Errorf("入口获取失败") } @@ -831,8 +833,7 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("初始化失败:%v", err) } - user := data.NewUserRepo() - _, err = user.Create("admin", value) + _, err = s.userRepo.Create("admin", value) if err != nil { return fmt.Errorf("初始化失败:%v", err) } diff --git a/internal/service/container.go b/internal/service/container.go index ed5d651b..c11aefe1 100644 --- a/internal/service/container.go +++ b/internal/service/container.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type ContainerService struct { containerRepo biz.ContainerRepo } -func NewContainerService() *ContainerService { +func NewContainerService(container biz.ContainerRepo) *ContainerService { return &ContainerService{ - containerRepo: data.NewContainerRepo(), + containerRepo: container, } } diff --git a/internal/service/container_image.go b/internal/service/container_image.go index bf8f2c87..5daa33a3 100644 --- a/internal/service/container_image.go +++ b/internal/service/container_image.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type ContainerImageService struct { containerImageRepo biz.ContainerImageRepo } -func NewContainerImageService() *ContainerImageService { +func NewContainerImageService(containerImage biz.ContainerImageRepo) *ContainerImageService { return &ContainerImageService{ - containerImageRepo: data.NewContainerImageRepo(), + containerImageRepo: containerImage, } } diff --git a/internal/service/container_network.go b/internal/service/container_network.go index 867c1815..cbee3632 100644 --- a/internal/service/container_network.go +++ b/internal/service/container_network.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type ContainerNetworkService struct { containerNetworkRepo biz.ContainerNetworkRepo } -func NewContainerNetworkService() *ContainerNetworkService { +func NewContainerNetworkService(containerNetwork biz.ContainerNetworkRepo) *ContainerNetworkService { return &ContainerNetworkService{ - containerNetworkRepo: data.NewContainerNetworkRepo(), + containerNetworkRepo: containerNetwork, } } diff --git a/internal/service/container_volume.go b/internal/service/container_volume.go index d361b158..2493b727 100644 --- a/internal/service/container_volume.go +++ b/internal/service/container_volume.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type ContainerVolumeService struct { containerVolumeRepo biz.ContainerVolumeRepo } -func NewContainerVolumeService() *ContainerVolumeService { +func NewContainerVolumeService(containerVolume biz.ContainerVolumeRepo) *ContainerVolumeService { return &ContainerVolumeService{ - containerVolumeRepo: data.NewContainerVolumeRepo(), + containerVolumeRepo: containerVolume, } } diff --git a/internal/service/cron.go b/internal/service/cron.go index 835140ae..8a2be46a 100644 --- a/internal/service/cron.go +++ b/internal/service/cron.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type CronService struct { cronRepo biz.CronRepo } -func NewCronService() *CronService { +func NewCronService(cron biz.CronRepo) *CronService { return &CronService{ - cronRepo: data.NewCronRepo(), + cronRepo: cron, } } diff --git a/internal/service/dashboard.go b/internal/service/dashboard.go index bb02be89..a6bc5660 100644 --- a/internal/service/dashboard.go +++ b/internal/service/dashboard.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "github.com/knadh/koanf/v2" "net" "net/http" "regexp" @@ -16,7 +17,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/api" "github.com/TheTNB/panel/pkg/db" @@ -27,21 +27,25 @@ import ( type DashboardService struct { api *api.API + conf *koanf.Koanf taskRepo biz.TaskRepo websiteRepo biz.WebsiteRepo appRepo biz.AppRepo settingRepo biz.SettingRepo cronRepo biz.CronRepo + backupRepo biz.BackupRepo } -func NewDashboardService() *DashboardService { +func NewDashboardService(conf *koanf.Koanf, task biz.TaskRepo, website biz.WebsiteRepo, appRepo biz.AppRepo, setting biz.SettingRepo, cron biz.CronRepo, backupRepo biz.BackupRepo) *DashboardService { return &DashboardService{ api: api.NewAPI(app.Version), - taskRepo: data.NewTaskRepo(), - websiteRepo: data.NewWebsiteRepo(), - appRepo: data.NewAppRepo(), - settingRepo: data.NewSettingRepo(), - cronRepo: data.NewCronRepo(), + conf: conf, + taskRepo: task, + websiteRepo: website, + appRepo: appRepo, + settingRepo: setting, + cronRepo: cron, + backupRepo: backupRepo, } } @@ -53,7 +57,7 @@ func (s *DashboardService) Panel(w http.ResponseWriter, r *http.Request) { Success(w, chix.M{ "name": name, - "locale": app.Conf.MustString("app.locale"), + "locale": s.conf.String("app.locale"), }) } @@ -309,7 +313,7 @@ func (s *DashboardService) Update(w http.ResponseWriter, r *http.Request) { ver, url, checksum := panel.Version, download.URL, download.Checksum app.Status = app.StatusUpgrade - if err = s.settingRepo.UpdatePanel(ver, url, checksum); err != nil { + if err = s.backupRepo.UpdatePanel(ver, url, checksum); err != nil { app.Status = app.StatusFailed Error(w, http.StatusInternalServerError, "%v", err) return diff --git a/internal/service/database.go b/internal/service/database.go index 2c32c978..87dc6e21 100644 --- a/internal/service/database.go +++ b/internal/service/database.go @@ -6,21 +6,20 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) -type Database struct { +type DatabaseService struct { databaseRepo biz.DatabaseRepo } -func NewDatabaseService() *Database { - return &Database{ - databaseRepo: data.NewDatabaseRepo(), +func NewDatabaseService(database biz.DatabaseRepo) *DatabaseService { + return &DatabaseService{ + databaseRepo: database, } } -func (s *Database) List(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseService) List(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.Paginate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -39,7 +38,7 @@ func (s *Database) List(w http.ResponseWriter, r *http.Request) { }) } -func (s *Database) Create(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseService) Create(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseCreate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -54,7 +53,7 @@ func (s *Database) Create(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *Database) Delete(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseService) Delete(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseDelete](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -69,7 +68,7 @@ func (s *Database) Delete(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *Database) Comment(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseService) Comment(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseComment](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) diff --git a/internal/service/database_server.go b/internal/service/database_server.go index 321ae4ef..abb529fb 100644 --- a/internal/service/database_server.go +++ b/internal/service/database_server.go @@ -6,21 +6,20 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) -type DatabaseServer struct { +type DatabaseServerService struct { databaseServerRepo biz.DatabaseServerRepo } -func NewDatabaseServerService() *DatabaseServer { - return &DatabaseServer{ - databaseServerRepo: data.NewDatabaseServerRepo(), +func NewDatabaseServerService(databaseServer biz.DatabaseServerRepo) *DatabaseServerService { + return &DatabaseServerService{ + databaseServerRepo: databaseServer, } } -func (s *DatabaseServer) List(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) List(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.Paginate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -39,7 +38,7 @@ func (s *DatabaseServer) List(w http.ResponseWriter, r *http.Request) { }) } -func (s *DatabaseServer) Create(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) Create(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseServerCreate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -54,7 +53,7 @@ func (s *DatabaseServer) Create(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseServer) Get(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) Get(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -70,7 +69,7 @@ func (s *DatabaseServer) Get(w http.ResponseWriter, r *http.Request) { Success(w, server) } -func (s *DatabaseServer) Update(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) Update(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseServerUpdate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -85,7 +84,7 @@ func (s *DatabaseServer) Update(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseServer) UpdateRemark(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) UpdateRemark(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseServerUpdateRemark](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -100,7 +99,7 @@ func (s *DatabaseServer) UpdateRemark(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseServer) Delete(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) Delete(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -115,7 +114,7 @@ func (s *DatabaseServer) Delete(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseServer) Sync(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseServerService) Sync(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) diff --git a/internal/service/database_user.go b/internal/service/database_user.go index c3665ad7..3bcdeebb 100644 --- a/internal/service/database_user.go +++ b/internal/service/database_user.go @@ -6,21 +6,20 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) -type DatabaseUser struct { +type DatabaseUserService struct { databaseUserRepo biz.DatabaseUserRepo } -func NewDatabaseUserService() *DatabaseUser { - return &DatabaseUser{ - databaseUserRepo: data.NewDatabaseUserRepo(), +func NewDatabaseUserService(databaseUser biz.DatabaseUserRepo) *DatabaseUserService { + return &DatabaseUserService{ + databaseUserRepo: databaseUser, } } -func (s *DatabaseUser) List(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) List(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.Paginate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -39,7 +38,7 @@ func (s *DatabaseUser) List(w http.ResponseWriter, r *http.Request) { }) } -func (s *DatabaseUser) Create(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) Create(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseUserCreate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -54,7 +53,7 @@ func (s *DatabaseUser) Create(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseUser) Get(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) Get(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -70,7 +69,7 @@ func (s *DatabaseUser) Get(w http.ResponseWriter, r *http.Request) { Success(w, user) } -func (s *DatabaseUser) Update(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) Update(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseUserUpdate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -85,7 +84,7 @@ func (s *DatabaseUser) Update(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseUser) UpdateRemark(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) UpdateRemark(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.DatabaseUserUpdateRemark](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) @@ -100,7 +99,7 @@ func (s *DatabaseUser) UpdateRemark(w http.ResponseWriter, r *http.Request) { Success(w, nil) } -func (s *DatabaseUser) Delete(w http.ResponseWriter, r *http.Request) { +func (s *DatabaseUserService) Delete(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) diff --git a/internal/service/file.go b/internal/service/file.go index fdf5762d..dfbd018b 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -21,7 +21,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/os" @@ -33,9 +32,9 @@ type FileService struct { taskRepo biz.TaskRepo } -func NewFileService() *FileService { +func NewFileService(task biz.TaskRepo) *FileService { return &FileService{ - taskRepo: data.NewTaskRepo(), + taskRepo: task, } } diff --git a/internal/service/file_windows.go b/internal/service/file_windows.go index 835e2111..1045ae87 100644 --- a/internal/service/file_windows.go +++ b/internal/service/file_windows.go @@ -18,6 +18,7 @@ import ( "github.com/go-rat/chix" "github.com/spf13/cast" + "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/shell" @@ -25,10 +26,13 @@ import ( ) type FileService struct { + taskRepo biz.TaskRepo } -func NewFileService() *FileService { - return &FileService{} +func NewFileService(task biz.TaskRepo) *FileService { + return &FileService{ + taskRepo: task, + } } func (s *FileService) Create(w http.ResponseWriter, r *http.Request) { diff --git a/internal/service/monitor.go b/internal/service/monitor.go index c7ee3b0b..e57c8d7d 100644 --- a/internal/service/monitor.go +++ b/internal/service/monitor.go @@ -6,7 +6,6 @@ import ( "time" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/types" ) @@ -16,10 +15,10 @@ type MonitorService struct { monitorRepo biz.MonitorRepo } -func NewMonitorService() *MonitorService { +func NewMonitorService(setting biz.SettingRepo, monitor biz.MonitorRepo) *MonitorService { return &MonitorService{ - settingRepo: data.NewSettingRepo(), - monitorRepo: data.NewMonitorRepo(), + settingRepo: setting, + monitorRepo: monitor, } } diff --git a/internal/service/safe.go b/internal/service/safe.go index bbc6692d..12726f3a 100644 --- a/internal/service/safe.go +++ b/internal/service/safe.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type SafeService struct { safeRepo biz.SafeRepo } -func NewSafeService() *SafeService { +func NewSafeService(safe biz.SafeRepo) *SafeService { return &SafeService{ - safeRepo: data.NewSafeRepo(), + safeRepo: safe, } } diff --git a/internal/service/service.go b/internal/service/service.go new file mode 100644 index 00000000..acb0d242 --- /dev/null +++ b/internal/service/service.go @@ -0,0 +1,34 @@ +package service + +import "github.com/google/wire" + +// ProviderSet is service providers. +var ProviderSet = wire.NewSet( + NewAppService, + NewBackupService, + NewCertService, + NewCertAccountService, + NewCertDNSService, + NewCliService, + NewContainerService, + NewContainerImageService, + NewContainerNetworkService, + NewContainerVolumeService, + NewCronService, + NewDashboardService, + NewDatabaseService, + NewDatabaseServerService, + NewDatabaseUserService, + NewFileService, + NewFirewallService, + NewMonitorService, + NewProcessService, + NewSafeService, + NewSettingService, + NewSSHService, + NewSystemctlService, + NewTaskService, + NewUserService, + NewWebsiteService, + NewWsService, +) diff --git a/internal/service/setting.go b/internal/service/setting.go index 4e4773e7..1580275b 100644 --- a/internal/service/setting.go +++ b/internal/service/setting.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/tools" ) @@ -13,9 +12,9 @@ type SettingService struct { settingRepo biz.SettingRepo } -func NewSettingService() *SettingService { +func NewSettingService(setting biz.SettingRepo) *SettingService { return &SettingService{ - settingRepo: data.NewSettingRepo(), + settingRepo: setting, } } diff --git a/internal/service/ssh.go b/internal/service/ssh.go index e1bb12ac..ef3bfae0 100644 --- a/internal/service/ssh.go +++ b/internal/service/ssh.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type SSHService struct { sshRepo biz.SSHRepo } -func NewSSHService() *SSHService { +func NewSSHService(ssh biz.SSHRepo) *SSHService { return &SSHService{ - sshRepo: data.NewSSHRepo(), + sshRepo: ssh, } } diff --git a/internal/service/task.go b/internal/service/task.go index 3792ca47..36b449b1 100644 --- a/internal/service/task.go +++ b/internal/service/task.go @@ -6,7 +6,6 @@ import ( "github.com/go-rat/chix" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" ) @@ -14,9 +13,9 @@ type TaskService struct { taskRepo biz.TaskRepo } -func NewTaskService() *TaskService { +func NewTaskService(task biz.TaskRepo) *TaskService { return &TaskService{ - taskRepo: data.NewTaskRepo(), + taskRepo: task, } } diff --git a/internal/service/user.go b/internal/service/user.go index 11ab9ce6..29020ecf 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -4,6 +4,8 @@ import ( "crypto/rsa" "encoding/gob" "fmt" + "github.com/go-rat/sessions" + "github.com/knadh/koanf/v2" "net" "net/http" "strings" @@ -12,21 +14,23 @@ import ( "github.com/spf13/cast" "golang.org/x/crypto/sha3" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/rsacrypto" ) type UserService struct { - repo biz.UserRepo + conf *koanf.Koanf + session *sessions.Manager + userRepo biz.UserRepo } -func NewUserService() *UserService { +func NewUserService(conf *koanf.Koanf, session *sessions.Manager, user biz.UserRepo) *UserService { gob.Register(rsa.PrivateKey{}) // 必须注册 rsa.PrivateKey 类型否则无法反序列化 session 中的 key return &UserService{ - repo: data.NewUserRepo(), + conf: conf, + session: session, + userRepo: user, } } @@ -37,7 +41,7 @@ func (s *UserService) GetKey(w http.ResponseWriter, r *http.Request) { return } - sess, err := app.Session.GetSession(r) + sess, err := s.session.GetSession(r) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return @@ -54,7 +58,7 @@ func (s *UserService) GetKey(w http.ResponseWriter, r *http.Request) { } func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { - sess, err := app.Session.GetSession(r) + sess, err := s.session.GetSession(r) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return @@ -74,7 +78,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { decryptedUsername, _ := rsacrypto.DecryptData(&key, req.Username) decryptedPassword, _ := rsacrypto.DecryptData(&key, req.Password) - user, err := s.repo.CheckPassword(string(decryptedUsername), string(decryptedPassword)) + user, err := s.userRepo.CheckPassword(string(decryptedUsername), string(decryptedPassword)) if err != nil { Error(w, http.StatusForbidden, "%v", err) return @@ -87,7 +91,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { Error(w, http.StatusInternalServerError, "%v", err) return } - if req.SafeLogin && !app.Conf.Bool("http.tls") { + if req.SafeLogin && !s.conf.Bool("http.tls") { sess.Put("safe_login", true) sess.Put("safe_client", fmt.Sprintf("%x", sha3.Sum256([]byte(ip)))) } @@ -98,7 +102,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) { } func (s *UserService) Logout(w http.ResponseWriter, r *http.Request) { - sess, err := app.Session.GetSession(r) + sess, err := s.session.GetSession(r) if err == nil { if err = sess.Invalidate(); err != nil { Error(w, http.StatusInternalServerError, "%v", err) @@ -110,7 +114,7 @@ func (s *UserService) Logout(w http.ResponseWriter, r *http.Request) { } func (s *UserService) IsLogin(w http.ResponseWriter, r *http.Request) { - sess, err := app.Session.GetSession(r) + sess, err := s.session.GetSession(r) if err != nil { Success(w, false) return @@ -125,7 +129,7 @@ func (s *UserService) Info(w http.ResponseWriter, r *http.Request) { return } - user, err := s.repo.Get(userID) + user, err := s.userRepo.Get(userID) if err != nil { ErrorSystem(w) return diff --git a/internal/service/website.go b/internal/service/website.go index 50909fc1..81278a56 100644 --- a/internal/service/website.go +++ b/internal/service/website.go @@ -8,7 +8,6 @@ import ( "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/io" ) @@ -18,10 +17,10 @@ type WebsiteService struct { settingRepo biz.SettingRepo } -func NewWebsiteService() *WebsiteService { +func NewWebsiteService(website biz.WebsiteRepo, setting biz.SettingRepo) *WebsiteService { return &WebsiteService{ - websiteRepo: data.NewWebsiteRepo(), - settingRepo: data.NewSettingRepo(), + websiteRepo: website, + settingRepo: setting, } } diff --git a/internal/service/ws.go b/internal/service/ws.go index 4cc413e0..de5e8785 100644 --- a/internal/service/ws.go +++ b/internal/service/ws.go @@ -3,26 +3,27 @@ package service import ( "bufio" "context" + "github.com/knadh/koanf/v2" "net/http" "sync" "github.com/gorilla/websocket" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" - "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/ssh" ) type WsService struct { + conf *koanf.Koanf sshRepo biz.SSHRepo } -func NewWsService() *WsService { +func NewWsService(conf *koanf.Koanf, ssh biz.SSHRepo) *WsService { return &WsService{ - sshRepo: data.NewSSHRepo(), + conf: conf, + sshRepo: ssh, } } @@ -121,7 +122,7 @@ func (s *WsService) upgrade(w http.ResponseWriter, r *http.Request) (*websocket. } // debug 模式下不校验 origin,方便 vite 代理调试 - if app.Conf.Bool("app.debug") { + if s.conf.Bool("app.debug") { upGrader.CheckOrigin = func(r *http.Request) bool { return true }