Files
lunar-mini/server/cmd/main.go
T
gouki b8c439deac fix(server): 安全加固与版本链路修复
- 微信登录改为真实 jscode2session,session_key 不再下发客户端
- 支付回调增加 HMAC 验签(X-Pay-Sign)与幂等处理,未配置密钥时拒绝回调
- 订单金额一律以服务端商品表定价,禁止客户端传入金额
- 付费许愿改为支付成功后创建,不再先许愿后付款
- 管理后台增加登录认证(ADMIN_PASSWORD + role=admin JWT + HttpOnly Cookie)
- 订单详情/取消增加本人归属校验,修复越权访问
- 版本信息改为 ldflags 注入单一链路,GoVersion 用 runtime.Version()
- 恢复 gin 默认访问日志(原 Logger 中间件输出为空)
- 加载 HTML 模板修复后台页面 500;godotenv 加载 .env.local
- CORS 支持 CORS_ORIGINS 白名单配置
2026-08-09 00:09:24 +00:00

131 lines
3.6 KiB
Go
Raw 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 main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/gouki/lunar-server/internal/config"
"github.com/gouki/lunar-server/internal/handler"
"github.com/gouki/lunar-server/internal/middleware"
"github.com/gouki/lunar-server/internal/service"
"github.com/joho/godotenv"
)
// 版本信息由 CI 通过 ldflags 注入:
// -X main.Version=... -X main.BuildTime=... -X main.CommitSha=...
var (
Version = "dev"
BuildTime = "unknown"
CommitSha = "unknown"
)
func main() {
// 加载本地环境变量文件(生产环境由容器注入,忽略缺失;../.env.local 兼容从 server/ 目录启动)
_ = godotenv.Load(".env.local", "../.env.local", ".env")
// 将构建时注入的版本信息传递给 handler
handler.Version = Version
handler.BuildTime = BuildTime
handler.CommitSha = CommitSha
log.Printf("lunar-server %s (commit %s, built at %s)", Version, CommitSha, BuildTime)
// 加载配置
cfg := config.Load()
// 初始化数据库
if err := config.InitDB(cfg); err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
// 启动机器人许愿定时任务
wishService := service.NewWishService()
wishService.StartRobotWishJob()
// 设置运行模式
if cfg.Server.Env == "production" {
gin.SetMode(gin.ReleaseMode)
}
// 创建路由(gin.Default 自带访问日志与 Recovery
r := gin.Default()
// 中间件
r.Use(middleware.CORS())
// 静态文件与管理后台模板
r.Static("/static", "./web/static")
r.StaticFile("/", "./web/index.html")
r.LoadHTMLFiles("./web/index.html")
// API 路由
api := r.Group("/api")
{
// 版本信息(无需认证)
api.GET("/version", handler.GetVersion)
// 用户相关
user := api.Group("/user")
{
user.POST("/login", handler.UserLogin)
user.POST("/logout", handler.UserLogout)
user.GET("/profile", middleware.Auth(), handler.GetUserProfile)
user.PUT("/profile", middleware.Auth(), handler.UpdateUserProfile)
user.POST("/auth", handler.WechatAuth)
}
// 支付相关
pay := api.Group("/pay")
{
pay.POST("/create", middleware.Auth(), handler.CreateOrder)
pay.POST("/notify", handler.PayNotify)
pay.GET("/status/:orderId", handler.GetPayStatus)
}
// 订单相关
order := api.Group("/order")
{
order.GET("/list", middleware.Auth(), handler.GetOrderList)
order.GET("/:id", middleware.Auth(), handler.GetOrderDetail)
order.POST("/cancel/:id", middleware.Auth(), handler.CancelOrder)
}
// 许愿相关
wish := api.Group("/wish")
{
wish.GET("/trees", handler.GetWishTrees)
wish.GET("/tree", handler.GetWishTree)
wish.GET("/tree/:id/wishes", handler.GetTreeWishes)
wish.POST("/create", middleware.Auth(), handler.CreateWish)
wish.GET("/list", handler.GetWishList)
wish.GET("/:id", handler.GetWishDetail)
wish.DELETE("/:id", middleware.Auth(), handler.DeleteWish)
wish.GET("/products", handler.GetWishProducts)
}
}
// 管理后台路由(Inertia.js
admin := r.Group("/admin")
{
// 登录/登出接口无需认证;未配置 ADMIN_PASSWORD 时登录返回 503
admin.POST("/login", handler.AdminLogin)
admin.POST("/logout", handler.AdminLogout)
authed := admin.Group("")
authed.Use(middleware.AdminAuth())
{
authed.GET("/", handler.AdminDashboard)
authed.GET("/users", handler.AdminUsers)
authed.GET("/orders", handler.AdminOrders)
authed.GET("/wishes", handler.AdminWishes)
authed.GET("/settings", handler.AdminSettings)
}
}
// 启动服务器
addr := ":" + cfg.Server.Port
log.Printf("Server starting on %s", addr)
if err := r.Run(addr); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}