Files
yunzerwebsiteallinone/go/controllers/platform_website_setting.go
2026-11-11 16:58:30 +08:00

227 lines
6.5 KiB
Go
Raw Permalink 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 controllers
import (
"encoding/json"
"io"
"strings"
"server/models"
"server/pkg/identity"
"server/services"
beego "github.com/beego/beego/v2/server/web"
)
// PlatformWebsiteSettingController 平台官网站点设置(平台端专用)
// 对应前端 views/website/setting/index.vue:
// - GET /platform/website/setting/info
// - POST /platform/website/setting/save
//
// 数据落在 yz_platform_site_setting(通用键值结构:title/value/tag/remark)。
// 该表完全数据驱动:表里插一行就是一个新的设置项,模板即可用 {yz:tag} 输出,
// platform 端界面也会自动出现该项,无需改动后端代码。
type PlatformWebsiteSettingController struct {
beego.Controller
}
func (c *PlatformWebsiteSettingController) jsonErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]any{"code": bizCode, "msg": msg}
_ = c.ServeJSON()
}
// Info GET /platform/website/setting/info
// 直接把表里所有设置项返回,前端按行渲染(新增设置项无需改代码):
// data.list = [{ id, title, value, tag, remark }]
func (c *PlatformWebsiteSettingController) Info() {
if _, err := requirePlatform(&c.Controller); err != nil {
code := identity.Status(err)
c.jsonErr(code, code, err.Error())
return
}
rows := models.GetPlatformSiteSettingRows()
list := make([]map[string]any, 0, len(rows))
for _, r := range rows {
list = append(list, map[string]any{
"id": r.ID,
"title": r.Title,
"value": r.Value,
"tag": r.Tag,
"remark": r.Remark,
})
}
c.Data["json"] = map[string]any{
"code": 200,
"msg": "success",
"data": map[string]any{
"list": list,
"values": models.GetPlatformSiteTagMap(),
},
}
_ = c.ServeJSON()
}
// Save POST /platform/website/setting/save
// 通用:表里已存在或新增的任意合法标签都可写入,不需要登记。
func (c *PlatformWebsiteSettingController) Save() {
if _, err := requirePlatform(&c.Controller); err != nil {
code := identity.Status(err)
c.jsonErr(code, code, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var payload map[string]string
if uerr := json.Unmarshal(raw, &payload); uerr != nil {
c.jsonErr(400, 400, "参数错误")
return
}
if len(payload) == 0 {
c.jsonErr(400, 400, "参数错误")
return
}
if strings.TrimSpace(payload["sitename"]) == "" {
c.jsonErr(400, 400, "站点名称不能为空")
return
}
// 统一去掉首尾空格,避免模板里输出多余空白
values := make(map[string]string, len(payload))
for k, v := range payload {
values[k] = strings.TrimSpace(v)
}
if err = models.SavePlatformSiteSettings(values); err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
c.Data["json"] = map[string]any{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
// loginBrandTags 登录页品牌/登录方式配置项,存放于 yz_platform_site_setting,
// 统一认证中心 /auth/login 渲染时读取。
// login_pwd_enabled / login_sms_enabled / login_qr_enabled:
// 值为 "0" 表示关闭该登录方式,未配置(空)默认开启;三者全关时登录页禁止登录。
var loginBrandTags = []string{
"login_title", "login_subtitle", "login_logo", "login_bg",
"login_pwd_enabled", "login_sms_enabled", "login_qr_enabled",
}
// LoginBrandInfo GET /platform/loginBrand
// 返回登录页品牌配置(主标题 / 副标题 / Logo / 背景图)。
func (c *PlatformWebsiteSettingController) LoginBrandInfo() {
if _, err := requirePlatform(&c.Controller); err != nil {
code := identity.Status(err)
c.jsonErr(code, code, err.Error())
return
}
all := models.GetPlatformSiteTagMap()
data := make(map[string]string, len(loginBrandTags))
for _, tag := range loginBrandTags {
data[tag] = all[tag]
}
c.Data["json"] = map[string]any{"code": 200, "msg": "success", "data": data}
_ = c.ServeJSON()
}
// SaveLoginBrand POST /platform/saveLoginBrand
// 入参 { login_title, login_subtitle, login_logo, login_bg }。
// 只接受这四个标签,避免与「站点设置」共用的 yz_platform_site_setting 被写入无关项。
func (c *PlatformWebsiteSettingController) SaveLoginBrand() {
if _, err := requirePlatform(&c.Controller); err != nil {
code := identity.Status(err)
c.jsonErr(code, code, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var payload map[string]string
if uerr := json.Unmarshal(raw, &payload); uerr != nil {
c.jsonErr(400, 400, "参数错误")
return
}
values := make(map[string]string, len(loginBrandTags))
for _, tag := range loginBrandTags {
if v, ok := payload[tag]; ok {
values[tag] = strings.TrimSpace(v)
}
}
if len(values) == 0 {
c.jsonErr(400, 400, "参数错误")
return
}
if len([]rune(values["login_title"])) > 60 {
c.jsonErr(400, 400, "主标题不能超过 60 个字符")
return
}
if len([]rune(values["login_subtitle"])) > 120 {
c.jsonErr(400, 400, "副标题不能超过 120 个字符")
return
}
// 只校验本次提交了的图片字段,避免「登录方式」等只传开关的保存把品牌图清空
for _, tag := range []string{"login_logo", "login_bg"} {
raw, submitted := payload[tag]
if !submitted {
continue
}
img, ok := normalizeLoginBrandImage(raw)
if !ok {
c.jsonErr(400, 400, "图片地址必须是 HTTPS 或站内相对路径")
return
}
values[tag] = img
}
if err = models.SavePlatformSiteSettings(values); err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
c.Data["json"] = map[string]any{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
// normalizeLoginBrandImage 校验登录页图片地址:
// - 站内相对路径(/uploads/...)直接放行;
// - HTTPS 完整地址放行;
// - HTTP 历史七牛域名尝试改写为存储配置的 HTTPS 基址,改不动则拒绝,
// 否则登录页会被浏览器按 Mixed Content 拦截。
func normalizeLoginBrandImage(raw string) (string, bool) {
raw = strings.TrimSpace(raw)
if raw == "" {
return "", true
}
if strings.HasPrefix(raw, "/") {
return raw, true
}
lower := strings.ToLower(raw)
if strings.HasPrefix(lower, "https://") {
return raw, true
}
if strings.HasPrefix(lower, "http://") {
rewritten := services.ResolvePublicURL("", "", raw)
if strings.HasPrefix(strings.ToLower(rewritten), "https://") {
return rewritten, true
}
return "", false
}
return "", false
}