feat: 站点访问统计与可配置侧边栏组件
- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
This commit is contained in:
123
backend/handler/sidebar.go
Normal file
123
backend/handler/sidebar.go
Normal file
@@ -0,0 +1,123 @@
|
||||
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})
|
||||
}
|
||||
Reference in New Issue
Block a user