From bc4fcfe6edd5915573c0f6d6c85cfc50d99862fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Mon, 19 May 2025 00:09:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=AE=B1=E5=92=8C=E8=B7=91=E5=88=86=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/cli/wire_gen.go | 6 +- cmd/web/wire_gen.go | 10 +- internal/apps/apps.go | 4 - internal/bootstrap/apps.go | 6 +- internal/http/middleware/entrance.go | 2 +- .../request/toolbox_benchmark.go} | 4 +- .../request/toolbox_system.go} | 16 +- internal/route/http.go | 26 +++ internal/service/service.go | 2 + .../app.go => service/toolbox_benchmark.go} | 87 +++++----- .../app.go => service/toolbox_system.go} | 155 ++++++++---------- 11 files changed, 155 insertions(+), 163 deletions(-) rename internal/{apps/benchmark/request.go => http/request/toolbox_benchmark.go} (75%) rename internal/{apps/toolbox/request.go => http/request/toolbox_system.go} (69%) rename internal/{apps/benchmark/app.go => service/toolbox_benchmark.go} (83%) rename internal/{apps/toolbox/app.go => service/toolbox_system.go} (52%) diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go index cdbed43f..3933cfc9 100644 --- a/cmd/cli/wire_gen.go +++ b/cmd/cli/wire_gen.go @@ -8,7 +8,6 @@ package main import ( "github.com/tnb-labs/panel/internal/app" - "github.com/tnb-labs/panel/internal/apps/benchmark" "github.com/tnb-labs/panel/internal/apps/codeserver" "github.com/tnb-labs/panel/internal/apps/docker" "github.com/tnb-labs/panel/internal/apps/fail2ban" @@ -32,7 +31,6 @@ import ( "github.com/tnb-labs/panel/internal/apps/rsync" "github.com/tnb-labs/panel/internal/apps/s3fs" "github.com/tnb-labs/panel/internal/apps/supervisor" - "github.com/tnb-labs/panel/internal/apps/toolbox" "github.com/tnb-labs/panel/internal/bootstrap" "github.com/tnb-labs/panel/internal/data" "github.com/tnb-labs/panel/internal/route" @@ -77,7 +75,6 @@ func initCli() (*app.Cli, error) { cli := route.NewCli(locale, cliService) command := bootstrap.NewCli(locale, cli) gormigrate := bootstrap.NewMigrate(db) - benchmarkApp := benchmark.NewApp(locale) codeserverApp := codeserver.NewApp() dockerApp := docker.NewApp() fail2banApp := fail2ban.NewApp(locale, websiteRepo) @@ -101,8 +98,7 @@ func initCli() (*app.Cli, error) { rsyncApp := rsync.NewApp(locale) s3fsApp := s3fs.NewApp(locale) supervisorApp := supervisor.NewApp(locale) - toolboxApp := toolbox.NewApp(locale) - loader := bootstrap.NewLoader(benchmarkApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp) + loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp) appCli := app.NewCli(command, gormigrate, loader) return appCli, nil } diff --git a/cmd/web/wire_gen.go b/cmd/web/wire_gen.go index e3d7fb04..b496755d 100644 --- a/cmd/web/wire_gen.go +++ b/cmd/web/wire_gen.go @@ -8,7 +8,6 @@ package main import ( "github.com/tnb-labs/panel/internal/app" - "github.com/tnb-labs/panel/internal/apps/benchmark" "github.com/tnb-labs/panel/internal/apps/codeserver" "github.com/tnb-labs/panel/internal/apps/docker" "github.com/tnb-labs/panel/internal/apps/fail2ban" @@ -32,7 +31,6 @@ import ( "github.com/tnb-labs/panel/internal/apps/rsync" "github.com/tnb-labs/panel/internal/apps/s3fs" "github.com/tnb-labs/panel/internal/apps/supervisor" - "github.com/tnb-labs/panel/internal/apps/toolbox" "github.com/tnb-labs/panel/internal/bootstrap" "github.com/tnb-labs/panel/internal/data" "github.com/tnb-labs/panel/internal/http/middleware" @@ -118,7 +116,8 @@ func initWeb() (*app.Web, error) { monitorService := service.NewMonitorService(settingRepo, monitorRepo) settingService := service.NewSettingService(settingRepo) systemctlService := service.NewSystemctlService(locale) - benchmarkApp := benchmark.NewApp(locale) + toolboxSystemService := service.NewToolboxSystemService(locale) + toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale) codeserverApp := codeserver.NewApp() dockerApp := docker.NewApp() fail2banApp := fail2ban.NewApp(locale, websiteRepo) @@ -142,9 +141,8 @@ func initWeb() (*app.Web, error) { rsyncApp := rsync.NewApp(locale) s3fsApp := s3fs.NewApp(locale) supervisorApp := supervisor.NewApp(locale) - toolboxApp := toolbox.NewApp(locale) - loader := bootstrap.NewLoader(benchmarkApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp) - http := route.NewHttp(userService, userTokenService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader) + loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp) + http := route.NewHttp(userService, userTokenService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, loader) wsService := service.NewWsService(locale, koanf, sshRepo) ws := route.NewWs(wsService) mux, err := bootstrap.NewRouter(locale, middlewares, http, ws) diff --git a/internal/apps/apps.go b/internal/apps/apps.go index 44730845..6ebec23d 100644 --- a/internal/apps/apps.go +++ b/internal/apps/apps.go @@ -3,7 +3,6 @@ package apps import ( "github.com/google/wire" - "github.com/tnb-labs/panel/internal/apps/benchmark" "github.com/tnb-labs/panel/internal/apps/codeserver" "github.com/tnb-labs/panel/internal/apps/docker" "github.com/tnb-labs/panel/internal/apps/fail2ban" @@ -27,11 +26,9 @@ import ( "github.com/tnb-labs/panel/internal/apps/rsync" "github.com/tnb-labs/panel/internal/apps/s3fs" "github.com/tnb-labs/panel/internal/apps/supervisor" - "github.com/tnb-labs/panel/internal/apps/toolbox" ) var ProviderSet = wire.NewSet( - benchmark.NewApp, codeserver.NewApp, docker.NewApp, fail2ban.NewApp, @@ -55,5 +52,4 @@ var ProviderSet = wire.NewSet( rsync.NewApp, s3fs.NewApp, supervisor.NewApp, - toolbox.NewApp, ) diff --git a/internal/bootstrap/apps.go b/internal/bootstrap/apps.go index 88606d6d..2a54b141 100644 --- a/internal/bootstrap/apps.go +++ b/internal/bootstrap/apps.go @@ -1,7 +1,6 @@ package bootstrap import ( - "github.com/tnb-labs/panel/internal/apps/benchmark" "github.com/tnb-labs/panel/internal/apps/codeserver" "github.com/tnb-labs/panel/internal/apps/docker" "github.com/tnb-labs/panel/internal/apps/fail2ban" @@ -25,12 +24,10 @@ import ( "github.com/tnb-labs/panel/internal/apps/rsync" "github.com/tnb-labs/panel/internal/apps/s3fs" "github.com/tnb-labs/panel/internal/apps/supervisor" - "github.com/tnb-labs/panel/internal/apps/toolbox" "github.com/tnb-labs/panel/pkg/apploader" ) func NewLoader( - benchmark *benchmark.App, codeserver *codeserver.App, docker *docker.App, fail2ban *fail2ban.App, @@ -54,9 +51,8 @@ func NewLoader( rsync *rsync.App, s3fs *s3fs.App, supervisor *supervisor.App, - toolbox *toolbox.App, ) *apploader.Loader { loader := new(apploader.Loader) - loader.Add(benchmark, codeserver, docker, fail2ban, frp, gitea, memcached, minio, mysql, nginx, php74, php80, php81, php82, php83, php84, phpmyadmin, podman, postgresql, pureftpd, redis, rsync, s3fs, supervisor, toolbox) + loader.Add(codeserver, docker, fail2ban, frp, gitea, memcached, minio, mysql, nginx, php74, php80, php81, php82, php83, php84, phpmyadmin, podman, postgresql, pureftpd, redis, rsync, s3fs, supervisor) return loader } diff --git a/internal/http/middleware/entrance.go b/internal/http/middleware/entrance.go index cfbe91eb..10fbf924 100644 --- a/internal/http/middleware/entrance.go +++ b/internal/http/middleware/entrance.go @@ -1,12 +1,12 @@ package middleware import ( - "github.com/go-chi/chi/v5" "net" "net/http" "slices" "strings" + "github.com/go-chi/chi/v5" "github.com/go-rat/chix" "github.com/go-rat/sessions" "github.com/knadh/koanf/v2" diff --git a/internal/apps/benchmark/request.go b/internal/http/request/toolbox_benchmark.go similarity index 75% rename from internal/apps/benchmark/request.go rename to internal/http/request/toolbox_benchmark.go index 1f5240a2..a0019481 100644 --- a/internal/apps/benchmark/request.go +++ b/internal/http/request/toolbox_benchmark.go @@ -1,6 +1,6 @@ -package benchmark +package request -type Test struct { +type ToolboxBenchmarkTest struct { Name string `json:"name" validate:"required|in:image,machine,compile,encryption,compression,physics,json,memory,disk"` Multi bool `json:"multi"` } diff --git a/internal/apps/toolbox/request.go b/internal/http/request/toolbox_system.go similarity index 69% rename from internal/apps/toolbox/request.go rename to internal/http/request/toolbox_system.go index d67083f1..b0d01a53 100644 --- a/internal/apps/toolbox/request.go +++ b/internal/http/request/toolbox_system.go @@ -1,32 +1,32 @@ -package toolbox +package request import "time" -type DNS struct { +type ToolboxSystemDNS struct { DNS1 string `form:"dns1" json:"dns1" validate:"required"` DNS2 string `form:"dns2" json:"dns2" validate:"required"` } -type SWAP struct { +type ToolboxSystemSWAP struct { Size int64 `form:"size" json:"size" validate:"min:0"` } -type Timezone struct { +type ToolboxSystemTimezone struct { Timezone string `form:"timezone" json:"timezone" validate:"required"` } -type Time struct { +type ToolboxSystemTime struct { Time time.Time `form:"time" json:"time" validate:"required"` } -type Hostname struct { +type ToolboxSystemHostname struct { Hostname string `form:"hostname" json:"hostname" validate:"required|regex:^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"` } -type Hosts struct { +type ToolboxSystemHosts struct { Hosts string `form:"hosts" json:"hosts"` } -type Password struct { +type ToolboxSystemPassword struct { Password string `form:"password" json:"password" validate:"required|password"` } diff --git a/internal/route/http.go b/internal/route/http.go index befcb087..23c2abff 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -42,6 +42,8 @@ type Http struct { monitor *service.MonitorService setting *service.SettingService systemctl *service.SystemctlService + toolboxSystem *service.ToolboxSystemService + toolboxBenchmark *service.ToolboxBenchmarkService apps *apploader.Loader } @@ -73,6 +75,8 @@ func NewHttp( monitor *service.MonitorService, setting *service.SettingService, systemctl *service.SystemctlService, + toolboxSystem *service.ToolboxSystemService, + toolboxBenchmark *service.ToolboxBenchmarkService, apps *apploader.Loader, ) *Http { return &Http{ @@ -103,6 +107,8 @@ func NewHttp( monitor: monitor, setting: setting, systemctl: systemctl, + toolboxSystem: toolboxSystem, + toolboxBenchmark: toolboxBenchmark, apps: apps, } } @@ -381,6 +387,26 @@ func (route *Http) Register(r *chi.Mux) { r.Post("/stop", route.systemctl.Stop) }) + r.Route("/toolbox_system", func(r chi.Router) { + r.Get("/dns", route.toolboxSystem.GetDNS) + r.Post("/dns", route.toolboxSystem.UpdateDNS) + r.Get("/swap", route.toolboxSystem.GetSWAP) + r.Post("/swap", route.toolboxSystem.UpdateSWAP) + r.Get("/timezone", route.toolboxSystem.GetTimezone) + r.Post("/timezone", route.toolboxSystem.UpdateTimezone) + r.Post("/time", route.toolboxSystem.UpdateTime) + r.Post("/sync_time", route.toolboxSystem.SyncTime) + r.Get("/hostname", route.toolboxSystem.GetHostname) + r.Post("/hostname", route.toolboxSystem.UpdateHostname) + r.Get("/hosts", route.toolboxSystem.GetHosts) + r.Post("/hosts", route.toolboxSystem.UpdateHosts) + r.Post("/root_password", route.toolboxSystem.UpdateRootPassword) + }) + + r.Route("/toolbox_benchmark", func(r chi.Router) { + r.Post("/test", route.toolboxBenchmark.Test) + }) + r.Route("/apps", func(r chi.Router) { route.apps.Register(r) }) diff --git a/internal/service/service.go b/internal/service/service.go index cda167aa..9d9ac57b 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -32,5 +32,7 @@ var ProviderSet = wire.NewSet( NewUserService, NewUserTokenService, NewWebsiteService, + NewToolboxSystemService, + NewToolboxBenchmarkService, NewWsService, ) diff --git a/internal/apps/benchmark/app.go b/internal/service/toolbox_benchmark.go similarity index 83% rename from internal/apps/benchmark/app.go rename to internal/service/toolbox_benchmark.go index 3fb6f1ce..11b92d95 100644 --- a/internal/apps/benchmark/app.go +++ b/internal/service/toolbox_benchmark.go @@ -1,4 +1,4 @@ -package benchmark +package service import ( "bytes" @@ -21,69 +21,64 @@ import ( "sync" "time" - "github.com/go-chi/chi/v5" "github.com/leonelquinteros/gotext" - "github.com/tnb-labs/panel/internal/service" + "github.com/tnb-labs/panel/internal/http/request" ) -type App struct { +type ToolboxBenchmarkService struct { t *gotext.Locale } -func NewApp(t *gotext.Locale) *App { - return &App{ +func NewToolboxBenchmarkService(t *gotext.Locale) *ToolboxBenchmarkService { + return &ToolboxBenchmarkService{ t: t, } } -func (s *App) Route(r chi.Router) { - r.Post("/test", s.Test) -} - // Test 运行测试 -func (s *App) Test(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Test](r) +func (s *ToolboxBenchmarkService) Test(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxBenchmarkTest](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } switch req.Name { case "image": result := s.imageProcessing(req.Multi) - service.Success(w, result) + Success(w, result) case "machine": result := s.machineLearning(req.Multi) - service.Success(w, result) + Success(w, result) case "compile": result := s.compileSimulationSingle(req.Multi) - service.Success(w, result) + Success(w, result) case "encryption": result := s.encryptionTest(req.Multi) - service.Success(w, result) + Success(w, result) case "compression": result := s.compressionTest(req.Multi) - service.Success(w, result) + Success(w, result) case "physics": result := s.physicsSimulation(req.Multi) - service.Success(w, result) + Success(w, result) case "json": result := s.jsonProcessing(req.Multi) - service.Success(w, result) + Success(w, result) case "disk": result := s.diskTestTask() - service.Success(w, result) + Success(w, result) case "memory": result := s.memoryTestTask() - service.Success(w, result) + Success(w, result) default: - service.Error(w, http.StatusUnprocessableEntity, s.t.Get("unknown test type")) + Error(w, http.StatusUnprocessableEntity, s.t.Get("unknown test type")) } } // calculateCpuScore 计算CPU成绩 -func (s *App) calculateCpuScore(duration time.Duration) int { +func (s *ToolboxBenchmarkService) calculateCpuScore(duration time.Duration) int { score := int((10 / duration.Seconds()) * float64(3000)) if score < 0 { @@ -93,7 +88,7 @@ func (s *App) calculateCpuScore(duration time.Duration) int { } // calculateScore 计算内存/硬盘成绩 -func (s *App) calculateScore(duration time.Duration) int { +func (s *ToolboxBenchmarkService) calculateScore(duration time.Duration) int { score := int((20 / duration.Seconds()) * float64(30000)) if score < 0 { @@ -104,7 +99,7 @@ func (s *App) calculateScore(duration time.Duration) int { // 图像处理 -func (s *App) imageProcessing(multi bool) int { +func (s *ToolboxBenchmarkService) imageProcessing(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -117,7 +112,7 @@ func (s *App) imageProcessing(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) imageProcessingTask(numThreads int) error { +func (s *ToolboxBenchmarkService) imageProcessingTask(numThreads int) error { img := image.NewRGBA(image.Rect(0, 0, 4000, 4000)) for x := 0; x < 4000; x++ { for y := 0; y < 4000; y++ { @@ -166,7 +161,7 @@ func (s *App) imageProcessingTask(numThreads int) error { // 机器学习(矩阵乘法) -func (s *App) machineLearning(multi bool) int { +func (s *ToolboxBenchmarkService) machineLearning(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -177,7 +172,7 @@ func (s *App) machineLearning(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) machineLearningTask(numThreads int) { +func (s *ToolboxBenchmarkService) machineLearningTask(numThreads int) { size := 900 a := make([][]float64, size) b := make([][]float64, size) @@ -224,7 +219,7 @@ func (s *App) machineLearningTask(numThreads int) { // 数学问题(计算斐波那契数) -func (s *App) compileSimulationSingle(multi bool) int { +func (s *ToolboxBenchmarkService) compileSimulationSingle(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -258,7 +253,7 @@ func (s *App) compileSimulationSingle(multi bool) int { } // 斐波那契函数 -func (s *App) fib(n int) *big.Int { +func (s *ToolboxBenchmarkService) fib(n int) *big.Int { if n < 2 { return big.NewInt(int64(n)) } @@ -275,7 +270,7 @@ func (s *App) fib(n int) *big.Int { // AES加密 -func (s *App) encryptionTest(multi bool) int { +func (s *ToolboxBenchmarkService) encryptionTest(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -288,7 +283,7 @@ func (s *App) encryptionTest(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) encryptionTestTask(numThreads int) error { +func (s *ToolboxBenchmarkService) encryptionTestTask(numThreads int) error { key := []byte("abcdefghijklmnopqrstuvwxyz123456") dataSize := 1024 * 1024 * 512 // 512 MB plaintext := []byte(strings.Repeat("A", dataSize)) @@ -330,7 +325,7 @@ func (s *App) encryptionTestTask(numThreads int) error { // 压缩/解压缩 -func (s *App) compressionTest(multi bool) int { +func (s *ToolboxBenchmarkService) compressionTest(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -341,7 +336,7 @@ func (s *App) compressionTest(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) compressionTestTask(numThreads int) { +func (s *ToolboxBenchmarkService) compressionTestTask(numThreads int) { data := []byte(strings.Repeat("耗子面板", 50000000)) chunkSize := len(data) / numThreads @@ -391,7 +386,7 @@ func (s *App) compressionTestTask(numThreads int) { // 物理仿真(N体问题) -func (s *App) physicsSimulation(multi bool) int { +func (s *ToolboxBenchmarkService) physicsSimulation(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -402,7 +397,7 @@ func (s *App) physicsSimulation(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) physicsSimulationTask(numThreads int) { +func (s *ToolboxBenchmarkService) physicsSimulationTask(numThreads int) { const ( numBodies = 4000 steps = 30 @@ -489,7 +484,7 @@ func (s *App) physicsSimulationTask(numThreads int) { // JSON解析 -func (s *App) jsonProcessing(multi bool) int { +func (s *ToolboxBenchmarkService) jsonProcessing(multi bool) int { n := 1 if multi { n = runtime.NumCPU() @@ -500,7 +495,7 @@ func (s *App) jsonProcessing(multi bool) int { return s.calculateCpuScore(duration) } -func (s *App) jsonProcessingTask(numThreads int) { +func (s *ToolboxBenchmarkService) jsonProcessingTask(numThreads int) { numElements := 1000000 elementsPerThread := numElements / numThreads @@ -541,7 +536,7 @@ func (s *App) jsonProcessingTask(numThreads int) { // 内存性能 -func (s *App) memoryTestTask() map[string]any { +func (s *ToolboxBenchmarkService) memoryTestTask() map[string]any { results := make(map[string]any) dataSize := 500 * 1024 * 1024 // 500 MB data := make([]byte, dataSize) @@ -559,7 +554,7 @@ func (s *App) memoryTestTask() map[string]any { return results } -func (s *App) memoryBandwidthTest(data []byte) string { +func (s *ToolboxBenchmarkService) memoryBandwidthTest(data []byte) string { dataSize := len(data) startTime := time.Now() @@ -576,7 +571,7 @@ func (s *App) memoryBandwidthTest(data []byte) string { return fmt.Sprintf("%.2f MB/s", speed) } -func (s *App) memoryLatencyTest(data []byte) string { +func (s *ToolboxBenchmarkService) memoryLatencyTest(data []byte) string { dataSize := len(data) indices := rand.Perm(dataSize) @@ -595,7 +590,7 @@ func (s *App) memoryLatencyTest(data []byte) string { // 硬盘IO -func (s *App) diskTestTask() map[string]any { +func (s *ToolboxBenchmarkService) diskTestTask() map[string]any { results := make(map[string]any) blockSizes := []int64{4 * 1024, 64 * 1024, 512 * 1024, 1 * 1024 * 1024} // 4K, 64K, 512K, 1M fileSize := int64(100 * 1024 * 1024) // 100MB 文件 @@ -611,7 +606,7 @@ func (s *App) diskTestTask() map[string]any { return results } -func (s *App) diskIOTest(blockSize int64, fileSize int64) map[string]any { +func (s *ToolboxBenchmarkService) diskIOTest(blockSize int64, fileSize int64) map[string]any { result := make(map[string]any) tempFile := fmt.Sprintf("tempfile_%d", blockSize) defer func(name string) { @@ -631,7 +626,7 @@ func (s *App) diskIOTest(blockSize int64, fileSize int64) map[string]any { return result } -func (s *App) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) { +func (s *ToolboxBenchmarkService) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) { totalBlocks := fileSize / blockSize data := make([]byte, blockSize) @@ -667,7 +662,7 @@ func (s *App) diskWriteTest(fileName string, blockSize int64, fileSize int64) (f return speed, iops } -func (s *App) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) { +func (s *ToolboxBenchmarkService) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) { totalBlocks := fileSize / blockSize data := make([]byte, blockSize) diff --git a/internal/apps/toolbox/app.go b/internal/service/toolbox_system.go similarity index 52% rename from internal/apps/toolbox/app.go rename to internal/service/toolbox_system.go index 79269c89..01df14e8 100644 --- a/internal/apps/toolbox/app.go +++ b/internal/service/toolbox_system.go @@ -1,4 +1,4 @@ -package toolbox +package service import ( "net/http" @@ -7,13 +7,12 @@ import ( "regexp" "strings" - "github.com/go-chi/chi/v5" "github.com/go-rat/chix" "github.com/leonelquinteros/gotext" "github.com/spf13/cast" "github.com/tnb-labs/panel/internal/app" - "github.com/tnb-labs/panel/internal/service" + "github.com/tnb-labs/panel/internal/http/request" "github.com/tnb-labs/panel/pkg/io" "github.com/tnb-labs/panel/pkg/ntp" "github.com/tnb-labs/panel/pkg/shell" @@ -21,37 +20,21 @@ import ( "github.com/tnb-labs/panel/pkg/types" ) -type App struct { +type ToolboxSystemService struct { t *gotext.Locale } -func NewApp(t *gotext.Locale) *App { - return &App{ +func NewToolboxSystemService(t *gotext.Locale) *ToolboxSystemService { + return &ToolboxSystemService{ t: t, } } -func (s *App) Route(r chi.Router) { - r.Get("/dns", s.GetDNS) - r.Post("/dns", s.UpdateDNS) - r.Get("/swap", s.GetSWAP) - r.Post("/swap", s.UpdateSWAP) - r.Get("/timezone", s.GetTimezone) - r.Post("/timezone", s.UpdateTimezone) - r.Post("/time", s.UpdateTime) - r.Post("/sync_time", s.SyncTime) - r.Get("/hostname", s.GetHostname) - r.Post("/hostname", s.UpdateHostname) - r.Get("/hosts", s.GetHosts) - r.Post("/hosts", s.UpdateHosts) - r.Post("/root_password", s.UpdateRootPassword) -} - // GetDNS 获取 DNS 信息 -func (s *App) GetDNS(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) GetDNS(w http.ResponseWriter, r *http.Request) { raw, err := io.Read("/etc/resolv.conf") if err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } @@ -61,14 +44,14 @@ func (s *App) GetDNS(w http.ResponseWriter, r *http.Request) { dns = append(dns, m[1]) } - service.Success(w, dns) + Success(w, dns) } // UpdateDNS 设置 DNS 信息 -func (s *App) UpdateDNS(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[DNS](r) +func (s *ToolboxSystemService) UpdateDNS(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemDNS](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } @@ -77,21 +60,21 @@ func (s *App) UpdateDNS(w http.ResponseWriter, r *http.Request) { dns += "nameserver " + req.DNS2 + "\n" if err := io.Write("/etc/resolv.conf", dns, 0644); err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to update DNS: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to update DNS: %v", err)) return } - service.Success(w, nil) + Success(w, nil) } // GetSWAP 获取 SWAP 信息 -func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) GetSWAP(w http.ResponseWriter, r *http.Request) { var total, used, free string var size int64 if io.Exists(filepath.Join(app.Root, "swap")) { file, err := os.Stat(filepath.Join(app.Root, "swap")) if err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err)) return } @@ -104,7 +87,7 @@ func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) { raw, err := shell.Execf("free | grep Swap") if err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err)) return } @@ -114,7 +97,7 @@ func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) { free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024) } - service.Success(w, chix.M{ + Success(w, chix.M{ "total": total, "size": size, "used": used, @@ -123,24 +106,24 @@ func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) { } // UpdateSWAP 设置 SWAP 信息 -func (s *App) UpdateSWAP(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[SWAP](r) +func (s *ToolboxSystemService) UpdateSWAP(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemSWAP](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } if io.Exists(filepath.Join(app.Root, "swap")) { if _, err = shell.Execf("swapoff '%s'", filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if _, err = shell.Execf("rm -f '%s'", filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if _, err = shell.Execf(`sed -i "\|^%s|d" /etc/fstab`, filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } } @@ -149,52 +132,52 @@ func (s *App) UpdateSWAP(w http.ResponseWriter, r *http.Request) { var free string free, err = shell.Execf("df -k %s | awk '{print $4}' | tail -n 1", app.Root) if err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get disk space: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to get disk space: %v", err)) return } if cast.ToInt64(free)*1024 < req.Size*1024*1024 { - service.Error(w, http.StatusInternalServerError, s.t.Get("disk space is insufficient, current free %s", tools.FormatBytes(cast.ToFloat64(free)))) + Error(w, http.StatusInternalServerError, s.t.Get("disk space is insufficient, current free %s", tools.FormatBytes(cast.ToFloat64(free)))) return } btrfsCheck, _ := shell.Execf("df -T %s | awk '{print $2}' | tail -n 1", app.Root) if strings.Contains(btrfsCheck, "btrfs") { if _, err = shell.Execf("btrfs filesystem mkswapfile --size %dM --uuid clear %s", req.Size, filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } } else { if _, err = shell.Execf("dd if=/dev/zero of=%s bs=1M count=%d", filepath.Join(app.Root, "swap"), req.Size); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if _, err = shell.Execf("mkswap -f '%s'", filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if err = io.Chmod(filepath.Join(app.Root, "swap"), 0600); err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set SWAP permission: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to set SWAP permission: %v", err)) return } } if _, err = shell.Execf("swapon '%s'", filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if _, err = shell.Execf("echo '%s swap swap defaults 0 0' >> /etc/fstab", filepath.Join(app.Root, "swap")); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } } - service.Success(w, nil) + Success(w, nil) } // GetTimezone 获取时区 -func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) GetTimezone(w http.ResponseWriter, r *http.Request) { raw, err := shell.Execf("timedatectl | grep zone") if err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get timezone: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to get timezone: %v", err)) return } @@ -205,7 +188,7 @@ func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) { zonesRaw, err := shell.Execf("timedatectl list-timezones") if err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get available timezones: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to get available timezones: %v", err)) return } zones := strings.Split(zonesRaw, "\n") @@ -218,121 +201,121 @@ func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) { }) } - service.Success(w, chix.M{ + Success(w, chix.M{ "timezone": match[1], "timezones": zonesList, }) } // UpdateTimezone 设置时区 -func (s *App) UpdateTimezone(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Timezone](r) +func (s *ToolboxSystemService) UpdateTimezone(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemTimezone](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } if _, err = shell.Execf("timedatectl set-timezone '%s'", req.Timezone); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } - service.Success(w, nil) + Success(w, nil) } // UpdateTime 设置时间 -func (s *App) UpdateTime(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Time](r) +func (s *ToolboxSystemService) UpdateTime(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemTime](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } if err = ntp.UpdateSystemTime(req.Time); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } - service.Success(w, nil) + Success(w, nil) } // SyncTime 同步时间 -func (s *App) SyncTime(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) SyncTime(w http.ResponseWriter, r *http.Request) { now, err := ntp.Now() if err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } if err = ntp.UpdateSystemTime(now); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", err) + Error(w, http.StatusInternalServerError, "%v", err) return } - service.Success(w, nil) + Success(w, nil) } // GetHostname 获取主机名 -func (s *App) GetHostname(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) GetHostname(w http.ResponseWriter, r *http.Request) { hostname, _ := io.Read("/etc/hostname") - service.Success(w, strings.TrimSpace(hostname)) + Success(w, strings.TrimSpace(hostname)) } // UpdateHostname 设置主机名 -func (s *App) UpdateHostname(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Hostname](r) +func (s *ToolboxSystemService) UpdateHostname(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemHostname](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } if _, err = shell.Execf("hostnamectl set-hostname '%s'", req.Hostname); err != nil { // 直接写 /etc/hostname if err = io.Write("/etc/hostname", req.Hostname, 0644); err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set hostname: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to set hostname: %v", err)) return } } - service.Success(w, nil) + Success(w, nil) } // GetHosts 获取 hosts 信息 -func (s *App) GetHosts(w http.ResponseWriter, r *http.Request) { +func (s *ToolboxSystemService) GetHosts(w http.ResponseWriter, r *http.Request) { hosts, _ := io.Read("/etc/hosts") - service.Success(w, hosts) + Success(w, hosts) } // UpdateHosts 设置 hosts 信息 -func (s *App) UpdateHosts(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Hosts](r) +func (s *ToolboxSystemService) UpdateHosts(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemHosts](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } if err = io.Write("/etc/hosts", req.Hosts, 0644); err != nil { - service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set hosts: %v", err)) + Error(w, http.StatusInternalServerError, s.t.Get("failed to set hosts: %v", err)) return } - service.Success(w, nil) + Success(w, nil) } // UpdateRootPassword 设置 root 密码 -func (s *App) UpdateRootPassword(w http.ResponseWriter, r *http.Request) { - req, err := service.Bind[Password](r) +func (s *ToolboxSystemService) UpdateRootPassword(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSystemPassword](r) if err != nil { - service.Error(w, http.StatusUnprocessableEntity, "%v", err) + Error(w, http.StatusUnprocessableEntity, "%v", err) return } req.Password = strings.ReplaceAll(req.Password, `'`, `\'`) if _, err = shell.Execf(`yes '%s' | passwd root`, req.Password); err != nil { - service.Error(w, http.StatusInternalServerError, "%v", s.t.Get("failed to set root password: %v", err)) + Error(w, http.StatusInternalServerError, "%v", s.t.Get("failed to set root password: %v", err)) return } - service.Success(w, nil) + Success(w, nil) }