5abc705eae
- web/: 产品官网(Hero 事件流终端、功能矩阵、架构、快速开始、下载、博客 + Markdown 详情页),青瓷绿双主题 - admin/: 内容管理(JWT 登录、文章分页/搜索/CRUD、草稿与发布),embed 挂 /admin - server/: Gin + GORM,MySQL(DSN 走 .env,SQLite 兜底);规范落地:sundynix_ 表前缀、字符串雪花主键、snake_case 列名、统一响应信封、公共分页参数 - 双 SPA embed 单二进制部署,make build 一键出包 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
package router
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"git.sundynix.cn/Blizzard/sundynix-site/server/internal/handler"
|
|
"git.sundynix.cn/Blizzard/sundynix-site/server/internal/middleware"
|
|
"git.sundynix.cn/Blizzard/sundynix-site/server/internal/pkg/resp"
|
|
"git.sundynix.cn/Blizzard/sundynix-site/server/internal/webfs"
|
|
)
|
|
|
|
func New(db *gorm.DB) (*gin.Engine, error) {
|
|
r := gin.New()
|
|
r.Use(gin.Logger(), gin.Recovery(), middleware.CORS())
|
|
|
|
// ── 公开 API ──
|
|
api := r.Group("/api")
|
|
{
|
|
api.GET("/healthz", func(c *gin.Context) {
|
|
resp.OK(c, gin.H{"status": "ok"})
|
|
})
|
|
|
|
posts := handler.NewPostHandler(db)
|
|
api.GET("/posts", posts.List)
|
|
api.GET("/posts/:slug", posts.Get)
|
|
}
|
|
|
|
// ── 管理端 API ──
|
|
adminAuth := handler.NewAdminAuthHandler()
|
|
api.POST("/admin/login", adminAuth.Login)
|
|
|
|
adminAPI := api.Group("/admin", middleware.Auth())
|
|
{
|
|
posts := handler.NewAdminPostHandler(db)
|
|
adminAPI.GET("/posts", posts.List)
|
|
adminAPI.POST("/posts", posts.Create)
|
|
adminAPI.GET("/posts/:id", posts.Get)
|
|
adminAPI.PUT("/posts/:id", posts.Update)
|
|
adminAPI.DELETE("/posts/:id", posts.Delete)
|
|
}
|
|
|
|
// ── 静态站点:/admin → 管理端,其余 → 用户端 ──
|
|
webDist, err := webfs.WebDist()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
adminDist, err := webfs.AdminDist()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
webServer := http.FileServer(http.FS(webDist))
|
|
adminServer := http.StripPrefix("/admin", http.FileServer(http.FS(adminDist)))
|
|
|
|
r.NoRoute(func(c *gin.Context) {
|
|
path := c.Request.URL.Path
|
|
if strings.HasPrefix(path, "/api/") {
|
|
resp.NotFound(c, "接口不存在")
|
|
return
|
|
}
|
|
if path == "/admin" || strings.HasPrefix(path, "/admin/") {
|
|
serveSPA(c, adminDist, adminServer, strings.TrimPrefix(path, "/admin"), "/admin/")
|
|
return
|
|
}
|
|
serveSPA(c, webDist, webServer, path, "/")
|
|
})
|
|
|
|
return r, nil
|
|
}
|
|
|
|
// serveSPA 真实文件直接吐,否则回退 index.html 交给前端路由。
|
|
func serveSPA(c *gin.Context, dist fs.FS, server http.Handler, rel, fallback string) {
|
|
p := strings.TrimPrefix(rel, "/")
|
|
if p != "" {
|
|
if _, err := fs.Stat(dist, p); err == nil {
|
|
server.ServeHTTP(c.Writer, c.Request)
|
|
return
|
|
}
|
|
}
|
|
c.Request.URL.Path = fallback
|
|
server.ServeHTTP(c.Writer, c.Request)
|
|
}
|