package cmd import ( "context" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcfg" "github.com/gogf/gf/v2/os/genv" adminctl "service.xpcool.com/internal/controller/admin" "service.xpcool.com/internal/controller/hello" openctl "service.xpcool.com/internal/controller/open" userctl "service.xpcool.com/internal/controller/user" "service.xpcool.com/internal/library/jwt" "service.xpcool.com/internal/middleware" "service.xpcool.com/internal/service" ) // injectEnv 手动把关键环境变量写入配置系统。 // 注意:gf v2.10.2 起配置不再自动替换 ${ENV} 占位符,需在此显式注入, // 否则 config.dev.yaml 中的 ${DB_DSN}/${JWT_SECRET} 会原样传给数据库与 JWT。 func injectEnv(ctx context.Context) { adapter, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile) if !ok { return } if v := genv.Get("DB_DSN"); !v.IsEmpty() { _ = adapter.Set("database.default.link", v.String()) } if v := genv.Get("JWT_SECRET"); !v.IsEmpty() { _ = adapter.Set("jwt.secret", v.String()) } } var ( Main = gcmd.Command{ Name: "main", Usage: "main", Brief: "start http server", Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { injectEnv(ctx) s := g.Server() tokens := jwt.New(ctx) service.RegisterUserAuth(service.NewUserAuth(tokens, nil, nil)) service.RegisterAdminAuth(service.NewAdminAuth(tokens)) service.RegisterAdminMenu(service.NewAdminMenu()) service.RegisterAdminManage(service.NewAdminManage()) service.RegisterRoleManage(service.NewRoleManage()) service.RegisterMenuManage(service.NewMenuManage()) service.RegisterLogManage(service.NewLogManage()) service.RegisterAdminAudit(service.NewAdminAudit()) s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS) group.Middleware(ghttp.MiddlewareHandlerResponse) group.Bind( hello.NewV1(), ) }) s.Group("/api/open/v1", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(openctl.New()) // Open tools API for frontends, no auth required. }) s.Group("/api/v1", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(userctl.New()) // Login and refresh routes are public. group.Group("/", func(protected *ghttp.RouterGroup) { protected.Middleware(middleware.UserAuth(tokens)) }) }) s.Group("/admin/v1", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(adminctl.NewAuth()) // Public: login only. group.Group("/", func(profile *ghttp.RouterGroup) { // Login-only endpoints: own profile / access codes / menu routes. profile.Middleware(middleware.AdminAuthOnly(tokens)) profile.Bind(adminctl.NewProfile()) }) group.Group("/", func(protected *ghttp.RouterGroup) { // Permission-protected endpoints: RBAC management, logs, ... // 权限由后端按「方法+路径」自动匹配,无需前端传 X-Permission。 protected.Middleware(middleware.AdminAuth(tokens, service.AdminAuth().PermissionForPath, service.AdminAuth().HasPermission, func(ctx context.Context, id uint64, permission, method, path, ip, param string, duration, status int) { service.AdminAudit().Record(ctx, service.AuditEvent{AdminID: id, Permission: permission, Method: method, Path: path, IP: ip, Param: param, DurationMS: duration, StatusCode: status}) })) protected.Bind(adminctl.New()) }) }) s.Run() return nil }, } )