diff --git a/app/console/commands/monitoring.go b/app/console/commands/monitoring.go index 3eeb28e5..4c7b9ae7 100644 --- a/app/console/commands/monitoring.go +++ b/app/console/commands/monitoring.go @@ -53,6 +53,8 @@ func (receiver *Monitoring) Handle(console.Context) error { info := tools.GetMonitoringInfo() + translate := facades.Lang(context.Background()) + // 去除部分数据以减少数据库存储 info.Disk = nil for _, cpu := range info.Cpus { @@ -72,7 +74,7 @@ func (receiver *Monitoring) Handle(console.Context) error { }) if err != nil { facades.Log().Infof("[面板] 系统监控保存失败: %s", err.Error()) - color.Redf("[面板] 系统监控保存失败: %s", err.Error()) + color.Redf(translate.Get("commands.panel:monitoring.fail")+": %s", err.Error()) return nil } diff --git a/app/console/commands/panel.go b/app/console/commands/panel.go index 128d78f8..d6f6cfc4 100644 --- a/app/console/commands/panel.go +++ b/app/console/commands/panel.go @@ -255,37 +255,37 @@ func (receiver *Panel) Handle(ctx console.Context) error { save := arg4 hr := `+----------------------------------------------------` if len(backupType) == 0 || len(name) == 0 || len(path) == 0 || len(save) == 0 { - color.Redln("参数错误") + color.Redln(translate.Get("commands.panel.backup.paramFail")) return nil } color.Greenln(hr) - color.Greenln("★ 开始备份 [" + carbon.Now().ToDateTimeString() + "]") + color.Greenln("★ " + translate.Get("commands.panel.backup.start") + " [" + carbon.Now().ToDateTimeString() + "]") color.Greenln(hr) if !tools.Exists(path) { if err := tools.Mkdir(path, 0644); err != nil { - color.Redln("|-创建备份目录失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error()) return nil } } switch backupType { case "website": - color.Yellowln("|-目标网站: " + name) + color.Yellowln("|-" + translate.Get("commands.panel.backup.targetSite") + ": " + name) var website models.Website if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil { - color.Redln("|-网站不存在") + color.Redln("|-" + translate.Get("commands.panel.backup.siteNotExist")) color.Greenln(hr) return nil } backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip" if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil { - color.Redln("|-备份失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error()) return nil } - color.Greenln("|-备份成功") + color.Greenln("|-" + translate.Get("commands.panel.backup.backupSuccess")) case "mysql": rootPassword := services.NewSettingImpl().Get(models.SettingKeyMysqlRootPassword) @@ -293,81 +293,81 @@ func (receiver *Panel) Handle(ctx console.Context) error { err := os.Setenv("MYSQL_PWD", rootPassword) if err != nil { - color.Redln("|-备份MySQL数据库失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.mysqlBackupFail") + ": " + err.Error()) color.Greenln(hr) return nil } - color.Greenln("|-目标MySQL数据库: " + name) - color.Greenln("|-开始导出") + color.Greenln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name) + color.Greenln("|-" + translate.Get("commands.panel.backup.startExport")) if _, err = tools.Exec(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil { - color.Redln("|-导出失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error()) return nil } - color.Greenln("|-导出成功") - color.Greenln("|-开始压缩") + color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess")) + color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress")) if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { - color.Redln("|-压缩失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error()) return nil } if err := tools.Remove("/tmp/" + backupFile); err != nil { - color.Redln("|-删除失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error()) return nil } - color.Greenln("|-压缩成功") - color.Greenln("|-开始移动") + color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess")) + color.Greenln("|-" + translate.Get("commands.panel.backup.startMove")) if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil { - color.Redln("|-移动失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error()) return nil } - color.Greenln("|-移动成功") + color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess")) _ = os.Unsetenv("MYSQL_PWD") - color.Greenln("|-备份成功") + color.Greenln("|-" + translate.Get("commands.panel.backup.success")) case "postgresql": backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql" check, err := tools.Exec(`su - postgres -c "psql -l" 2>&1`) if err != nil { - color.Redln("|-获取数据库失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error()) color.Greenln(hr) return nil } if !strings.Contains(check, name) { - color.Redln("|-数据库不存在") + color.Redln("|-" + translate.Get("commands.panel.backup.databaseNotExist")) color.Greenln(hr) return nil } - color.Greenln("|-目标PostgreSQL数据库: " + name) - color.Greenln("|-开始导出") + color.Greenln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name) + color.Greenln("|-" + translate.Get("commands.panel.backup.startExport")) if _, err = tools.Exec(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil { - color.Redln("|-导出失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error()) return nil } - color.Greenln("|-导出成功") - color.Greenln("|-开始压缩") + color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess")) + color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress")) if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { - color.Redln("|-压缩失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error()) return nil } if err := tools.Remove("/tmp/" + backupFile); err != nil { - color.Redln("|-删除失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error()) return nil } - color.Greenln("|-压缩成功") - color.Greenln("|-开始移动") + color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess")) + color.Greenln("|-" + translate.Get("commands.panel.backup.startMove")) if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil { - color.Redln("|-移动失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error()) return nil } - color.Greenln("|-移动成功") - color.Greenln("|-备份成功") + color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess")) + color.Greenln("|-" + translate.Get("commands.panel.backup.success")) } color.Greenln(hr) files, err := os.ReadDir(path) if err != nil { - color.Redln("|-清理失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error()) return nil } var filteredFiles []os.FileInfo @@ -385,15 +385,15 @@ func (receiver *Panel) Handle(ctx console.Context) error { }) for i := cast.ToInt(save); i < len(filteredFiles); i++ { fileToDelete := filepath.Join(path, filteredFiles[i].Name()) - color.Yellowln("|-清理备份: " + fileToDelete) + color.Yellowln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete) if err := tools.Remove(fileToDelete); err != nil { - color.Redln("|-清理失败: " + err.Error()) + color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error()) return nil } } - color.Greenln("|-清理完成") + color.Greenln("|-" + translate.Get("commands.panel.backup.cleanupSuccess")) color.Greenln(hr) - color.Greenln("☆ 备份完成 [" + carbon.Now().ToDateTimeString() + "]") + color.Greenln("☆ " + translate.Get("commands.panel.backup.success") + " [" + carbon.Now().ToDateTimeString() + "]") color.Greenln(hr) case "cutoff": @@ -576,14 +576,14 @@ func (receiver *Panel) Handle(ctx console.Context) error { path := arg4 php := arg5 if len(name) == 0 || len(domain) == 0 || len(port) == 0 || len(path) == 0 { - color.Redln("参数错误") + color.Redln(translate.Get("commands.panel.addSite.paramFail")) return nil } domains := strings.Split(domain, ",") ports := strings.Split(port, ",") if len(domains) == 0 || len(ports) == 0 { - color.Redln("参数错误") + color.Redln(translate.Get("commands.panel.addSite.paramFail")) return nil } @@ -599,7 +599,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { return nil } if id != 0 { - color.Redln("网站名已存在") + color.Redln(translate.Get("commands.panel.addSite.siteExist")) return nil } @@ -618,12 +618,12 @@ func (receiver *Panel) Handle(ctx console.Context) error { return nil } - color.Greenln("网站添加成功") + color.Greenln(translate.Get("commands.panel.addSite.success")) case "removeSite": name := arg1 if len(name) == 0 { - color.Redln("参数错误") + color.Redln(translate.Get("commands.panel.removeSite.paramFail")) return nil } @@ -634,7 +634,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { return nil } if id == 0 { - color.Redln("网站名不存在") + color.Redln(translate.Get("commands.panel.removeSite.siteNotExist")) return nil } @@ -643,7 +643,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { return nil } - color.Greenln("网站删除成功") + color.Greenln(translate.Get("commands.panel.removeSite.success")) case "installPlugin": slug := arg1 diff --git a/app/http/controllers/controller.go b/app/http/controllers/controller.go index c5fe445c..66014cb1 100644 --- a/app/http/controllers/controller.go +++ b/app/http/controllers/controller.go @@ -2,6 +2,7 @@ package controllers import ( "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" ) // SuccessResponse 通用成功响应 @@ -26,14 +27,14 @@ func Success(ctx http.Context, data any) http.Response { // Error 响应错误 func Error(ctx http.Context, code int, message string) http.Response { return ctx.Response().Json(code, &ErrorResponse{ - Message: "错误: " + message, + Message: facades.Lang(ctx).Get("messages.mistake") + ": " + message, }) } // ErrorSystem 响应系统错误 func ErrorSystem(ctx http.Context) http.Response { return ctx.Response().Json(http.StatusInternalServerError, &ErrorResponse{ - Message: "系统内部错误", + Message: facades.Lang(ctx).Get("errors.internal"), }) } diff --git a/app/http/middleware/jwt.go b/app/http/middleware/jwt.go index b862271c..1e6bc8f8 100644 --- a/app/http/middleware/jwt.go +++ b/app/http/middleware/jwt.go @@ -11,10 +11,11 @@ import ( // Jwt 确保通过 JWT 鉴权 func Jwt() http.Middleware { return func(ctx http.Context) { + translate := facades.Lang(ctx) token := ctx.Request().Header("Authorization", ctx.Request().Header("Sec-WebSocket-Protocol")) if len(token) == 0 { ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": "未登录", + "message": translate.Get("auth.token.missing"), }) return } @@ -26,7 +27,7 @@ func Jwt() http.Middleware { if err != nil { // 到达刷新时间上限 ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": "登录已过期", + "message": translate.Get("auth.token.expired"), }) return } @@ -34,7 +35,7 @@ func Jwt() http.Middleware { token = "Bearer " + token } else { ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": "登录已过期", + "message": translate.Get("auth.token.expired"), }) return } diff --git a/app/http/middleware/must_install.go b/app/http/middleware/must_install.go index 6768854a..46bcb7fb 100644 --- a/app/http/middleware/must_install.go +++ b/app/http/middleware/must_install.go @@ -4,6 +4,8 @@ import ( "strings" "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/translation" + "github.com/goravel/framework/facades" "panel/internal/services" ) @@ -12,6 +14,7 @@ import ( func MustInstall() http.Middleware { return func(ctx http.Context) { path := ctx.Request().Path() + translate := facades.Lang(ctx) var slug string if strings.HasPrefix(path, "/api/panel/website") { slug = "openresty" @@ -19,7 +22,7 @@ func MustInstall() http.Middleware { pathArr := strings.Split(path, "/") if len(pathArr) < 4 { ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{ - "message": "插件不存在", + "message": translate.Get("errors.plugin.notExist"), }) return } @@ -31,14 +34,18 @@ func MustInstall() http.Middleware { installedPlugins, err := services.NewPluginImpl().AllInstalled() if err != nil { ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{ - "message": "系统内部错误", + "message": translate.Get("errors.internal"), }) return } if installedPlugin.Slug != plugin.Slug { ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{ - "message": "插件 " + slug + " 未安装", + "message": translate.Get("errors.plugin.notInstalled", translation.Option{ + Replace: map[string]string{ + "slug": slug, + }, + }), }) return } @@ -53,7 +60,12 @@ func MustInstall() http.Middleware { _, requireFound := pluginsMap[require] if !requireFound { ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{ - "message": "插件 " + slug + " 需要依赖 " + require + " 插件", + "message": translate.Get("errors.plugin.dependent", translation.Option{ + Replace: map[string]string{ + "slug": slug, + "dependency": require, + }, + }), }) return } @@ -63,7 +75,12 @@ func MustInstall() http.Middleware { _, excludeFound := pluginsMap[exclude] if excludeFound { ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{ - "message": "插件 " + slug + " 不兼容 " + exclude + " 插件", + "message": translate.Get("errors.plugin.incompatible", translation.Option{ + Replace: map[string]string{ + "slug": slug, + "exclude": exclude, + }, + }), }) return } diff --git a/app/http/middleware/status.go b/app/http/middleware/status.go index c9525c1a..1964ee04 100644 --- a/app/http/middleware/status.go +++ b/app/http/middleware/status.go @@ -2,6 +2,7 @@ package middleware import ( "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" "panel/internal" ) @@ -9,25 +10,26 @@ import ( // Status 检查程序状态 func Status() http.Middleware { return func(ctx http.Context) { + translate := facades.Lang(ctx) switch internal.Status { case internal.StatusUpgrade: ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{ - "message": "面板升级中,请稍后", + "message": translate.Get("status.upgrade"), }) return case internal.StatusMaintain: ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{ - "message": "面板正在运行维护,请稍后", + "message": translate.Get("status.maintain"), }) return case internal.StatusClosed: ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{ - "message": "面板已关闭", + "message": translate.Get("status.closed"), }) return case internal.StatusFailed: ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{ - "message": "面板运行出错,请检查排除或联系支持", + "message": translate.Get("status.failed"), }) return default: diff --git a/lang/en.json b/lang/en.json index df706fc7..43eb7101 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,10 +1,17 @@ { + "auth": { + "token": { + "expired": "login has expired", + "missing": "not logged in" + } + }, "commands": { "panel:cert-renew": { "description": "[Panel] Certificate renewal" }, "panel:monitoring": { - "description": "[Panel] System Monitoring" + "description": "[Panel] System Monitoring", + "fail": "[Panel] System monitoring failed to save" }, "panel": { "description": "[Panel] Command line", @@ -47,7 +54,33 @@ "success": "tasks cleaned up successfully" }, "backup": { - "description": "back up website/MySQL database/PostgreSQL database to the specified directory and retain the specified amount" + "description": "back up website/MySQL database/PostgreSQL database to the specified directory and retain the specified amount", + "paramFail": "backup type, path, name and keep amount are required", + "start": "start backup", + "backupDirFail": "failed to create backup directory", + "targetSite": "target website", + "siteNotExist": "website does not exist", + "backupFail": "backup failed", + "backupSuccess": "backup successful", + "mysqlBackupFail": "MySQL database backup failed", + "targetMysql": "target MySQL database", + "startExport": "start exporting", + "exportFail": "export failed", + "exportSuccess": "export successful", + "startCompress": "start compressing", + "compressFail": "compression failed", + "compressSuccess": "compression successful", + "startMove": "start moving", + "moveFail": "move failed", + "moveSuccess": "move successful", + "databaseGetFail": "failed to get database", + "databaseNotExist": "database does not exist", + "targetPostgres": "target PostgreSQL database", + "cleanBackup" : "clean backup", + "cleanupFail": "cleanup failed", + "cleanupSuccess": "cleanup successful", + "deleteFail": "failed to delete", + "success": "backup completed" }, "cutoff": { "description": "cut website logs and keep specified amount", @@ -80,10 +113,16 @@ "success": "task has been submitted" }, "addSite": { - "description": "add website [domain name and port separated by commas]" + "description": "add website [domain name and port separated by commas]", + "paramFail": "name, domain, port and path are required", + "siteExist": "website already exists", + "success": "website added successfully" }, "removeSite": { - "description": "remove website" + "description": "remove website", + "paramFail": "name is required", + "siteNotExist": "website does not exist", + "success": "website deleted successfully" }, "init": { "description": "initialize the panel", @@ -144,5 +183,23 @@ "panel:task": { "description": "[Panel] Daily tasks" } + }, + "errors": { + "internal": "internal system error", + "plugin": { + "notExist": "plugin does not exist", + "notInstalled": "plugin :slug is not installed", + "dependent": "plugin :slug requires dependency :dependency", + "incompatible": "plugin :slug is incompatible with :exclude plugin" + } + }, + "messages": { + "mistake": "mistake" + }, + "status": { + "upgrade": "Panel is currently undergoing an upgrade. Please try again later.", + "maintain": "Panel is currently undergoing maintenance. Please try again later.", + "closed": "Panel is closed.", + "failed": "Panel encountered an error during operation. Please check the troubleshooting or contact support." } } \ No newline at end of file diff --git a/lang/zh_CN.json b/lang/zh_CN.json index 84d618ba..40ffbf76 100644 --- a/lang/zh_CN.json +++ b/lang/zh_CN.json @@ -1,10 +1,17 @@ { + "auth": { + "token": { + "expired": "登录已过期", + "missing": "未登录" + } + }, "commands": { "panel:cert-renew": { "description": "[面板] 证书续签" }, "panel:monitoring": { - "description": "[面板] 系统监控" + "description": "[面板] 系统监控", + "fail": "[面板] 系统监控保存失败" }, "panel": { "description": "[面板] 命令行", @@ -47,7 +54,33 @@ "success": "清理任务成功" }, "backup": { - "description": "备份网站 / MySQL数据库 / PostgreSQL数据库到指定目录并保留指定数量" + "description": "备份网站 / MySQL数据库 / PostgreSQL数据库到指定目录并保留指定数量", + "paramFail": "参数错误", + "start": "开始备份", + "backupDirFail": "创建备份目录失败", + "targetSite": "目标网站", + "siteNotExist": "网站不存在", + "backupFail": "备份失败", + "backupSuccess": "备份成功", + "mysqlBackupFail": "备份MySQL数据库失败", + "targetMysql": "目标MySQL数据库", + "startExport": "开始导出", + "exportFail": "导出失败", + "exportSuccess": "导出成功", + "startCompress": "开始压缩", + "compressFail": "压缩失败", + "compressSuccess": "压缩成功", + "startMove": "开始移动", + "moveFail": "移动失败", + "moveSuccess": "移动成功", + "databaseGetFail": "获取数据库失败", + "databaseNotExist": "数据库不存在", + "targetPostgres": "目标PostgreSQL数据库", + "cleanBackup": "清理备份", + "cleanupFail": "清理失败", + "cleanupSuccess": "清理完成", + "deleteFail": "删除失败", + "success": "备份完成" }, "cutoff": { "description": "切割网站日志并保留指定数量", @@ -80,10 +113,16 @@ "success": "任务已提交" }, "addSite": { - "description": "添加网站[域名和端口用英文逗号分隔]" + "description": "添加网站[域名和端口用英文逗号分隔]", + "paramFail": "参数错误", + "siteExist": "网站名已存在", + "success": "网站添加成功" }, "removeSite": { - "description": "删除网站" + "description": "删除网站", + "paramFail": "参数错误", + "siteNotExist": "网站名不存在", + "success": "网站删除成功" }, "init": { "description": "初始化面板", @@ -144,5 +183,23 @@ "panel:task": { "description": "[面板] 每日任务" } + }, + "errors": { + "internal": "系统内部错误", + "plugin": { + "notExist": "插件不存在", + "notInstalled": "插件 :slug 未安装", + "dependent": "插件 :slug 需要依赖 :dependency 插件", + "incompatible": "插件 :slug 不兼容 :exclude 插件" + } + }, + "messages": { + "mistake": "错误" + }, + "status": { + "upgrade": "面板升级中,请稍后", + "maintain": "面板正在运行维护,请稍后", + "closed": "面板已关闭", + "failed": "面板运行出错,请检查排除或联系支持" } } \ No newline at end of file