diff --git a/internal/apps/mysql/service.go b/internal/apps/mysql/service.go index 96551efe..b4e8f19f 100644 --- a/internal/apps/mysql/service.go +++ b/internal/apps/mysql/service.go @@ -216,7 +216,7 @@ func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) { } } if err = s.settingRepo.Set(biz.SettingKeyMySQLRootPassword, req.Password); err != nil { - service.Error(w, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err)) + service.Error(w, http.StatusInternalServerError, fmt.Sprintf("设置保存失败:%v", err)) return } diff --git a/internal/apps/openresty/service.go b/internal/apps/openresty/service.go index 4f90c8b1..fc703305 100644 --- a/internal/apps/openresty/service.go +++ b/internal/apps/openresty/service.go @@ -50,7 +50,7 @@ func (s *Service) SaveConfig(w http.ResponseWriter, r *http.Request) { if err = systemctl.Reload("openresty"); err != nil { _, err = shell.Execf("openresty -t") - service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载服务失败: %v", err)) + service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载服务失败:%v", err)) return } diff --git a/internal/apps/php/service.go b/internal/apps/php/service.go index 7272298f..b766f7a9 100644 --- a/internal/apps/php/service.go +++ b/internal/apps/php/service.go @@ -86,7 +86,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) { client := resty.New().SetTimeout(10 * time.Second) resp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1/phpfpm_status/%d", s.version)) if err != nil || !resp.IsSuccess() { - service.Error(w, http.StatusInternalServerError, "获取负载状态失败") + service.Error(w, http.StatusInternalServerError, fmt.Sprintf("获取负载状态失败:%v", err)) return } @@ -94,19 +94,19 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) { dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"} regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"} - data := make([]types.NV, len(dataKeys)) + loads := make([]types.NV, len(dataKeys)) for i := range dataKeys { - data[i].Name = dataKeys[i] + loads[i].Name = dataKeys[i] r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i])) match := r.FindStringSubmatch(raw) if len(match) > 1 { - data[i].Value = strings.TrimSpace(match[1]) + loads[i].Value = strings.TrimSpace(match[1]) } } - service.Success(w, data) + service.Success(w, loads) } func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) { diff --git a/internal/apps/phpmyadmin/service.go b/internal/apps/phpmyadmin/service.go index 6cd13b85..d60ed3bb 100644 --- a/internal/apps/phpmyadmin/service.go +++ b/internal/apps/phpmyadmin/service.go @@ -88,7 +88,7 @@ func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) { if err = systemctl.Reload("openresty"); err != nil { _, err = shell.Execf("openresty -t") - service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err)) + service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败:%v", err)) return } @@ -119,7 +119,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) { if err = systemctl.Reload("openresty"); err != nil { _, err = shell.Execf("openresty -t") - service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err)) + service.Error(w, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败:%v", err)) return } diff --git a/internal/http/middleware/entrance.go b/internal/http/middleware/entrance.go new file mode 100644 index 00000000..8748f066 --- /dev/null +++ b/internal/http/middleware/entrance.go @@ -0,0 +1,46 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/go-rat/chix" + "github.com/spf13/cast" + + "github.com/TheTNB/panel/internal/app" +) + +// Entrance 确保通过正确的入口访问 +func Entrance(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + sess, err := app.Session.GetSession(r) + if err != nil { + render := chix.NewRender(w) + render.Status(http.StatusInternalServerError) + render.JSON(chix.M{ + "message": err.Error(), + }) + } + + entrance := app.Conf.String("http.entrance") + if strings.TrimSuffix(r.URL.Path, "/") == strings.TrimSuffix(entrance, "/") { + sess.Put("verify_entrance", true) + render := chix.NewRender(w, r) + render.Redirect("/login") + return + } + + if !app.Conf.Bool("app.debug") && + !cast.ToBool(sess.Get("verify_entrance", false)) && + r.URL.Path != "/robots.txt" { + render := chix.NewRender(w) + render.Status(http.StatusTeapot) + render.JSON(chix.M{ + "message": "请通过正确的入口访问", + }) + return + } + + next.ServeHTTP(w, r) + }) +} diff --git a/internal/http/middleware/middleware.go b/internal/http/middleware/middleware.go index 7c563bf5..f438fabe 100644 --- a/internal/http/middleware/middleware.go +++ b/internal/http/middleware/middleware.go @@ -19,5 +19,8 @@ func GlobalMiddleware() []func(http.Handler) http.Handler { middleware.Logger, middleware.Recoverer, middleware.Compress(5), + Entrance, + Status, + MustInstall, } } diff --git a/internal/http/middleware/must_install.go b/internal/http/middleware/must_install.go new file mode 100644 index 00000000..4e072b60 --- /dev/null +++ b/internal/http/middleware/must_install.go @@ -0,0 +1,52 @@ +package middleware + +import ( + "fmt" + "net/http" + "strings" + + "github.com/go-rat/chix" + + "github.com/TheTNB/panel/internal/data" +) + +// MustInstall 确保已安装应用 +func MustInstall(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var slugs []string + if strings.HasPrefix(r.URL.Path, "/api/website") { + slugs = append(slugs, "openresty") + } else if strings.HasPrefix(r.URL.Path, "/api/container") { + slugs = append(slugs, "podman", "docker") + } else if strings.HasPrefix(r.URL.Path, "/api/apps/") { + pathArr := strings.Split(r.URL.Path, "/") + if len(pathArr) < 4 { + render := chix.NewRender(w) + render.Status(http.StatusForbidden) + render.JSON(chix.M{ + "message": "应用不存在", + }) + return + } + slugs = append(slugs, pathArr[3]) + } + + flag := false + for _, s := range slugs { + if installed, _ := data.NewAppRepo().IsInstalled("slug = ?", s); installed { + flag = true + break + } + } + if !flag && len(slugs) > 0 { + render := chix.NewRender(w) + render.Status(http.StatusForbidden) + render.JSON(chix.M{ + "message": fmt.Sprintf("应用 %s 未安装", slugs), + }) + return + } + + next.ServeHTTP(w, r) + }) +} diff --git a/internal/http/middleware/must_login.go b/internal/http/middleware/must_login.go index 77bbd4e5..611db64b 100644 --- a/internal/http/middleware/must_login.go +++ b/internal/http/middleware/must_login.go @@ -13,7 +13,7 @@ import ( // MustLogin 确保已登录 func MustLogin(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session, err := app.Session.GetSession(r) + sess, err := app.Session.GetSession(r) if err != nil { render := chix.NewRender(w) render.Status(http.StatusInternalServerError) @@ -22,7 +22,7 @@ func MustLogin(next http.Handler) http.Handler { }) } - if session.Missing("user_id") { + if sess.Missing("user_id") { render := chix.NewRender(w) render.Status(http.StatusUnauthorized) render.JSON(chix.M{ @@ -31,7 +31,7 @@ func MustLogin(next http.Handler) http.Handler { return } - userID := cast.ToUint(session.Get("user_id")) + userID := cast.ToUint(sess.Get("user_id")) if userID == 0 { render := chix.NewRender(w) render.Status(http.StatusUnauthorized) diff --git a/internal/http/middleware/status.go b/internal/http/middleware/status.go new file mode 100644 index 00000000..8d98c39f --- /dev/null +++ b/internal/http/middleware/status.go @@ -0,0 +1,47 @@ +package middleware + +import ( + "net/http" + + "github.com/go-rat/chix" + + "github.com/TheTNB/panel/pkg/types" +) + +// Status 检查程序状态 +func Status(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch types.Status { + case types.StatusUpgrade: + render := chix.NewRender(w) + render.Status(http.StatusServiceUnavailable) + render.JSON(chix.M{ + "message": "面板升级中,请稍后刷新", + }) + return + case types.StatusMaintain: + render := chix.NewRender(w) + render.Status(http.StatusServiceUnavailable) + render.JSON(chix.M{ + "message": "面板正在运行维护任务,请稍后刷新", + }) + return + case types.StatusClosed: + render := chix.NewRender(w) + render.Status(http.StatusForbidden) + render.JSON(chix.M{ + "message": "面板已关闭", + }) + return + case types.StatusFailed: + render := chix.NewRender(w) + render.Status(http.StatusInternalServerError) + render.JSON(chix.M{ + "message": "面板运行出错,请检查排除或联系支持", + }) + return + default: + next.ServeHTTP(w, r) + } + }) +} diff --git a/internal/route/http.go b/internal/route/http.go index 98e9dd52..51224a24 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -47,9 +47,7 @@ func Http(r chi.Router) { }) r.Route("/website", func(r chi.Router) { - // TODO 修改前端 r.Use(middleware.MustLogin) - // r.Use(middleware.MustInstallWebServer) website := service.NewWebsiteService() r.Get("/defaultConfig", website.GetDefaultConfig) r.Post("/defaultConfig", website.UpdateDefaultConfig) diff --git a/internal/service/cli.go b/internal/service/cli.go index 51fef8f4..de939590 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -358,18 +358,18 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { settings := []biz.Setting{{Key: biz.SettingKeyName, Value: "耗子面板"}, {Key: biz.SettingKeyMonitor, Value: "1"}, {Key: biz.SettingKeyMonitorDays, Value: "30"}, {Key: biz.SettingKeyBackupPath, Value: filepath.Join(app.Root, "backup")}, {Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "wwwroot")}, {Key: biz.SettingKeyVersion, Value: app.Conf.String("app.version")}} if err := app.Orm.Create(&settings).Error; err != nil { - return fmt.Errorf("初始化失败: %v", err) + return fmt.Errorf("初始化失败:%v", err) } value, err := hash.NewArgon2id().Make(str.RandomString(32)) if err != nil { - return fmt.Errorf("初始化失败: %v", err) + return fmt.Errorf("初始化失败:%v", err) } user := data.NewUserRepo() _, err = user.Create("admin", value) if err != nil { - return fmt.Errorf("初始化失败: %v", err) + return fmt.Errorf("初始化失败:%v", err) } config := new(types.PanelConfig) diff --git a/pkg/types/config.go b/pkg/types/config.go index f8bec892..6edb9d45 100644 --- a/pkg/types/config.go +++ b/pkg/types/config.go @@ -8,6 +8,7 @@ type PanelConfig struct { } type PanelAppConfig struct { + Debug bool `yaml:"debug"` Key string `yaml:"key"` Locale string `yaml:"locale"` Timezone string `yaml:"timezone"` diff --git a/web/src/api/apps/php/index.ts b/web/src/api/apps/php/index.ts index 7cc097ce..08649a44 100644 --- a/web/src/api/apps/php/index.ts +++ b/web/src/api/apps/php/index.ts @@ -4,38 +4,38 @@ import { request } from '@/utils' export default { // 负载状态 - load: (version: number): Promise> => request.get(`/apps/php/${version}/load`), + load: (version: number): Promise> => request.get(`/apps/php${version}/load`), // 获取配置 config: (version: number): Promise> => - request.get(`/apps/php/${version}/config`), + request.get(`/apps/php${version}/config`), // 保存配置 saveConfig: (version: number, config: string): Promise> => - request.post(`/apps/php/${version}/config`, { config }), + request.post(`/apps/php${version}/config`, { config }), // 获取FPM配置 fpmConfig: (version: number): Promise> => - request.get(`/apps/php/${version}/fpmConfig`), + request.get(`/apps/php${version}/fpmConfig`), // 保存FPM配置 saveFPMConfig: (version: number, config: string): Promise> => - request.post(`/apps/php/${version}/fpmConfig`, { config }), + request.post(`/apps/php${version}/fpmConfig`, { config }), // 获取错误日志 errorLog: (version: number): Promise> => - request.get(`/apps/php/${version}/errorLog`), + request.get(`/apps/php${version}/errorLog`), // 清空错误日志 clearErrorLog: (version: number): Promise> => - request.post(`/apps/php/${version}/clearErrorLog`), + request.post(`/apps/php${version}/clearErrorLog`), // 获取慢日志 slowLog: (version: number): Promise> => - request.get(`/apps/php/${version}/slowLog`), + request.get(`/apps/php${version}/slowLog`), // 清空慢日志 clearSlowLog: (version: number): Promise> => - request.post(`/apps/php/${version}/clearSlowLog`), + request.post(`/apps/php${version}/clearSlowLog`), // 拓展列表 extensions: (version: number): Promise> => - request.get(`/apps/php/${version}/extensions`), + request.get(`/apps/php${version}/extensions`), // 安装拓展 installExtension: (version: number, slug: string): Promise> => - request.post(`/apps/php/${version}/extensions`, { slug }), + request.post(`/apps/php${version}/extensions`, { slug }), // 卸载拓展 uninstallExtension: (version: number, slug: string): Promise> => - request.delete(`/apps/php/${version}/extensions`, { params: { slug } }) + request.delete(`/apps/php${version}/extensions`, { params: { slug } }) }