2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-06 14:47:18 +08:00

refactor: 应用支持依赖注入

This commit is contained in:
耗子
2024-12-16 02:01:31 +08:00
parent 1fdc86cc0a
commit a64660d0cb
57 changed files with 663 additions and 861 deletions

View File

@@ -38,13 +38,13 @@ func initCli() (*app.Cli, error) {
userRepo := data.NewUserRepo(db)
settingRepo := data.NewSettingRepo(db, koanf, taskRepo)
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(databaseServerRepo, databaseUserRepo)
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(db, 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)
cliService := service.NewCliService(koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo)
cli := route.NewCli(cliService)
command := bootstrap.NewCli(cli)
appCli := app.NewCli(command)

View File

@@ -6,6 +6,7 @@ import (
"github.com/google/wire"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/apps"
"github.com/TheTNB/panel/internal/bootstrap"
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/internal/http/middleware"
@@ -16,5 +17,5 @@ import (
// initWeb init application.
func initWeb() (*app.Web, error) {
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, job.ProviderSet, app.NewWeb))
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, job.ProviderSet, app.NewWeb))
}

View File

@@ -8,6 +8,25 @@ package main
import (
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/apps"
"github.com/TheTNB/panel/internal/apps/benchmark"
"github.com/TheTNB/panel/internal/apps/docker"
"github.com/TheTNB/panel/internal/apps/fail2ban"
"github.com/TheTNB/panel/internal/apps/frp"
"github.com/TheTNB/panel/internal/apps/gitea"
"github.com/TheTNB/panel/internal/apps/memcached"
"github.com/TheTNB/panel/internal/apps/mysql"
"github.com/TheTNB/panel/internal/apps/nginx"
"github.com/TheTNB/panel/internal/apps/php"
"github.com/TheTNB/panel/internal/apps/phpmyadmin"
"github.com/TheTNB/panel/internal/apps/podman"
"github.com/TheTNB/panel/internal/apps/postgresql"
"github.com/TheTNB/panel/internal/apps/pureftpd"
"github.com/TheTNB/panel/internal/apps/redis"
"github.com/TheTNB/panel/internal/apps/rsync"
"github.com/TheTNB/panel/internal/apps/s3fs"
"github.com/TheTNB/panel/internal/apps/supervisor"
"github.com/TheTNB/panel/internal/apps/toolbox"
"github.com/TheTNB/panel/internal/bootstrap"
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/internal/http/middleware"
@@ -45,8 +64,8 @@ func initWeb() (*app.Web, error) {
userRepo := data.NewUserRepo(db)
userService := service.NewUserService(koanf, manager, userRepo)
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(databaseServerRepo, databaseUserRepo)
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(db, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(db)
certAccountRepo := data.NewCertAccountRepo(db, userRepo)
websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
@@ -85,10 +104,29 @@ func initWeb() (*app.Web, error) {
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)
benchmarkApp := benchmark.NewApp()
dockerApp := docker.NewApp()
fail2banApp := fail2ban.NewApp(websiteRepo)
frpApp := frp.NewApp()
giteaApp := gitea.NewApp()
memcachedApp := memcached.NewApp()
mysqlApp := mysql.NewApp(settingRepo)
nginxApp := nginx.NewApp()
phpApp := php.NewApp(taskRepo)
phpmyadminApp := phpmyadmin.NewApp()
podmanApp := podman.NewApp()
postgresqlApp := postgresql.NewApp()
pureftpdApp := pureftpd.NewApp()
redisApp := redis.NewApp()
rsyncApp := rsync.NewApp()
s3fsApp := s3fs.NewApp(settingRepo)
supervisorApp := supervisor.NewApp()
toolboxApp := toolbox.NewApp()
loader := apps.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, mysqlApp, nginxApp, phpApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp)
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, loader)
wsService := service.NewWsService(koanf, sshRepo)
ws := route.NewWs(wsService)
mux, err := bootstrap.NewRouter(koanf, db, logger, manager, middlewares, http, ws)
mux, err := bootstrap.NewRouter(middlewares, http, ws)
if err != nil {
return nil, err
}

1
go.mod
View File

@@ -5,7 +5,6 @@ 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

3
go.sum
View File

@@ -6,8 +6,6 @@ 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=
@@ -190,7 +188,6 @@ 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=

View File

@@ -1,13 +1,5 @@
package app
import (
"gorm.io/gorm"
)
var (
Orm *gorm.DB
)
// 面板状态常量
const (
StatusNormal = iota

View File

@@ -1,19 +1,12 @@
package app
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"syscall"
"time"
"path/filepath"
"github.com/bddjr/hlfhr"
"github.com/cloudflare/tableflip"
"github.com/go-chi/chi/v5"
"github.com/go-gormigrate/gormigrate/v2"
"github.com/gookit/validate"
@@ -51,73 +44,18 @@ func (r *Web) Run() error {
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)
}
if r.conf.Bool("http.tls") {
cert := filepath.Join(Root, "panel/storage/cert.pem")
key := filepath.Join(Root, "panel/storage/cert.key")
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"), "with tls")
if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) {
return 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)
} else {
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"))
if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
return 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

170
internal/apps/apps.go Normal file
View File

@@ -0,0 +1,170 @@
package apps
import (
"reflect"
"slices"
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/wire"
"github.com/TheTNB/panel/internal/apps/benchmark"
"github.com/TheTNB/panel/internal/apps/docker"
"github.com/TheTNB/panel/internal/apps/fail2ban"
"github.com/TheTNB/panel/internal/apps/frp"
"github.com/TheTNB/panel/internal/apps/gitea"
"github.com/TheTNB/panel/internal/apps/memcached"
"github.com/TheTNB/panel/internal/apps/mysql"
"github.com/TheTNB/panel/internal/apps/nginx"
"github.com/TheTNB/panel/internal/apps/php"
"github.com/TheTNB/panel/internal/apps/phpmyadmin"
"github.com/TheTNB/panel/internal/apps/podman"
"github.com/TheTNB/panel/internal/apps/postgresql"
"github.com/TheTNB/panel/internal/apps/pureftpd"
"github.com/TheTNB/panel/internal/apps/redis"
"github.com/TheTNB/panel/internal/apps/rsync"
"github.com/TheTNB/panel/internal/apps/s3fs"
"github.com/TheTNB/panel/internal/apps/supervisor"
"github.com/TheTNB/panel/internal/apps/toolbox"
)
var ProviderSet = wire.NewSet(
NewLoader,
benchmark.NewApp,
docker.NewApp,
fail2ban.NewApp,
frp.NewApp,
gitea.NewApp,
memcached.NewApp,
mysql.NewApp,
nginx.NewApp,
php.NewApp,
phpmyadmin.NewApp,
podman.NewApp,
postgresql.NewApp,
pureftpd.NewApp,
redis.NewApp,
rsync.NewApp,
s3fs.NewApp,
supervisor.NewApp,
toolbox.NewApp,
)
var slugs []string
type Loader struct {
benchmark *benchmark.App
docker *docker.App
fail2ban *fail2ban.App
frp *frp.App
gitea *gitea.App
memcached *memcached.App
mysql *mysql.App
nginx *nginx.App
php *php.App
phpmyadmin *phpmyadmin.App
podman *podman.App
postgresql *postgresql.App
pureftpd *pureftpd.App
redis *redis.App
rsync *rsync.App
s3fs *s3fs.App
supervisor *supervisor.App
toolbox *toolbox.App
}
func NewLoader(
benchmark *benchmark.App,
docker *docker.App,
fail2ban *fail2ban.App,
frp *frp.App,
gitea *gitea.App,
memcached *memcached.App,
mysql *mysql.App,
nginx *nginx.App,
php *php.App,
phpmyadmin *phpmyadmin.App,
podman *podman.App,
postgresql *postgresql.App,
pureftpd *pureftpd.App,
redis *redis.App,
rsync *rsync.App,
s3fs *s3fs.App,
supervisor *supervisor.App,
toolbox *toolbox.App,
) *Loader {
loader := &Loader{
benchmark: benchmark,
docker: docker,
fail2ban: fail2ban,
frp: frp,
gitea: gitea,
memcached: memcached,
mysql: mysql,
nginx: nginx,
php: php,
phpmyadmin: phpmyadmin,
podman: podman,
postgresql: postgresql,
pureftpd: pureftpd,
redis: redis,
rsync: rsync,
s3fs: s3fs,
supervisor: supervisor,
toolbox: toolbox,
}
loader.initSlugs()
return loader
}
func (r *Loader) Register(mux chi.Router) {
mux.Route("/benchmark", r.benchmark.Route)
mux.Route("/docker", r.docker.Route)
mux.Route("/fail2ban", r.fail2ban.Route)
mux.Route("/frp", r.frp.Route)
mux.Route("/gitea", r.gitea.Route)
mux.Route("/memcached", r.memcached.Route)
mux.Route("/mysql", r.mysql.Route)
mux.Route("/nginx", r.nginx.Route)
mux.Route("/php74", r.php.Route(74))
mux.Route("/php80", r.php.Route(80))
mux.Route("/php81", r.php.Route(81))
mux.Route("/php82", r.php.Route(82))
mux.Route("/php83", r.php.Route(83))
mux.Route("/php84", r.php.Route(84))
mux.Route("/phpmyadmin", r.phpmyadmin.Route)
mux.Route("/podman", r.podman.Route)
mux.Route("/postgresql", r.postgresql.Route)
mux.Route("/pureftpd", r.pureftpd.Route)
mux.Route("/redis", r.redis.Route)
mux.Route("/rsync", r.rsync.Route)
mux.Route("/s3fs", r.s3fs.Route)
mux.Route("/supervisor", r.supervisor.Route)
mux.Route("/toolbox", r.toolbox.Route)
}
func (r *Loader) initSlugs() []string {
if len(slugs) == 0 {
v := reflect.Indirect(reflect.ValueOf(r))
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
slug := strings.ToLower(v.Type().Field(i).Name)
if !field.IsNil() {
slugs = append(slugs, slug)
}
}
// 处理php
slugs = slices.DeleteFunc(slugs, func(slug string) bool {
return slug == "php"
})
slugs = append(slugs, "php74", "php80", "php81", "php82", "php83", "php84")
}
return slugs
}
func Slugs() []string {
return slugs
}

View File

@@ -21,18 +21,24 @@ import (
"sync"
"time"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/service"
)
type Service struct {
type App struct {
}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *App) Route(r chi.Router) {
r.Post("/test", s.Test)
}
// Test 运行测试
func (s *Service) Test(w http.ResponseWriter, r *http.Request) {
func (s *App) Test(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Test](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -73,7 +79,7 @@ func (s *Service) Test(w http.ResponseWriter, r *http.Request) {
}
// calculateCpuScore 计算CPU成绩
func (s *Service) calculateCpuScore(duration time.Duration) int {
func (s *App) calculateCpuScore(duration time.Duration) int {
score := int((10 / duration.Seconds()) * float64(3000))
if score < 0 {
@@ -83,7 +89,7 @@ func (s *Service) calculateCpuScore(duration time.Duration) int {
}
// calculateScore 计算内存/硬盘成绩
func (s *Service) calculateScore(duration time.Duration) int {
func (s *App) calculateScore(duration time.Duration) int {
score := int((20 / duration.Seconds()) * float64(30000))
if score < 0 {
@@ -94,7 +100,7 @@ func (s *Service) calculateScore(duration time.Duration) int {
// 图像处理
func (s *Service) imageProcessing(multi bool) int {
func (s *App) imageProcessing(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -107,7 +113,7 @@ func (s *Service) imageProcessing(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) imageProcessingTask(numThreads int) error {
func (s *App) 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++ {
@@ -156,7 +162,7 @@ func (s *Service) imageProcessingTask(numThreads int) error {
// 机器学习(矩阵乘法)
func (s *Service) machineLearning(multi bool) int {
func (s *App) machineLearning(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -167,7 +173,7 @@ func (s *Service) machineLearning(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) machineLearningTask(numThreads int) {
func (s *App) machineLearningTask(numThreads int) {
size := 900
a := make([][]float64, size)
b := make([][]float64, size)
@@ -214,7 +220,7 @@ func (s *Service) machineLearningTask(numThreads int) {
// 数学问题(计算斐波那契数)
func (s *Service) compileSimulationSingle(multi bool) int {
func (s *App) compileSimulationSingle(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -248,7 +254,7 @@ func (s *Service) compileSimulationSingle(multi bool) int {
}
// 斐波那契函数
func (s *Service) fib(n int) *big.Int {
func (s *App) fib(n int) *big.Int {
if n < 2 {
return big.NewInt(int64(n))
}
@@ -265,7 +271,7 @@ func (s *Service) fib(n int) *big.Int {
// AES加密
func (s *Service) encryptionTest(multi bool) int {
func (s *App) encryptionTest(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -278,7 +284,7 @@ func (s *Service) encryptionTest(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) encryptionTestTask(numThreads int) error {
func (s *App) encryptionTestTask(numThreads int) error {
key := []byte("abcdefghijklmnopqrstuvwxyz123456")
dataSize := 1 * 1024 * 1024 * 1024 // 1GB
plaintext := []byte(strings.Repeat("A", dataSize))
@@ -320,7 +326,7 @@ func (s *Service) encryptionTestTask(numThreads int) error {
// 压缩/解压缩
func (s *Service) compressionTest(multi bool) int {
func (s *App) compressionTest(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -331,7 +337,7 @@ func (s *Service) compressionTest(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) compressionTestTask(numThreads int) {
func (s *App) compressionTestTask(numThreads int) {
data := []byte(strings.Repeat("耗子面板", 50000000))
chunkSize := len(data) / numThreads
@@ -381,7 +387,7 @@ func (s *Service) compressionTestTask(numThreads int) {
// 物理仿真N体问题
func (s *Service) physicsSimulation(multi bool) int {
func (s *App) physicsSimulation(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -392,7 +398,7 @@ func (s *Service) physicsSimulation(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) physicsSimulationTask(numThreads int) {
func (s *App) physicsSimulationTask(numThreads int) {
const (
numBodies = 4000
steps = 30
@@ -479,7 +485,7 @@ func (s *Service) physicsSimulationTask(numThreads int) {
// JSON解析
func (s *Service) jsonProcessing(multi bool) int {
func (s *App) jsonProcessing(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
@@ -490,7 +496,7 @@ func (s *Service) jsonProcessing(multi bool) int {
return s.calculateCpuScore(duration)
}
func (s *Service) jsonProcessingTask(numThreads int) {
func (s *App) jsonProcessingTask(numThreads int) {
numElements := 1000000
elementsPerThread := numElements / numThreads
@@ -531,7 +537,7 @@ func (s *Service) jsonProcessingTask(numThreads int) {
// 内存性能
func (s *Service) memoryTestTask() map[string]any {
func (s *App) memoryTestTask() map[string]any {
results := make(map[string]any)
dataSize := 500 * 1024 * 1024 // 500 MB
data := make([]byte, dataSize)
@@ -549,7 +555,7 @@ func (s *Service) memoryTestTask() map[string]any {
return results
}
func (s *Service) memoryBandwidthTest(data []byte) string {
func (s *App) memoryBandwidthTest(data []byte) string {
dataSize := len(data)
startTime := time.Now()
@@ -566,7 +572,7 @@ func (s *Service) memoryBandwidthTest(data []byte) string {
return fmt.Sprintf("%.2f MB/s", speed)
}
func (s *Service) memoryLatencyTest(data []byte) string {
func (s *App) memoryLatencyTest(data []byte) string {
dataSize := len(data)
indices := rand.Perm(dataSize)
@@ -585,7 +591,7 @@ func (s *Service) memoryLatencyTest(data []byte) string {
// 硬盘IO
func (s *Service) diskTestTask() map[string]any {
func (s *App) 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 文件
@@ -601,7 +607,7 @@ func (s *Service) diskTestTask() map[string]any {
return results
}
func (s *Service) diskIOTest(blockSize int64, fileSize int64) map[string]any {
func (s *App) diskIOTest(blockSize int64, fileSize int64) map[string]any {
result := make(map[string]any)
tempFile := fmt.Sprintf("tempfile_%d", blockSize)
defer os.Remove(tempFile)
@@ -619,7 +625,7 @@ func (s *Service) diskIOTest(blockSize int64, fileSize int64) map[string]any {
return result
}
func (s *Service) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
func (s *App) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
totalBlocks := fileSize / blockSize
data := make([]byte, blockSize)
@@ -653,7 +659,7 @@ func (s *Service) diskWriteTest(fileName string, blockSize int64, fileSize int64
return speed, iops
}
func (s *Service) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
func (s *App) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
totalBlocks := fileSize / blockSize
data := make([]byte, blockSize)

View File

@@ -1,18 +0,0 @@
package benchmark
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "benchmark",
Route: func(r chi.Router) {
service := NewService()
r.Post("/test", service.Test)
},
})
}

View File

@@ -3,18 +3,25 @@ package docker
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/docker/daemon.json")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -24,7 +31,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,19 +0,0 @@
package docker
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "docker",
Route: func(r chi.Router) {
service := NewService()
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/go-rat/utils/str"
"github.com/spf13/cast"
@@ -17,18 +18,28 @@ import (
"github.com/TheTNB/panel/pkg/shell"
)
type Service struct {
type App struct {
websiteRepo biz.WebsiteRepo
}
func NewService() *Service {
return &Service{
websiteRepo: nil, // TODO fixme
func NewApp(website biz.WebsiteRepo) *App {
return &App{
websiteRepo: website,
}
}
func (s *App) Route(r chi.Router) {
r.Get("/jails", s.List)
r.Post("/jails", s.Create)
r.Delete("/jails", s.Delete)
r.Get("/jails/{name}", s.BanList)
r.Post("/unban", s.Unban)
r.Post("/whiteList", s.SetWhiteList)
r.Get("/whiteList", s.GetWhiteList)
}
// List 所有规则
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
func (s *App) List(w http.ResponseWriter, r *http.Request) {
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -75,7 +86,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
}
// Create 添加规则
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Add](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -202,7 +213,7 @@ bantime = ` + jailBanTime + `
}
// Delete 删除规则
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Delete](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -236,7 +247,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
}
// BanList 获取封禁列表
func (s *Service) BanList(w http.ResponseWriter, r *http.Request) {
func (s *App) BanList(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[BanList](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -281,7 +292,7 @@ func (s *Service) BanList(w http.ResponseWriter, r *http.Request) {
}
// Unban 解封
func (s *Service) Unban(w http.ResponseWriter, r *http.Request) {
func (s *App) Unban(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Unban](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -297,7 +308,7 @@ func (s *Service) Unban(w http.ResponseWriter, r *http.Request) {
}
// SetWhiteList 设置白名单
func (s *Service) SetWhiteList(w http.ResponseWriter, r *http.Request) {
func (s *App) SetWhiteList(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SetWhiteList](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -331,7 +342,7 @@ func (s *Service) SetWhiteList(w http.ResponseWriter, r *http.Request) {
}
// GetWhiteList 获取白名单
func (s *Service) GetWhiteList(w http.ResponseWriter, r *http.Request) {
func (s *App) GetWhiteList(w http.ResponseWriter, r *http.Request) {
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,24 +0,0 @@
package fail2ban
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "fail2ban",
Route: func(r chi.Router) {
service := NewService()
r.Get("/jails", service.List)
r.Post("/jails", service.Create)
r.Delete("/jails", service.Delete)
r.Get("/jails/{name}", service.BanList)
r.Post("/unban", service.Unban)
r.Post("/whiteList", service.SetWhiteList)
r.Get("/whiteList", service.GetWhiteList)
},
})
}

View File

@@ -4,19 +4,26 @@ import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Name](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -32,7 +39,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,19 +0,0 @@
package frp
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "frp",
Route: func(r chi.Router) {
service := NewService()
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -4,24 +4,31 @@ import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read(fmt.Sprintf("%s/server/gitea/app.ini", app.Root))
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,19 +0,0 @@
package gitea
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "gitea",
Route: func(r chi.Router) {
service := NewService()
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -1,29 +0,0 @@
package apps
import (
"github.com/go-chi/chi/v5"
_ "github.com/TheTNB/panel/internal/apps/benchmark"
_ "github.com/TheTNB/panel/internal/apps/docker"
_ "github.com/TheTNB/panel/internal/apps/fail2ban"
_ "github.com/TheTNB/panel/internal/apps/frp"
_ "github.com/TheTNB/panel/internal/apps/gitea"
_ "github.com/TheTNB/panel/internal/apps/memcached"
_ "github.com/TheTNB/panel/internal/apps/mysql"
_ "github.com/TheTNB/panel/internal/apps/nginx"
_ "github.com/TheTNB/panel/internal/apps/php"
_ "github.com/TheTNB/panel/internal/apps/phpmyadmin"
_ "github.com/TheTNB/panel/internal/apps/podman"
_ "github.com/TheTNB/panel/internal/apps/postgresql"
_ "github.com/TheTNB/panel/internal/apps/pureftpd"
_ "github.com/TheTNB/panel/internal/apps/redis"
_ "github.com/TheTNB/panel/internal/apps/rsync"
_ "github.com/TheTNB/panel/internal/apps/s3fs"
_ "github.com/TheTNB/panel/internal/apps/supervisor"
_ "github.com/TheTNB/panel/internal/apps/toolbox"
"github.com/TheTNB/panel/pkg/apploader"
)
func Boot(r chi.Router) {
apploader.Boot(r)
}

View File

@@ -6,19 +6,27 @@ import (
"net/http"
"regexp"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("memcached")
if err != nil {
service.Error(w, http.StatusInternalServerError, "failed to get Memcached status: %v", err)
@@ -66,7 +74,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, data)
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/systemd/system/memcached.service")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -76,7 +84,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,20 +0,0 @@
package memcached
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "memcached",
Route: func(r chi.Router) {
service := NewService()
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"os"
"regexp"
"github.com/go-chi/chi/v5"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/app"
@@ -19,18 +20,29 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct {
type App struct {
settingRepo biz.SettingRepo
}
func NewService() *Service {
return &Service{
settingRepo: nil, // TODO fixme
func NewApp(setting biz.SettingRepo) *App {
return &App{
settingRepo: setting,
}
}
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Post("/clearErrorLog", s.ClearErrorLog)
r.Get("/slowLog", s.SlowLog)
r.Post("/clearSlowLog", s.ClearSlowLog)
r.Get("/rootPassword", s.GetRootPassword)
r.Post("/rootPassword", s.SetRootPassword)
}
// GetConfig 获取配置
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(app.Root + "/server/mysql/conf/my.cnf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取配置失败")
@@ -41,7 +53,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
}
// UpdateConfig 保存配置
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -62,7 +74,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
}
// Load 获取负载
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取root密码失败")
@@ -142,7 +154,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
}
// ClearErrorLog 清空错误日志
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if err := systemctl.LogsClear("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -152,12 +164,12 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
}
// SlowLog 获取慢查询日志
func (s *Service) SlowLog(w http.ResponseWriter, r *http.Request) {
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-slow.log", app.Root))
}
// ClearSlowLog 清空慢查询日志
func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -167,7 +179,7 @@ func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
}
// GetRootPassword 获取root密码
func (s *Service) GetRootPassword(w http.ResponseWriter, r *http.Request) {
func (s *App) GetRootPassword(w http.ResponseWriter, r *http.Request) {
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取root密码失败")
@@ -182,7 +194,7 @@ func (s *Service) GetRootPassword(w http.ResponseWriter, r *http.Request) {
}
// SetRootPassword 设置root密码
func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) {
func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SetRootPassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -212,7 +224,7 @@ func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) getSock() string {
func (s *App) getSock() string {
if io.Exists("/tmp/mysql.sock") {
return "/tmp/mysql.sock"
}

View File

@@ -1,25 +0,0 @@
package mysql
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "mysql",
Route: func(r chi.Router) {
service := NewService()
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/rootPassword", service.GetRootPassword)
r.Post("/rootPassword", service.SetRootPassword)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-resty/resty/v2"
"github.com/spf13/cast"
@@ -18,15 +19,23 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct {
type App struct {
// Dependent services
}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.SaveConfig)
r.Get("/errorLog", s.ErrorLog)
r.Post("/clearErrorLog", s.ClearErrorLog)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取配置失败")
@@ -36,7 +45,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) SaveConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -57,11 +66,11 @@ func (s *Service) SaveConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/%s", app.Root, "wwwlogs/nginx-error.log"))
}
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/%s", app.Root, "wwwlogs/nginx-error.log"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -70,7 +79,7 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
client := resty.New().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
if err != nil || !resp.IsSuccess() {

View File

@@ -1,22 +0,0 @@
package nginx
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "nginx",
Route: func(r chi.Router) {
service := NewService()
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.SaveConfig)
r.Get("/errorLog", service.ErrorLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
},
})
}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-resty/resty/v2"
"github.com/spf13/cast"
@@ -19,19 +20,39 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct {
type App struct {
version uint
taskRepo biz.TaskRepo
}
func NewService(version uint) *Service {
return &Service{
version: version,
taskRepo: nil, // TODO fixme
func NewApp(task biz.TaskRepo) *App {
return &App{
taskRepo: task,
}
}
func (s *Service) SetCli(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(version uint) func(r chi.Router) {
return func(r chi.Router) {
php := new(App)
php.version = version
php.taskRepo = s.taskRepo
r.Post("/setCli", php.SetCli)
r.Get("/config", php.GetConfig)
r.Post("/config", php.UpdateConfig)
r.Get("/fpmConfig", php.GetFPMConfig)
r.Post("/fpmConfig", php.UpdateFPMConfig)
r.Get("/load", php.Load)
r.Get("/errorLog", php.ErrorLog)
r.Get("/slowLog", php.SlowLog)
r.Post("/clearErrorLog", php.ClearErrorLog)
r.Post("/clearSlowLog", php.ClearSlowLog)
r.Get("/extensions", php.ExtensionList)
r.Post("/extensions", php.InstallExtension)
r.Delete("/extensions", php.UninstallExtension)
}
}
func (s *App) SetCli(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("ln -sf %s/server/php/%d/bin/php /usr/bin/php", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -40,7 +61,7 @@ func (s *Service) SetCli(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, s.version))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -50,7 +71,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -65,7 +86,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, s.version))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -75,7 +96,7 @@ func (s *Service) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -90,7 +111,7 @@ func (s *Service) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
var raw map[string]any
client := resty.New().SetTimeout(10 * time.Second)
_, err := client.R().SetResult(&raw).Get(fmt.Sprintf("http://127.0.0.1/phpfpm_status/%d?json", s.version))
@@ -116,15 +137,15 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, loads)
}
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/php-fpm.log", app.Root, s.version))
}
func (s *Service) SlowLog(w http.ResponseWriter, r *http.Request) {
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/slow.log", app.Root, s.version))
}
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/server/php/%d/var/log/php-fpm.log", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -133,7 +154,7 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/server/php/%d/var/log/slow.log", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -142,7 +163,7 @@ func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) ExtensionList(w http.ResponseWriter, r *http.Request) {
func (s *App) ExtensionList(w http.ResponseWriter, r *http.Request) {
extensions := s.getExtensions()
raw, err := shell.Execf("%s/server/php/%d/bin/php -m", app.Root, s.version)
if err != nil {
@@ -165,7 +186,7 @@ func (s *Service) ExtensionList(w http.ResponseWriter, r *http.Request) {
service.Success(w, extensions)
}
func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
func (s *App) InstallExtension(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExtensionSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -196,7 +217,7 @@ func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
func (s *App) UninstallExtension(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExtensionSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -227,7 +248,7 @@ func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) getExtensions() []Extension {
func (s *App) getExtensions() []Extension {
extensions := []Extension{
{
Name: "fileinfo",
@@ -395,7 +416,7 @@ func (s *Service) getExtensions() []Extension {
return extensions
}
func (s *Service) checkExtension(slug string) bool {
func (s *App) checkExtension(slug string) bool {
extensions := s.getExtensions()
for _, item := range extensions {

View File

@@ -1,35 +0,0 @@
package php
import (
"fmt"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
php := []uint{74, 80, 81, 82, 83, 84}
for _, version := range php {
apploader.Register(&types.App{
Slug: fmt.Sprintf("php%d", version),
Route: func(r chi.Router) {
service := NewService(version)
r.Post("/setCli", service.SetCli)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/load", service.Load)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
}
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/spf13/cast"
@@ -17,13 +18,20 @@ import (
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) Info(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/info", s.Info)
r.Post("/port", s.UpdatePort)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
files, err := io.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
@@ -58,7 +66,7 @@ func (s *Service) Info(w http.ResponseWriter, r *http.Request) {
})
}
func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdatePort](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -98,7 +106,7 @@ func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -108,7 +116,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,21 +0,0 @@
package phpmyadmin
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "phpmyadmin",
Route: func(r chi.Router) {
service := NewService()
r.Get("/info", service.Info)
r.Post("/port", service.UpdatePort)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -3,18 +3,27 @@ package podman
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/registryConfig", s.GetRegistryConfig)
r.Post("/registryConfig", s.UpdateRegistryConfig)
r.Get("/storageConfig", s.GetStorageConfig)
r.Post("/storageConfig", s.UpdateStorageConfig)
}
func (s *App) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/containers/registries.conf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -24,7 +33,7 @@ func (s *Service) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -44,7 +53,7 @@ func (s *Service) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/containers/storage.conf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -54,7 +63,7 @@ func (s *Service) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateStorageConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateStorageConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,21 +0,0 @@
package podman
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "podman",
Route: func(r chi.Router) {
service := NewService()
r.Get("/registryConfig", service.GetRegistryConfig)
r.Post("/registryConfig", service.UpdateRegistryConfig)
r.Get("/storageConfig", service.GetStorageConfig)
r.Post("/storageConfig", service.UpdateStorageConfig)
},
})
}

View File

@@ -5,6 +5,8 @@ import (
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
@@ -13,14 +15,24 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/userConfig", s.GetUserConfig)
r.Post("/userConfig", s.UpdateUserConfig)
r.Get("/load", s.Load)
r.Get("/log", s.Log)
r.Post("/clearLog", s.ClearLog)
}
// GetConfig 获取配置
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
// 获取配置
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root))
if err != nil {
@@ -32,7 +44,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
}
// UpdateConfig 保存配置
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -53,7 +65,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
}
// GetUserConfig 获取用户配置
func (s *Service) GetUserConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetUserConfig(w http.ResponseWriter, r *http.Request) {
// 获取配置
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root))
if err != nil {
@@ -65,7 +77,7 @@ func (s *Service) GetUserConfig(w http.ResponseWriter, r *http.Request) {
}
// UpdateUserConfig 保存用户配置
func (s *Service) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -86,7 +98,7 @@ func (s *Service) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
}
// Load 获取负载
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, _ := systemctl.Status("postgresql")
if !status {
service.Success(w, []types.NV{})
@@ -131,12 +143,12 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
}
// Log 获取日志
func (s *Service) Log(w http.ResponseWriter, r *http.Request) {
func (s *App) Log(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly)))
}
// ClearLog 清空日志
func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("rm -rf %s/server/postgresql/logs/postgresql-*.log", app.Root); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return

View File

@@ -1,24 +0,0 @@
package postgresql
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "postgresql",
Route: func(r chi.Router) {
service := NewService()
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/userConfig", service.GetUserConfig)
r.Post("/userConfig", service.UpdateUserConfig)
r.Get("/load", service.Load)
r.Get("/log", service.Log)
r.Post("/clearLog", service.ClearLog)
},
})
}

View File

@@ -5,6 +5,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/spf13/cast"
@@ -16,14 +17,23 @@ import (
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *App) Route(r chi.Router) {
r.Get("/users", s.List)
r.Post("/users", s.Create)
r.Delete("/users/{username}", s.Delete)
r.Post("/users/{username}/password", s.ChangePassword)
r.Get("/port", s.GetPort)
r.Post("/port", s.UpdatePort)
}
// List 获取用户列表
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
func (s *App) List(w http.ResponseWriter, r *http.Request) {
listRaw, err := shell.Execf("pure-pw list")
if err != nil {
service.Success(w, chix.M{
@@ -55,7 +65,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
}
// Create 创建用户
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Create](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -91,7 +101,7 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
}
// Delete 删除用户
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Delete](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -111,7 +121,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
}
// ChangePassword 修改密码
func (s *Service) ChangePassword(w http.ResponseWriter, r *http.Request) {
func (s *App) ChangePassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ChangePassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -131,7 +141,7 @@ func (s *Service) ChangePassword(w http.ResponseWriter, r *http.Request) {
}
// GetPort 获取端口
func (s *Service) GetPort(w http.ResponseWriter, r *http.Request) {
func (s *App) GetPort(w http.ResponseWriter, r *http.Request) {
port, err := shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PureFtpd端口失败")
@@ -142,7 +152,7 @@ func (s *Service) GetPort(w http.ResponseWriter, r *http.Request) {
}
// UpdatePort 设置端口
func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdatePort](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,23 +0,0 @@
package pureftpd
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "pureftpd",
Route: func(r chi.Router) {
service := NewService()
r.Get("/users", service.List)
r.Post("/users", service.Create)
r.Delete("/users/{username}", service.Delete)
r.Post("/users/{username}/password", service.ChangePassword)
r.Get("/port", service.GetPort)
r.Post("/port", service.UpdatePort)
},
})
}

View File

@@ -5,6 +5,8 @@ import (
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
@@ -13,13 +15,19 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("redis")
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取 Redis 状态失败")
@@ -65,7 +73,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, data)
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/redis/redis.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -75,7 +83,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,20 +0,0 @@
package redis
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "redis",
Route: func(r chi.Router) {
service := NewService()
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/go-rat/utils/str"
@@ -15,13 +16,22 @@ import (
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
func (s *App) Route(r chi.Router) {
r.Get("/modules", s.List)
r.Post("/modules", s.Create)
r.Post("/modules/{name}", s.Update)
r.Delete("/modules/{name}", s.Delete)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) List(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -86,7 +96,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
})
}
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Create](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -131,7 +141,7 @@ secrets file = /etc/rsyncd.secrets
service.Success(w, nil)
}
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Delete](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -173,7 +183,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *Service) Update(w http.ResponseWriter, r *http.Request) {
func (s *App) Update(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Update](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -229,7 +239,7 @@ secrets file = /etc/rsyncd.secrets
service.Success(w, nil)
}
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -239,7 +249,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, config)
}
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,23 +0,0 @@
package rsync
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "rsync",
Route: func(r chi.Router) {
service := NewService()
r.Get("/modules", service.List)
r.Post("/modules", service.Create)
r.Post("/modules/{name}", service.Update)
r.Delete("/modules/{name}", service.Delete)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/spf13/cast"
@@ -15,18 +16,24 @@ import (
"github.com/TheTNB/panel/pkg/shell"
)
type Service struct {
type App struct {
settingRepo biz.SettingRepo
}
func NewService() *Service {
return &Service{
settingRepo: nil, // TODO fixme
func NewApp(setting biz.SettingRepo) *App {
return &App{
settingRepo: setting,
}
}
func (s *App) Route(r chi.Router) {
r.Get("/mounts", s.List)
r.Post("/mounts", s.Create)
r.Delete("/mounts", s.Delete)
}
// List 所有 S3fs 挂载
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
func (s *App) List(w http.ResponseWriter, r *http.Request) {
var s3fsList []Mount
list, err := s.settingRepo.Get("s3fs", "[]")
if err != nil {
@@ -48,7 +55,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
}
// Create 添加 S3fs 挂载
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Create](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -133,7 +140,7 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
}
// Delete 删除 S3fs 挂载
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Delete](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,20 +0,0 @@
package s3fs
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "s3fs",
Route: func(r chi.Router) {
service := NewService()
r.Get("/mounts", service.List)
r.Post("/mounts", service.Create)
r.Delete("/mounts", service.Delete)
},
})
}

View File

@@ -4,6 +4,7 @@ import (
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/spf13/cast"
@@ -14,11 +15,11 @@ import (
"github.com/TheTNB/panel/pkg/systemctl"
)
type Service struct {
type App struct {
name string
}
func NewService() *Service {
func NewApp() *App {
var name string
if os.IsRHEL() {
name = "supervisord"
@@ -26,18 +27,35 @@ func NewService() *Service {
name = "supervisor"
}
return &Service{
return &App{
name: name,
}
}
func (s *App) Route(r chi.Router) {
r.Get("/service", s.Service)
r.Post("/clearLog", s.ClearLog)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/processes", s.Processes)
r.Post("/processes/{process}/start", s.StartProcess)
r.Post("/processes/{process}/stop", s.StopProcess)
r.Post("/processes/{process}/restart", s.RestartProcess)
r.Get("/processes/{process}/log", s.ProcessLog)
r.Post("/processes/{process}/clearLog", s.ClearProcessLog)
r.Get("/processes/{process}", s.ProcessConfig)
r.Post("/processes/{process}", s.UpdateProcessConfig)
r.Delete("/processes/{process}", s.DeleteProcess)
r.Post("/processes", s.CreateProcess)
}
// Service 获取服务名称
func (s *Service) Service(w http.ResponseWriter, r *http.Request) {
func (s *App) Service(w http.ResponseWriter, r *http.Request) {
service.Success(w, s.name)
}
// ClearLog 清空日志
func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -47,7 +65,7 @@ func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
}
// GetConfig 获取配置
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
var config string
var err error
if os.IsRHEL() {
@@ -65,7 +83,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
}
// UpdateConfig 保存配置
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -92,7 +110,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
}
// Processes 进程列表
func (s *Service) Processes(w http.ResponseWriter, r *http.Request) {
func (s *App) Processes(w http.ResponseWriter, r *http.Request) {
out, err := shell.Execf(`supervisorctl status | awk '{print $1}'`)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -131,7 +149,7 @@ func (s *Service) Processes(w http.ResponseWriter, r *http.Request) {
}
// StartProcess 启动进程
func (s *Service) StartProcess(w http.ResponseWriter, r *http.Request) {
func (s *App) StartProcess(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -147,7 +165,7 @@ func (s *Service) StartProcess(w http.ResponseWriter, r *http.Request) {
}
// StopProcess 停止进程
func (s *Service) StopProcess(w http.ResponseWriter, r *http.Request) {
func (s *App) StopProcess(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -163,7 +181,7 @@ func (s *Service) StopProcess(w http.ResponseWriter, r *http.Request) {
}
// RestartProcess 重启进程
func (s *Service) RestartProcess(w http.ResponseWriter, r *http.Request) {
func (s *App) RestartProcess(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -179,7 +197,7 @@ func (s *Service) RestartProcess(w http.ResponseWriter, r *http.Request) {
}
// ProcessLog 进程日志
func (s *Service) ProcessLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ProcessLog(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -202,7 +220,7 @@ func (s *Service) ProcessLog(w http.ResponseWriter, r *http.Request) {
}
// ClearProcessLog 清空进程日志
func (s *Service) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
func (s *App) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -230,7 +248,7 @@ func (s *Service) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
}
// ProcessConfig 获取进程配置
func (s *Service) ProcessConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) ProcessConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -253,7 +271,7 @@ func (s *Service) ProcessConfig(w http.ResponseWriter, r *http.Request) {
}
// UpdateProcessConfig 保存进程配置
func (s *Service) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateProcessConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -279,7 +297,7 @@ func (s *Service) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
}
// CreateProcess 添加进程
func (s *Service) CreateProcess(w http.ResponseWriter, r *http.Request) {
func (s *App) CreateProcess(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[CreateProcess](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -318,7 +336,7 @@ stdout_logfile_maxbytes=2MB
}
// DeleteProcess 删除进程
func (s *Service) DeleteProcess(w http.ResponseWriter, r *http.Request) {
func (s *App) DeleteProcess(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,31 +0,0 @@
package supervisor
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "supervisor",
Route: func(r chi.Router) {
service := NewService()
r.Get("/service", service.Service)
r.Post("/clearLog", service.ClearLog)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/processes", service.Processes)
r.Post("/processes/{process}/start", service.StartProcess)
r.Post("/processes/{process}/stop", service.StopProcess)
r.Post("/processes/{process}/restart", service.RestartProcess)
r.Get("/processes/{process}/log", service.ProcessLog)
r.Post("/processes/{process}/clearLog", service.ClearProcessLog)
r.Get("/processes/{process}", service.ProcessConfig)
r.Post("/processes/{process}", service.UpdateProcessConfig)
r.Delete("/processes/{process}", service.DeleteProcess)
r.Post("/processes", service.CreateProcess)
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/spf13/cast"
@@ -18,14 +19,30 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type Service struct{}
type App struct{}
func NewService() *Service {
return &Service{}
func NewApp() *App {
return &App{}
}
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("/syncTime", s.SyncTime)
r.Get("/hostname", s.GetHostname)
r.Post("/hostname", s.UpdateHostname)
r.Get("/hosts", s.GetHosts)
r.Post("/hosts", s.UpdateHosts)
r.Post("/rootPassword", s.UpdateRootPassword)
}
// GetDNS 获取 DNS 信息
func (s *Service) GetDNS(w http.ResponseWriter, r *http.Request) {
func (s *App) GetDNS(w http.ResponseWriter, r *http.Request) {
raw, err := io.Read("/etc/resolv.conf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -46,7 +63,7 @@ func (s *Service) GetDNS(w http.ResponseWriter, r *http.Request) {
}
// UpdateDNS 设置 DNS 信息
func (s *Service) UpdateDNS(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateDNS(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[DNS](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -66,7 +83,7 @@ func (s *Service) UpdateDNS(w http.ResponseWriter, r *http.Request) {
}
// GetSWAP 获取 SWAP 信息
func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) {
var total, used, free string
var size int64
if io.Exists(filepath.Join(app.Root, "swap")) {
@@ -104,7 +121,7 @@ func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
}
// UpdateSWAP 设置 SWAP 信息
func (s *Service) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SWAP](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -172,7 +189,7 @@ func (s *Service) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
}
// GetTimezone 获取时区
func (s *Service) GetTimezone(w http.ResponseWriter, r *http.Request) {
func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) {
raw, err := shell.Execf("timedatectl | grep zone")
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取时区信息失败")
@@ -207,7 +224,7 @@ func (s *Service) GetTimezone(w http.ResponseWriter, r *http.Request) {
}
// UpdateTimezone 设置时区
func (s *Service) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Timezone](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -223,7 +240,7 @@ func (s *Service) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
}
// UpdateTime 设置时间
func (s *Service) UpdateTime(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateTime(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Time](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -240,7 +257,7 @@ func (s *Service) UpdateTime(w http.ResponseWriter, r *http.Request) {
}
// SyncTime 同步时间
func (s *Service) SyncTime(w http.ResponseWriter, r *http.Request) {
func (s *App) SyncTime(w http.ResponseWriter, r *http.Request) {
now, err := ntp.Now()
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -256,13 +273,13 @@ func (s *Service) SyncTime(w http.ResponseWriter, r *http.Request) {
}
// GetHostname 获取主机名
func (s *Service) GetHostname(w http.ResponseWriter, r *http.Request) {
func (s *App) GetHostname(w http.ResponseWriter, r *http.Request) {
hostname, _ := io.Read("/etc/hostname")
service.Success(w, strings.TrimSpace(hostname))
}
// UpdateHostname 设置主机名
func (s *Service) UpdateHostname(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateHostname(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Hostname](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -281,13 +298,13 @@ func (s *Service) UpdateHostname(w http.ResponseWriter, r *http.Request) {
}
// GetHosts 获取 hosts 信息
func (s *Service) GetHosts(w http.ResponseWriter, r *http.Request) {
func (s *App) GetHosts(w http.ResponseWriter, r *http.Request) {
hosts, _ := io.Read("/etc/hosts")
service.Success(w, hosts)
}
// UpdateHosts 设置 hosts 信息
func (s *Service) UpdateHosts(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateHosts(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Hosts](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
@@ -303,7 +320,7 @@ func (s *Service) UpdateHosts(w http.ResponseWriter, r *http.Request) {
}
// UpdateRootPassword 设置 root 密码
func (s *Service) UpdateRootPassword(w http.ResponseWriter, r *http.Request) {
func (s *App) UpdateRootPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Password](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)

View File

@@ -1,30 +0,0 @@
package toolbox
import (
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/types"
)
func init() {
apploader.Register(&types.App{
Slug: "toolbox",
Route: func(r chi.Router) {
service := NewService()
r.Get("/dns", service.GetDNS)
r.Post("/dns", service.UpdateDNS)
r.Get("/swap", service.GetSWAP)
r.Post("/swap", service.UpdateSWAP)
r.Get("/timezone", service.GetTimezone)
r.Post("/timezone", service.UpdateTimezone)
r.Post("/time", service.UpdateTime)
r.Post("/syncTime", service.SyncTime)
r.Get("/hostname", service.GetHostname)
r.Post("/hostname", service.UpdateHostname)
r.Get("/hosts", service.GetHosts)
r.Post("/hosts", service.UpdateHosts)
r.Post("/rootPassword", service.UpdateRootPassword)
},
})
}

View File

@@ -28,5 +28,4 @@ type TaskRepo interface {
Delete(id uint) error
UpdateStatus(id uint, status TaskStatus) error
Push(task *Task) error
DispatchWaiting()
}

View File

@@ -2,20 +2,18 @@ package bootstrap
import (
"crypto/tls"
"log/slog"
"fmt"
"net/http"
"github.com/bddjr/hlfhr"
"github.com/go-chi/chi/v5"
"github.com/go-rat/sessions"
"github.com/knadh/koanf/v2"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/http/middleware"
"github.com/TheTNB/panel/internal/route"
)
func NewRouter(conf *koanf.Koanf, db *gorm.DB, log *slog.Logger, session *sessions.Manager, middlewares *middleware.Middlewares, http *route.Http, ws *route.Ws) (*chi.Mux, error) {
func NewRouter(middlewares *middleware.Middlewares, http *route.Http, ws *route.Ws) (*chi.Mux, error) {
r := chi.NewRouter()
// add middleware
@@ -30,7 +28,7 @@ func NewRouter(conf *koanf.Koanf, db *gorm.DB, log *slog.Logger, session *sessio
func NewHttp(conf *koanf.Koanf, r *chi.Mux) (*hlfhr.Server, error) {
srv := hlfhr.New(&http.Server{
Addr: conf.MustString("http.address"),
Addr: fmt.Sprintf(":%d", conf.MustInt("http.port")),
Handler: http.AllowQuerySemicolons(r),
MaxHeaderBytes: 2048 << 20,
})

View File

@@ -8,9 +8,9 @@ import (
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/apps"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/pkg/api"
"github.com/TheTNB/panel/pkg/apploader"
)
type cacheRepo struct {
@@ -58,8 +58,7 @@ func (r *cacheRepo) UpdateApps() error {
// 去除本地不存在的应用
*remote = slices.Clip(slices.DeleteFunc(*remote, func(app *api.App) bool {
_, err = apploader.Get(app.Slug)
return err != nil
return !slices.Contains(apps.Slugs(), app.Slug)
}))
encoded, err := json.Marshal(remote)

View File

@@ -5,24 +5,30 @@ import (
"fmt"
"slices"
"github.com/TheTNB/panel/internal/app"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/db"
)
type databaseRepo struct {
db *gorm.DB
server biz.DatabaseServerRepo
user biz.DatabaseUserRepo
}
func NewDatabaseRepo(server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
return &databaseRepo{server: server, user: user}
func NewDatabaseRepo(db *gorm.DB, server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
return &databaseRepo{
db: db,
server: server,
user: user,
}
}
func (r databaseRepo) List(page, limit uint) ([]*biz.Database, int64, error) {
var databaseServer []*biz.DatabaseServer
if err := app.Orm.Model(&biz.DatabaseServer{}).Order("id desc").Find(&databaseServer).Error; err != nil {
if err := r.db.Model(&biz.DatabaseServer{}).Order("id desc").Find(&databaseServer).Error; err != nil {
return nil, 0, err
}

View File

@@ -7,7 +7,6 @@ import (
"gorm.io/gorm"
"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"
@@ -120,7 +119,7 @@ func (r databaseServerRepo) Delete(id uint) error {
// ClearUsers 删除指定服务器的所有用户,只是删除面板记录,不会实际删除
func (r databaseServerRepo) ClearUsers(serverID uint) error {
return app.Orm.Where("server_id = ?", serverID).Delete(&biz.DatabaseUser{}).Error
return r.db.Where("server_id = ?", serverID).Delete(&biz.DatabaseUser{}).Error
}
func (r databaseServerRepo) Sync(id uint) error {

View File

@@ -4,23 +4,28 @@ import (
"fmt"
"slices"
"github.com/TheTNB/panel/internal/app"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/db"
)
type databaseUserRepo struct {
db *gorm.DB
server biz.DatabaseServerRepo
}
func NewDatabaseUserRepo(server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
return &databaseUserRepo{server: server}
func NewDatabaseUserRepo(db *gorm.DB, server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
return &databaseUserRepo{
db: db,
server: server,
}
}
func (r databaseUserRepo) Count() (int64, error) {
var count int64
if err := app.Orm.Model(&biz.DatabaseUser{}).Count(&count).Error; err != nil {
if err := r.db.Model(&biz.DatabaseUser{}).Count(&count).Error; err != nil {
return 0, err
}
@@ -30,7 +35,7 @@ func (r databaseUserRepo) Count() (int64, error) {
func (r databaseUserRepo) List(page, limit uint) ([]*biz.DatabaseUser, int64, error) {
var user []*biz.DatabaseUser
var total int64
err := app.Orm.Model(&biz.DatabaseUser{}).Preload("Server").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&user).Error
err := r.db.Model(&biz.DatabaseUser{}).Preload("Server").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&user).Error
for u := range slices.Values(user) {
r.fillUser(u)
@@ -41,7 +46,7 @@ func (r databaseUserRepo) List(page, limit uint) ([]*biz.DatabaseUser, int64, er
func (r databaseUserRepo) Get(id uint) (*biz.DatabaseUser, error) {
user := new(biz.DatabaseUser)
if err := app.Orm.Preload("Server").Where("id = ?", id).First(user).Error; err != nil {
if err := r.db.Preload("Server").Where("id = ?", id).First(user).Error; err != nil {
return nil, err
}
@@ -97,14 +102,14 @@ func (r databaseUserRepo) Create(req *request.DatabaseUserCreate) error {
}
}
if err = app.Orm.FirstOrInit(user, user).Error; err != nil {
if err = r.db.FirstOrInit(user, user).Error; err != nil {
return err
}
user.Password = req.Password
user.Remark = req.Remark
return app.Orm.Save(user).Error
return r.db.Save(user).Error
}
func (r databaseUserRepo) Update(req *request.DatabaseUserUpdate) error {
@@ -156,7 +161,7 @@ func (r databaseUserRepo) Update(req *request.DatabaseUserUpdate) error {
user.Password = req.Password
user.Remark = req.Remark
return app.Orm.Save(user).Error
return r.db.Save(user).Error
}
func (r databaseUserRepo) UpdateRemark(req *request.DatabaseUserUpdateRemark) error {
@@ -167,7 +172,7 @@ func (r databaseUserRepo) UpdateRemark(req *request.DatabaseUserUpdateRemark) er
user.Remark = req.Remark
return app.Orm.Save(user).Error
return r.db.Save(user).Error
}
func (r databaseUserRepo) Delete(id uint) error {
@@ -198,7 +203,7 @@ func (r databaseUserRepo) Delete(id uint) error {
_ = postgres.UserDrop(user.Username)
}
return app.Orm.Where("id = ?", id).Delete(&biz.DatabaseUser{}).Error
return r.db.Where("id = ?", id).Delete(&biz.DatabaseUser{}).Error
}
func (r databaseUserRepo) DeleteByNames(serverID uint, names []string) error {
@@ -215,7 +220,7 @@ func (r databaseUserRepo) DeleteByNames(serverID uint, names []string) error {
}
defer mysql.Close()
users := make([]*biz.DatabaseUser, 0)
if err = app.Orm.Where("server_id = ? AND username IN ?", serverID, names).Find(&users).Error; err != nil {
if err = r.db.Where("server_id = ? AND username IN ?", serverID, names).Find(&users).Error; err != nil {
return err
}
for name := range slices.Values(names) {
@@ -239,7 +244,7 @@ func (r databaseUserRepo) DeleteByNames(serverID uint, names []string) error {
}
}
return app.Orm.Where("server_id = ? AND username IN ?", serverID, names).Delete(&biz.DatabaseUser{}).Error
return r.db.Where("server_id = ? AND username IN ?", serverID, names).Delete(&biz.DatabaseUser{}).Error
}
func (r databaseUserRepo) fillUser(user *biz.DatabaseUser) {

View File

@@ -7,7 +7,6 @@ import (
"github.com/spf13/cast"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
)
@@ -53,12 +52,12 @@ func (r monitorRepo) UpdateSetting(setting *request.MonitorSetting) error {
}
func (r monitorRepo) Clear() error {
return app.Orm.Where("1 = 1").Delete(&biz.Monitor{}).Error
return r.db.Where("1 = 1").Delete(&biz.Monitor{}).Error
}
func (r monitorRepo) List(start, end time.Time) ([]*biz.Monitor, error) {
var monitors []*biz.Monitor
if err := app.Orm.Where("created_at BETWEEN ? AND ?", start, end).Find(&monitors).Error; err != nil {
if err := r.db.Where("created_at BETWEEN ? AND ?", start, end).Find(&monitors).Error; err != nil {
return nil, err
}

View File

@@ -6,7 +6,6 @@ import (
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/queuejob"
"github.com/TheTNB/panel/pkg/queue"
@@ -70,31 +69,3 @@ func (r *taskRepo) Push(task *biz.Task) error {
task.ID,
})
}
// TODO 修复此功能
func (r *taskRepo) DispatchWaiting() {
// cli下不处理
if app.IsCli {
return
}
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 := 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 := r.queue.Push(queuejob.NewProcessTask(r.log, r), []any{
task.ID,
}); err != nil {
r.log.Warn("failed to push task", slog.Any("err", err))
return
}
}
}

View File

@@ -32,10 +32,6 @@ func (r *Monitoring) Run() {
return
}
// 将等待中的任务分发
//task := data.NewTaskRepo()
//_ = task.DispatchWaiting()
monitor, err := r.settingRepo.Get(biz.SettingKeyMonitor)
if err != nil || !cast.ToBool(monitor) {
return

View File

@@ -40,6 +40,7 @@ type Http struct {
monitor *service.MonitorService
setting *service.SettingService
systemctl *service.SystemctlService
apps *apps.Loader
}
func NewHttp(
@@ -68,6 +69,7 @@ func NewHttp(
monitor *service.MonitorService,
setting *service.SettingService,
systemctl *service.SystemctlService,
apps *apps.Loader,
) *Http {
return &Http{
user: user,
@@ -95,6 +97,7 @@ func NewHttp(
monitor: monitor,
setting: setting,
systemctl: systemctl,
apps: apps,
}
}
@@ -344,7 +347,7 @@ func (route *Http) Register(r *chi.Mux) {
})
r.Route("/apps", func(r chi.Router) {
apps.Boot(r)
route.apps.Register(r)
})
})

View File

@@ -34,6 +34,7 @@ type CliService struct {
hr string
api *api.API
conf *koanf.Koanf
db *gorm.DB
appRepo biz.AppRepo
cacheRepo biz.CacheRepo
userRepo biz.UserRepo
@@ -44,11 +45,12 @@ type CliService struct {
hash hash.Hasher
}
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 {
func NewCliService(conf *koanf.Koanf, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService {
return &CliService{
hr: `+----------------------------------------------------`,
api: api.NewAPI(app.Version),
conf: conf,
db: db,
appRepo: appRepo,
cacheRepo: cache,
userRepo: user,
@@ -120,7 +122,7 @@ func (s *CliService) Fix(ctx context.Context, cmd *cli.Command) error {
func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
user := new(biz.User)
if err := app.Orm.Where("id", 1).First(user).Error; err != nil {
if err := s.db.Where("id", 1).First(user).Error; err != nil {
return fmt.Errorf("获取管理员信息失败:%v", err)
}
@@ -135,7 +137,7 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
user.Email = str.Random(8) + "@example.com"
}
if err = app.Orm.Save(user).Error; err != nil {
if err = s.db.Save(user).Error; err != nil {
return fmt.Errorf("管理员信息保存失败:%v", err)
}
@@ -185,7 +187,7 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
func (s *CliService) UserList(ctx context.Context, cmd *cli.Command) error {
users := make([]biz.User, 0)
if err := app.Orm.Find(&users).Error; err != nil {
if err := s.db.Find(&users).Error; err != nil {
return fmt.Errorf("获取用户列表失败:%v", err)
}
@@ -207,7 +209,7 @@ func (s *CliService) UserName(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("新用户名不能为空")
}
if err := app.Orm.Where("username", oldUsername).First(user).Error; err != nil {
if err := s.db.Where("username", oldUsername).First(user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("用户不存在")
} else {
@@ -216,7 +218,7 @@ func (s *CliService) UserName(ctx context.Context, cmd *cli.Command) error {
}
user.Username = newUsername
if err := app.Orm.Save(user).Error; err != nil {
if err := s.db.Save(user).Error; err != nil {
return fmt.Errorf("用户名修改失败:%v", err)
}
@@ -235,7 +237,7 @@ func (s *CliService) UserPassword(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("密码长度不能小于6")
}
if err := app.Orm.Where("username", username).First(user).Error; err != nil {
if err := s.db.Where("username", username).First(user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("用户不存在")
} else {
@@ -248,7 +250,7 @@ func (s *CliService) UserPassword(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("密码生成失败:%v", err)
}
user.Password = hashed
if err = app.Orm.Save(user).Error; err != nil {
if err = s.db.Save(user).Error; err != nil {
return fmt.Errorf("密码修改失败:%v", err)
}
@@ -702,7 +704,7 @@ func (s *CliService) AppWrite(ctx context.Context, cmd *cli.Command) error {
}
newApp := new(biz.App)
if err := app.Orm.Where("slug", slug).First(newApp).Error; err != nil {
if err := s.db.Where("slug", slug).First(newApp).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("获取应用失败:%v", err)
}
@@ -710,7 +712,7 @@ func (s *CliService) AppWrite(ctx context.Context, cmd *cli.Command) error {
newApp.Slug = slug
newApp.Channel = channel
newApp.Version = version
if err := app.Orm.Save(newApp).Error; err != nil {
if err := s.db.Save(newApp).Error; err != nil {
return fmt.Errorf("应用保存失败:%v", err)
}
@@ -723,7 +725,7 @@ func (s *CliService) AppRemove(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("参数不能为空")
}
if err := app.Orm.Where("slug", slug).Delete(&biz.App{}).Error; err != nil {
if err := s.db.Where("slug", slug).Delete(&biz.App{}).Error; err != nil {
return fmt.Errorf("应用删除失败:%v", err)
}
@@ -745,7 +747,7 @@ func (s *CliService) SyncTime(ctx context.Context, cmd *cli.Command) error {
}
func (s *CliService) ClearTask(ctx context.Context, cmd *cli.Command) error {
if err := app.Orm.Model(&biz.Task{}).
if err := s.db.Model(&biz.Task{}).
Where("status", biz.TaskStatusRunning).Or("status", biz.TaskStatusWaiting).
Update("status", biz.TaskStatusFailed).
Error; err != nil {
@@ -763,7 +765,7 @@ func (s *CliService) GetSetting(ctx context.Context, cmd *cli.Command) error {
}
setting := new(biz.Setting)
if err := app.Orm.Where("key", key).First(setting).Error; err != nil {
if err := s.db.Where("key", key).First(setting).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("设置不存在")
}
@@ -783,14 +785,14 @@ func (s *CliService) WriteSetting(ctx context.Context, cmd *cli.Command) error {
}
setting := new(biz.Setting)
if err := app.Orm.Where("key", key).First(setting).Error; err != nil {
if err := s.db.Where("key", key).First(setting).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("获取设置失败:%v", err)
}
}
setting.Key = biz.SettingKey(key)
setting.Value = value
if err := app.Orm.Save(setting).Error; err != nil {
if err := s.db.Save(setting).Error; err != nil {
return fmt.Errorf("设置保存失败:%v", err)
}
@@ -803,7 +805,7 @@ func (s *CliService) RemoveSetting(ctx context.Context, cmd *cli.Command) error
return fmt.Errorf("参数不能为空")
}
if err := app.Orm.Where("key", key).Delete(&biz.Setting{}).Error; err != nil {
if err := s.db.Where("key", key).Delete(&biz.Setting{}).Error; err != nil {
return fmt.Errorf("设置删除失败:%v", err)
}
@@ -812,7 +814,7 @@ func (s *CliService) RemoveSetting(ctx context.Context, cmd *cli.Command) error
func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error {
var check biz.User
if err := app.Orm.First(&check).Error; err == nil {
if err := s.db.First(&check).Error; err == nil {
return fmt.Errorf("已经初始化过了")
}
@@ -824,7 +826,7 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error {
{Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "wwwroot")},
{Key: biz.SettingKeyVersion, Value: app.Version},
}
if err := app.Orm.Create(&settings).Error; err != nil {
if err := s.db.Create(&settings).Error; err != nil {
return fmt.Errorf("初始化失败:%v", err)
}

View File

@@ -1,54 +0,0 @@
// Package apploader 面板应用加载器
package apploader
import (
"fmt"
"log"
"sync"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/types"
)
var apps sync.Map
func Register(app *types.App) {
if _, ok := apps.Load(app.Slug); ok {
log.Fatalf("app %s already exists", app.Slug)
}
apps.Store(app.Slug, app)
}
func Get(slug string) (*types.App, error) {
if app, ok := apps.Load(slug); ok {
return app.(*types.App), nil
}
return nil, fmt.Errorf("app %s not found", slug)
}
func All() []*types.App {
var list []*types.App
apps.Range(func(_, app any) bool {
if p, ok := app.(*types.App); ok {
list = append(list, p)
}
return true
})
// 排序
/*slices.SortFunc(list, func(a, b *types.App) int {
return cmp.Compare(a.Order, b.Order)
})*/
return list
}
func Boot(r chi.Router) {
apps.Range(func(_, app any) bool {
if p, ok := app.(*types.App); ok {
r.Route(fmt.Sprintf("/%s", p.Slug), p.Route)
}
return true
})
}