Files
YMhut-box-C-/server/unified-management/internal/web/response.go
T
QWQLwToo 962a2f2143
build-winui / winui (push) Waiting to run
更新 update 门户站点界面和后台功能
2026-06-27 18:09:11 +08:00

179 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package web
import (
"encoding/json"
"net/http"
"strings"
)
func withSecurity(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Referrer-Policy", "same-origin")
next.ServeHTTP(w, r)
})
}
func writeJSON(w http.ResponseWriter, status int, payload any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(payload)
}
func writeSSE(w http.ResponseWriter, event string, payload any) {
data, _ := json.Marshal(payload)
_, _ = w.Write([]byte("event: " + event + "\n"))
_, _ = w.Write([]byte("data: " + string(data) + "\n\n"))
}
func writeError(w http.ResponseWriter, status int, code string, err error) {
message := ""
if err != nil {
message = err.Error()
}
writeJSON(w, status, map[string]any{"ok": false, "error": code, "message": localizedErrorMessage(code, message)})
}
func localizedErrorMessage(code, message string) string {
raw := strings.TrimSpace(message)
lower := strings.ToLower(raw)
exact := map[string]string{
"current password is invalid": "当前密码不正确",
"new password is required": "新密码不能为空",
"new password must be at least 8 characters": "新密码至少需要 8 位",
"new password cannot be admin": "新密码不能为 admin",
"new password must be different from current password": "新密码不能与当前密码相同",
"invalid password or captcha": "密码或验证码不正确",
"login required": "需要登录后继续操作",
"csrf token required": "页面安全令牌已失效,请刷新后重试",
"csrf token invalid": "页面安全令牌无效,请刷新后重试",
"code is required": "缺少反馈编号",
"revisionid is required": "请选择要恢复的历史版本",
"post required": "该操作需要使用 POST 请求",
"get required": "该操作需要使用 GET 请求",
"file is required": "请选择要上传的文件",
"invalid filename": "文件名不合法",
"path escape rejected": "文件路径不合法",
"check job not found": "未找到心跳检测任务",
"streaming is not supported": "当前运行环境不支持实时事件流",
"source api_url is empty": "接口地址不能为空",
"database is not available": "数据库当前不可用",
"provider must be sqlite or mysql": "数据库类型必须是 SQLite 或 MySQL",
"mysql connection is required": "请填写 MySQL 连接信息",
"mysql database is required": "请填写 MySQL 数据库名",
"mysql username is required": "请填写 MySQL 数据库用户",
"sqlite path is required": "请填写 SQLite 路径",
"mysql_dsn is required": "请填写 MySQL DSN",
"remote database is not configured": "远端 MySQL 未配置",
"database sync is already running": "数据库同步正在执行,请稍后再试",
"mail is not configured": "邮件通知尚未配置完整",
"release notices are not configured": "版本日志功能尚未配置",
"legacy sync service is not configured": "旧项目同步服务尚未配置",
"update-info requires app_version or title": "更新 JSON 需要填写 app_version 或 title",
"media-types requires categories array": "媒体源 JSON 需要包含 categories 数组",
"version or app_version is required": "版本日志需要填写 version 或 app_version",
}
if translated, ok := exact[lower]; ok {
return translated
}
byCode := map[string]string{
"UNAUTHORIZED": "需要登录后继续操作",
"LOGIN_FAILED": "登录失败,请检查密码和验证码",
"PASSWORD_CHANGE_FAILED": "密码修改失败",
"INVALID_PAYLOAD": "提交内容格式不正确",
"DATABASE_TEST_FAILED": "数据库连接测试失败",
"DATABASE_SAVE_FAILED": "数据库配置保存失败",
"DATABASE_IMPORT_FAILED": "SQLite 导入远端库失败",
"DATABASE_SYNC_FAILED": "远端库同步回本地失败",
"LEGACY_SAVE_FAILED": "兼容 JSON 保存失败",
"LEGACY_VALIDATE_FAILED": "兼容 JSON 校验失败",
"LEGACY_RESTORE_FAILED": "兼容 JSON 恢复失败",
"NOTICE_SAVE_FAILED": "版本日志保存失败",
"NOTICE_VALIDATE_FAILED": "版本日志校验失败",
"NOTICE_RESTORE_FAILED": "版本日志恢复失败",
"PACKAGE_UPLOAD_FAILED": "发布包上传失败",
"SOURCE_SAVE_FAILED": "接口源保存失败",
"CHECK_FAILED": "接口健康检测失败",
"SYNC_FAILED": "同步操作失败",
"FORBIDDEN": "没有权限执行该操作",
"METHOD_NOT_ALLOWED": "请求方法不正确",
"FILE_REQUIRED": "请选择要上传的文件",
"CHECK_JOB_NOT_FOUND": "未找到心跳检测任务",
"SSE_UNSUPPORTED": "当前运行环境不支持实时事件流",
"SOURCES_FAILED": "接口源数据加载失败",
"ENDPOINTS_FAILED": "客户端接口数据加载失败",
"DASHBOARD_FAILED": "仪表盘数据加载失败",
"AUDIT_FAILED": "审计日志加载失败",
"FEEDBACK_LIST_FAILED": "反馈列表加载失败",
"FEEDBACK_UPDATE_FAILED": "反馈工单更新失败",
"MAIL_CONFIG_FAILED": "邮件配置保存失败",
"MAIL_TEST_FAILED": "测试邮件发送失败",
"MAIL_RETRY_FAILED": "反馈邮件重试失败",
"NOTICE_NOT_FOUND": "未找到版本日志",
"NOTICES_FAILED": "版本日志加载失败",
"MEDIA_TYPES_FAILED": "媒体源 JSON 加载失败",
"SOURCE_CALL_FAILED": "接口调用状态上报失败",
"IMPORT_FAILED": "导入失败",
"PATH_FAILED": "路径解析失败",
"INVALID_UPLOAD": "上传内容不正确",
"BOOTSTRAP_FAILED": "后台初始化信息加载失败",
"CAPTCHA_FAILED": "验证码加载失败",
"TOO_LARGE": "反馈包过大",
"MISSING_FIELD": "缺少旧反馈提交字段",
"INVALID_TIMESTAMP": "反馈提交时间已过期",
"INVALID_SIGNATURE": "反馈签名校验失败",
"INVALID_PACKAGE": "反馈包格式不正确",
"INVALID_ENCRYPTED_PACKAGE": "反馈加密包格式不正确",
"DECRYPT_FAILED": "反馈包解密失败",
"HASH_MISMATCH": "反馈包哈希校验失败",
"SERVER_CONFIG": "反馈服务配置异常",
}
if translated, ok := byCode[code]; ok {
if raw == "" || strings.EqualFold(raw, code) {
return translated
}
return translated + "" + raw
}
if raw == "" {
return "操作失败"
}
return raw
}
func cleanPath(path string) string {
if path == "" {
return "/"
}
if path != "/" {
path = strings.TrimRight(path, "/")
}
if path == "" {
return "/"
}
return path
}
func requestBaseURL(r *http.Request, fallback string) string {
scheme := r.Header.Get("X-Forwarded-Proto")
if scheme == "" {
if r.TLS != nil {
scheme = "https"
} else {
scheme = "http"
}
}
if r.Host != "" {
return scheme + "://" + r.Host
}
return strings.TrimRight(fallback, "/")
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
return strings.TrimSpace(value)
}
}
return ""
}