Files
jiang13-bbs/backend/handler/sidebar.go
freefire 7b327bc8cc feat: 站点访问统计与可配置侧边栏组件
- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面
- 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件
- 新增 widget 嵌入代码生成(widgetCode)与 widgetApi
- 遥测逻辑迁移至 handler 层,删除 service/telemetry
- env 示例补充 Docker 部署可信代理 IP 说明
2026-09-24 18:53:45 +08:00

124 lines
3.9 KiB
Go

package handler
import (
"errors"
"net/http"
"strconv"
"github.com/freefire/jiang13-bbs/model"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-gonic/gin"
)
// GET /api/sidebar 公开侧栏载荷(布局 + 启用中的自定义工具)
func (h *Handlers) GetSidebar(c *gin.Context) {
payload, err := h.Sidebar.PublicPayload()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取侧栏配置失败"})
return
}
c.JSON(http.StatusOK, payload)
}
// GET /api/admin/sidebar 管理端全量(生效配置 + 全部工具)
func (h *Handlers) AdminGetSidebar(c *gin.Context) {
cfg, err := h.Sidebar.GetConfig()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取侧栏配置失败"})
return
}
widgets, err := h.Sidebar.ListWidgets()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取侧栏工具失败"})
return
}
if widgets == nil {
widgets = []model.SidebarWidget{}
}
c.JSON(http.StatusOK, gin.H{"config": cfg, "widgets": widgets})
}
// PUT /api/admin/sidebar/config 保存布局配置
func (h *Handlers) AdminSaveSidebarConfig(c *gin.Context) {
var req struct {
Config *service.SidebarConfig `json:"config"`
}
if err := c.ShouldBindJSON(&req); err != nil || req.Config == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
saved, err := h.Sidebar.SaveConfig(*req.Config)
if err != nil {
if errors.Is(err, service.ErrSidebarInvalid) {
c.JSON(http.StatusBadRequest, gin.H{"error": "侧栏布局配置无效"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存侧栏配置失败"})
return
}
c.JSON(http.StatusOK, gin.H{"config": saved})
}
// POST /api/admin/sidebar/widgets 新建自定义工具
func (h *Handlers) AdminCreateSidebarWidget(c *gin.Context) {
var w model.SidebarWidget
if err := c.ShouldBindJSON(&w); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
if err := h.Sidebar.CreateWidget(&w); err != nil {
if errors.Is(err, service.ErrSidebarInvalid) {
c.JSON(http.StatusBadRequest, gin.H{"error": "工具名称或代码长度无效"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建侧栏工具失败"})
return
}
c.JSON(http.StatusOK, gin.H{"widget": w})
}
// PUT /api/admin/sidebar/widgets/:id 更新工具(内容 + 启用态)
func (h *Handlers) AdminUpdateSidebarWidget(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
var body model.SidebarWidget
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
if err := h.Sidebar.UpdateWidget(uint(id), &body); err != nil {
if errors.Is(err, service.ErrSidebarWidgetNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "侧栏工具不存在"})
return
}
if errors.Is(err, service.ErrSidebarInvalid) {
c.JSON(http.StatusBadRequest, gin.H{"error": "工具名称或代码长度无效"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "更新侧栏工具失败"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}
// DELETE /api/admin/sidebar/widgets/:id 删除工具并清理布局引用
func (h *Handlers) AdminDeleteSidebarWidget(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
if err := h.Sidebar.DeleteWidget(uint(id)); err != nil {
if errors.Is(err, service.ErrSidebarWidgetNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "侧栏工具不存在"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除侧栏工具失败"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}