管理后台可关全站动画,并细调代码折叠、平滑滚动、装饰入场;统一 lib/motion,尊重系统减少动态效果。 Co-authored-by: Cursor <cursoragent@cursor.com>
179 lines
6.1 KiB
Go
179 lines
6.1 KiB
Go
package handler
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
|
||
"github.com/freefire/jiang13-bbs/realtime"
|
||
"github.com/freefire/jiang13-bbs/service"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// GET /api/settings
|
||
func (h *Handlers) PublicSettings(c *gin.Context) {
|
||
s, err := h.Setting.Public()
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取站点设置失败"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, s)
|
||
}
|
||
|
||
// updateSettingsRequest 字段均为可选指针:只更新请求里出现的项,避免外观页覆盖其它设置
|
||
type updateSettingsRequest struct {
|
||
Accent *string `json:"accent"`
|
||
TrustReviewedPublish *bool `json:"trust_reviewed_publish"`
|
||
SiteName *string `json:"site_name"`
|
||
SiteDescription *string `json:"site_description"`
|
||
AllowRegister *bool `json:"allow_register"`
|
||
PostCooldownHours *int `json:"post_cooldown_hours"`
|
||
CodeBlockAutoFold *bool `json:"code_block_auto_fold"`
|
||
CodeBlockFoldLines *int `json:"code_block_fold_lines"`
|
||
UIAnimations *bool `json:"ui_animations"`
|
||
AnimCodeFold *bool `json:"anim_code_fold"`
|
||
AnimSmoothScroll *bool `json:"anim_smooth_scroll"`
|
||
AnimChrome *bool `json:"anim_chrome"`
|
||
}
|
||
|
||
func settingsPayload(saved service.PublicSiteSettings) gin.H {
|
||
return gin.H{
|
||
"accent": saved.Accent,
|
||
"trust_reviewed_publish": saved.TrustReviewedPublish,
|
||
"site_name": saved.SiteName,
|
||
"site_description": saved.SiteDescription,
|
||
"allow_register": saved.AllowRegister,
|
||
"post_cooldown_hours": saved.PostCooldownHours,
|
||
"code_block_auto_fold": saved.CodeBlockAutoFold,
|
||
"code_block_fold_lines": saved.CodeBlockFoldLines,
|
||
"ui_animations": saved.UIAnimations,
|
||
"anim_code_fold": saved.AnimCodeFold,
|
||
"anim_smooth_scroll": saved.AnimSmoothScroll,
|
||
"anim_chrome": saved.AnimChrome,
|
||
}
|
||
}
|
||
|
||
// PUT /api/admin/settings
|
||
func (h *Handlers) UpdateSettings(c *gin.Context) {
|
||
var req updateSettingsRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
|
||
return
|
||
}
|
||
if req.Accent == nil && req.TrustReviewedPublish == nil && req.SiteName == nil &&
|
||
req.SiteDescription == nil && req.AllowRegister == nil &&
|
||
req.PostCooldownHours == nil && req.CodeBlockAutoFold == nil && req.CodeBlockFoldLines == nil &&
|
||
req.UIAnimations == nil && req.AnimCodeFold == nil && req.AnimSmoothScroll == nil &&
|
||
req.AnimChrome == nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "未提供任何可更新的设置项"})
|
||
return
|
||
}
|
||
|
||
if req.Accent != nil {
|
||
if err := h.Setting.SetAccent(*req.Accent); err != nil {
|
||
if errors.Is(err, service.ErrInvalidAccent) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.TrustReviewedPublish != nil {
|
||
if err := h.Setting.SetTrustReviewedPublish(*req.TrustReviewedPublish); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.SiteName != nil {
|
||
if err := h.Setting.SetSiteName(*req.SiteName); err != nil {
|
||
if errors.Is(err, service.ErrInvalidSiteSetting) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "站点名称过长或无效"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.SiteDescription != nil {
|
||
if err := h.Setting.SetSiteDescription(*req.SiteDescription); err != nil {
|
||
if errors.Is(err, service.ErrInvalidSiteSetting) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "站点简介过长"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.AllowRegister != nil {
|
||
if err := h.Setting.SetAllowRegister(*req.AllowRegister); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.PostCooldownHours != nil {
|
||
if err := h.Setting.SetPostCooldownHours(*req.PostCooldownHours); err != nil {
|
||
if errors.Is(err, service.ErrInvalidSiteSetting) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "冷静期须为 0–168 小时"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.CodeBlockAutoFold != nil {
|
||
if err := h.Setting.SetCodeBlockAutoFold(*req.CodeBlockAutoFold); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.CodeBlockFoldLines != nil {
|
||
if err := h.Setting.SetCodeBlockFoldLines(*req.CodeBlockFoldLines); err != nil {
|
||
if errors.Is(err, service.ErrInvalidSiteSetting) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "折叠行数须为 0–100"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.UIAnimations != nil {
|
||
if err := h.Setting.SetUIAnimations(*req.UIAnimations); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.AnimCodeFold != nil {
|
||
if err := h.Setting.SetAnimCodeFold(*req.AnimCodeFold); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.AnimSmoothScroll != nil {
|
||
if err := h.Setting.SetAnimSmoothScroll(*req.AnimSmoothScroll); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
if req.AnimChrome != nil {
|
||
if err := h.Setting.SetAnimChrome(*req.AnimChrome); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存站点设置失败"})
|
||
return
|
||
}
|
||
}
|
||
|
||
saved, err := h.Setting.Public()
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取站点设置失败"})
|
||
return
|
||
}
|
||
|
||
payload := settingsPayload(saved)
|
||
h.Hub.BroadcastAll(realtime.Envelope{
|
||
Type: realtime.EventSettingsChanged,
|
||
Data: payload,
|
||
})
|
||
out := settingsPayload(saved)
|
||
out["ok"] = true
|
||
c.JSON(http.StatusOK, out)
|
||
}
|