Build and Publish Server / build (push) Successful in 4m30s
- 新增商业化模块:Stripe/Binance Pay 支付、邮箱登录、订单管理 - 新增数据模型:ReportProduct, ReportEntitlement, ReportSnapshot, PaymentEvent, EmailLoginCode - 新增 API:/api/commerce/checkout, /api/commerce/webhooks/*, /api/commerce/orders/* - 新增管理后台:订单列表、统计图表、统一 AdminHeader 组件 - 新增中间件测试:RateLimit, Auth, CORS 共 7 个测试用例 - 新增 AGENTS.md 项目代理指令文件 - 新增 REMINDER.md 商业化功能开发规范 - 修复 JWT 解析:添加 WithValidMethods 限制 HS256 - 修复 GetPayStatus:增加用户权限校验 - 更新 .gitignore 和 README.md
119 lines
2.8 KiB
Go
119 lines
2.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gouki/lunar-server/internal/config"
|
|
"github.com/gouki/lunar-server/internal/service"
|
|
)
|
|
|
|
// adminPage 统一的管理后台页面渲染(补充 Inertia 需要的 url 属性)
|
|
func adminPage(c *gin.Context, title, page string) {
|
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|
"title": title,
|
|
"page": page,
|
|
"url": c.Request.URL.Path,
|
|
})
|
|
}
|
|
|
|
// AdminLogin 管理员登录:密码正确签发 role=admin 的 JWT
|
|
func AdminLogin(c *gin.Context) {
|
|
cfg := config.Load()
|
|
if cfg.Admin.Password == "" {
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{
|
|
"code": 503,
|
|
"msg": "后台登录未启用",
|
|
})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Password string `json:"password" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"msg": "参数错误",
|
|
})
|
|
return
|
|
}
|
|
|
|
// 恒定时间比较,防时序攻击
|
|
if subtle.ConstantTimeCompare([]byte(req.Password), []byte(cfg.Admin.Password)) != 1 {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"code": 401,
|
|
"msg": "密码错误",
|
|
})
|
|
return
|
|
}
|
|
|
|
userService := service.NewUserService()
|
|
token, err := userService.GenerateAdminToken(cfg.JWT.Secret)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"msg": "生成token失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
// 同时写入 HttpOnly Cookie,支持浏览器直接导航后台页面
|
|
c.SetSameSite(http.SameSiteLaxMode)
|
|
c.SetCookie(service.AdminTokenCookie, token, 12*3600, "/admin", "", cfg.Server.Env == "production", true)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": gin.H{
|
|
"token": token,
|
|
},
|
|
})
|
|
}
|
|
|
|
// AdminLogout 管理员登出:清除会话 Cookie
|
|
func AdminLogout(c *gin.Context) {
|
|
cfg := config.Load()
|
|
c.SetCookie(service.AdminTokenCookie, "", -1, "/admin", "", cfg.Server.Env == "production", true)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 0,
|
|
"msg": "success",
|
|
})
|
|
}
|
|
|
|
// AdminDashboard 管理后台首页
|
|
func AdminDashboard(c *gin.Context) {
|
|
adminPage(c, "管理后台", "dashboard")
|
|
}
|
|
|
|
// AdminUsers 用户管理
|
|
func AdminUsers(c *gin.Context) {
|
|
adminPage(c, "用户管理", "users")
|
|
}
|
|
|
|
// AdminOrders PC 商业版订单管理
|
|
func AdminOrders(c *gin.Context) {
|
|
adminPage(c, "PC 报告订单", "orders")
|
|
}
|
|
|
|
// AdminCharts 排盘记录
|
|
func AdminCharts(c *gin.Context) {
|
|
adminPage(c, "排盘记录", "charts")
|
|
}
|
|
|
|
// AdminWishes 许愿管理
|
|
func AdminWishes(c *gin.Context) {
|
|
adminPage(c, "许愿管理", "wishes")
|
|
}
|
|
|
|
// AdminWiki 百科管理(百科条目 + 历史上的今天同步)
|
|
func AdminWiki(c *gin.Context) {
|
|
adminPage(c, "百科管理", "wiki")
|
|
}
|
|
|
|
// AdminSettings 系统设置
|
|
func AdminSettings(c *gin.Context) {
|
|
adminPage(c, "系统设置", "settings")
|
|
}
|