Files
2026-08-14 16:11:10 +08:00

103 lines
3.8 KiB
Go
Raw Permalink 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 (
"context"
"os"
"strings"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
"36wisdom/biz/consts"
"36wisdom/biz/controller"
"36wisdom/biz/dao"
"36wisdom/common"
"36wisdom/common/auth"
"36wisdom/seed"
)
func main() {
var ctx context.Context = gctx.New()
for _, dir := range []string{"data", "workspace", "workspace/uploads/image", "workspace/uploads/audio"} {
if err := os.MkdirAll(dir, 0o755); err != nil {
g.Log().Fatal(ctx, err)
}
}
initTables(ctx)
seed.EnsureSeeded(ctx)
s := g.Server()
// 路由注册与中间件装配(静态,不属业务表分层)
s.Use(common.MiddlewareNoiseSilence)
s.Use(ghttp.MiddlewareHandlerResponse)
// 静态生成资产(PNG/音频)路径不可变:强缓存,客户端每文件只下载一次。
// 静态文件服务不走中间件链,须用 HookBeforeServe(对静态/动态请求均执行)
s.BindHookHandler("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
if strings.HasPrefix(r.URL.Path, "/static/") {
r.Response.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
})
s.BindHandler("GET:/ping", func(r *ghttp.Request) {
r.Response.Write("pong")
})
secret := g.Cfg().MustGet(ctx, "auth.secret").String()
// 前台:公开接口(注册/登录),鉴权接口按模块挂载
s.Group("/api", func(g *ghttp.RouterGroup) {
g.Bind(controller.Parent)
g.Group("/", func(g *ghttp.RouterGroup) {
g.Middleware(auth.Middleware(secret, ""))
g.Bind(controller.Child, controller.Strategy, controller.Level)
})
})
// 后台:登录公开,其余 admin 角色鉴权
s.Group("/api/admin", func(g *ghttp.RouterGroup) {
g.Bind(controller.AdminUser)
g.Group("/", func(g *ghttp.RouterGroup) {
g.Middleware(auth.Middleware(secret, consts.RoleAdmin))
g.Bind(controller.AdminUpload, controller.AdminElement, controller.AdminStrategy, controller.AdminLevel, controller.AdminSceneNode, controller.AdminNodeOption, controller.AdminPrize, controller.AdminBadge, controller.AdminLifeTask, controller.AdminParent, controller.AdminChild, controller.AdminRedemption, controller.AdminStats)
})
})
// 生产形态:前端产物 ui-src/dist 由后端 :8080 统一托管(前后端同端口)
// 注:根路径须用 SetServerRoot——AddStaticPath("/", ...) 因前缀防误匹配守卫只服务根路径,
// /assets 等子路径全部 404;素材目录 ui-src/static 以 /static 前缀单独挂载
if _, err := os.Stat("ui-src/dist"); err == nil {
s.SetServerRoot("ui-src/dist")
}
if _, err := os.Stat("ui-src/static"); err == nil {
s.AddStaticPath("/static", "ui-src/static")
}
// 后台上传素材:/uploads 前缀(可变内容,不强缓存)
s.AddStaticPath("/uploads", "workspace/uploads")
// 管理后台产物 admin-src/disthash 路由,由 /admin 前缀托管)
if _, err := os.Stat("admin-src/dist"); err == nil {
s.AddStaticPath("/admin", "admin-src/dist")
}
s.Run()
}
// initTables 建表与死表清理(各 dao 的 Init 幂等;life_task_log 无读写引用整套移除,
// 库内残留表启动时 DROP 与代码保持一致)。
func initTables(ctx context.Context) {
inits := []func(ctx context.Context) error{
dao.Parent.Init, dao.Child.Init, dao.Strategy.Init, dao.Level.Init, dao.SceneNode.Init,
dao.NodeOption.Init, dao.Element.Init, dao.UserProgress.Init, dao.ChapterReview.Init,
dao.UserRouteLog.Init, dao.UserCollection.Init, dao.Badge.Init, dao.UserBadge.Init,
dao.Prize.Init, dao.Redemption.Init, dao.AdminUser.Init, dao.LifeTask.Init,
}
for _, init := range inits {
if err := init(ctx); err != nil {
g.Log().Fatal(ctx, err)
}
}
if _, err := g.DB().Exec(ctx, "DROP TABLE IF EXISTS life_task_log"); err != nil {
g.Log().Fatal(ctx, err)
}
}