2025-11-28 23:30:49 +08:00
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-02 19:43:39 +08:00
|
|
|
|
"carrotskin/internal/container"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
"carrotskin/internal/middleware"
|
|
|
|
|
|
"carrotskin/internal/model"
|
2025-12-03 15:27:12 +08:00
|
|
|
|
"carrotskin/pkg/auth"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// Handlers 集中管理所有Handler
|
|
|
|
|
|
type Handlers struct {
|
2025-12-03 10:58:39 +08:00
|
|
|
|
Auth *AuthHandler
|
|
|
|
|
|
User *UserHandler
|
|
|
|
|
|
Texture *TextureHandler
|
|
|
|
|
|
Profile *ProfileHandler
|
|
|
|
|
|
Captcha *CaptchaHandler
|
|
|
|
|
|
Yggdrasil *YggdrasilHandler
|
|
|
|
|
|
CustomSkin *CustomSkinHandler
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewHandlers 创建所有Handler实例
|
|
|
|
|
|
func NewHandlers(c *container.Container) *Handlers {
|
|
|
|
|
|
return &Handlers{
|
2025-12-03 10:58:39 +08:00
|
|
|
|
Auth: NewAuthHandler(c),
|
|
|
|
|
|
User: NewUserHandler(c),
|
|
|
|
|
|
Texture: NewTextureHandler(c),
|
|
|
|
|
|
Profile: NewProfileHandler(c),
|
|
|
|
|
|
Captcha: NewCaptchaHandler(c),
|
|
|
|
|
|
Yggdrasil: NewYggdrasilHandler(c),
|
|
|
|
|
|
CustomSkin: NewCustomSkinHandler(c),
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RegisterRoutesWithDI 使用依赖注入注册所有路由
|
|
|
|
|
|
func RegisterRoutesWithDI(router *gin.Engine, c *container.Container) {
|
2025-12-03 10:58:39 +08:00
|
|
|
|
// 健康检查路由
|
|
|
|
|
|
router.GET("/health", HealthCheck)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 创建Handler实例
|
|
|
|
|
|
h := NewHandlers(c)
|
|
|
|
|
|
|
2025-11-28 23:30:49 +08:00
|
|
|
|
// API路由组
|
|
|
|
|
|
v1 := router.Group("/api/v1")
|
|
|
|
|
|
{
|
|
|
|
|
|
// 认证路由(无需JWT)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
registerAuthRoutes(v1, h.Auth)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户路由(需要JWT认证)
|
2025-12-03 15:27:12 +08:00
|
|
|
|
registerUserRoutes(v1, h.User, c.JWT)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 材质路由
|
2025-12-03 15:27:12 +08:00
|
|
|
|
registerTextureRoutes(v1, h.Texture, c.JWT)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 档案路由
|
2025-12-03 15:27:12 +08:00
|
|
|
|
registerProfileRoutesWithDI(v1, h.Profile, c.JWT)
|
2025-11-30 18:56:56 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 验证码路由
|
|
|
|
|
|
registerCaptchaRoutesWithDI(v1, h.Captcha)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// Yggdrasil API路由组
|
|
|
|
|
|
registerYggdrasilRoutesWithDI(v1, h.Yggdrasil)
|
|
|
|
|
|
|
|
|
|
|
|
// 系统路由
|
|
|
|
|
|
registerSystemRoutes(v1)
|
2025-12-03 10:58:39 +08:00
|
|
|
|
|
|
|
|
|
|
// CustomSkinAPI 路由
|
|
|
|
|
|
registerCustomSkinRoutes(v1, h.CustomSkin)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// registerAuthRoutes 注册认证路由
|
|
|
|
|
|
func registerAuthRoutes(v1 *gin.RouterGroup, h *AuthHandler) {
|
|
|
|
|
|
authGroup := v1.Group("/auth")
|
|
|
|
|
|
{
|
|
|
|
|
|
authGroup.POST("/register", h.Register)
|
|
|
|
|
|
authGroup.POST("/login", h.Login)
|
|
|
|
|
|
authGroup.POST("/send-code", h.SendVerificationCode)
|
|
|
|
|
|
authGroup.POST("/reset-password", h.ResetPassword)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// registerUserRoutes 注册用户路由
|
2025-12-03 15:27:12 +08:00
|
|
|
|
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
userGroup := v1.Group("/user")
|
2025-12-03 15:27:12 +08:00
|
|
|
|
userGroup.Use(middleware.AuthMiddleware(jwtService))
|
2025-12-02 19:43:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
userGroup.GET("/profile", h.GetProfile)
|
|
|
|
|
|
userGroup.PUT("/profile", h.UpdateProfile)
|
|
|
|
|
|
|
|
|
|
|
|
// 头像相关
|
|
|
|
|
|
userGroup.POST("/avatar/upload-url", h.GenerateAvatarUploadURL)
|
|
|
|
|
|
userGroup.PUT("/avatar", h.UpdateAvatar)
|
|
|
|
|
|
|
|
|
|
|
|
// 更换邮箱
|
|
|
|
|
|
userGroup.POST("/change-email", h.ChangeEmail)
|
|
|
|
|
|
|
|
|
|
|
|
// Yggdrasil密码相关
|
|
|
|
|
|
userGroup.POST("/yggdrasil-password/reset", h.ResetYggdrasilPassword)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// registerTextureRoutes 注册材质路由
|
2025-12-03 15:27:12 +08:00
|
|
|
|
func registerTextureRoutes(v1 *gin.RouterGroup, h *TextureHandler, jwtService *auth.JWTService) {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
textureGroup := v1.Group("/texture")
|
|
|
|
|
|
{
|
|
|
|
|
|
// 公开路由(无需认证)
|
|
|
|
|
|
textureGroup.GET("", h.Search)
|
|
|
|
|
|
textureGroup.GET("/:id", h.Get)
|
|
|
|
|
|
|
|
|
|
|
|
// 需要认证的路由
|
|
|
|
|
|
textureAuth := textureGroup.Group("")
|
2025-12-03 15:27:12 +08:00
|
|
|
|
textureAuth.Use(middleware.AuthMiddleware(jwtService))
|
2025-11-28 23:30:49 +08:00
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
textureAuth.POST("/upload-url", h.GenerateUploadURL)
|
|
|
|
|
|
textureAuth.POST("", h.Create)
|
|
|
|
|
|
textureAuth.PUT("/:id", h.Update)
|
|
|
|
|
|
textureAuth.DELETE("/:id", h.Delete)
|
|
|
|
|
|
textureAuth.POST("/:id/favorite", h.ToggleFavorite)
|
|
|
|
|
|
textureAuth.GET("/my", h.GetUserTextures)
|
|
|
|
|
|
textureAuth.GET("/favorites", h.GetUserFavorites)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// registerProfileRoutesWithDI 注册档案路由(依赖注入版本)
|
2025-12-03 15:27:12 +08:00
|
|
|
|
func registerProfileRoutesWithDI(v1 *gin.RouterGroup, h *ProfileHandler, jwtService *auth.JWTService) {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
profileGroup := v1.Group("/profile")
|
|
|
|
|
|
{
|
|
|
|
|
|
// 公开路由(无需认证)
|
|
|
|
|
|
profileGroup.GET("/:uuid", h.Get)
|
|
|
|
|
|
|
|
|
|
|
|
// 需要认证的路由
|
|
|
|
|
|
profileAuth := profileGroup.Group("")
|
2025-12-03 15:27:12 +08:00
|
|
|
|
profileAuth.Use(middleware.AuthMiddleware(jwtService))
|
2025-11-28 23:30:49 +08:00
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
profileAuth.POST("/", h.Create)
|
|
|
|
|
|
profileAuth.GET("/", h.List)
|
|
|
|
|
|
profileAuth.PUT("/:uuid", h.Update)
|
|
|
|
|
|
profileAuth.DELETE("/:uuid", h.Delete)
|
|
|
|
|
|
profileAuth.POST("/:uuid/activate", h.SetActive)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// registerCaptchaRoutesWithDI 注册验证码路由(依赖注入版本)
|
|
|
|
|
|
func registerCaptchaRoutesWithDI(v1 *gin.RouterGroup, h *CaptchaHandler) {
|
|
|
|
|
|
captchaGroup := v1.Group("/captcha")
|
|
|
|
|
|
{
|
|
|
|
|
|
captchaGroup.GET("/generate", h.Generate)
|
|
|
|
|
|
captchaGroup.POST("/verify", h.Verify)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// registerYggdrasilRoutesWithDI 注册Yggdrasil API路由(依赖注入版本)
|
|
|
|
|
|
func registerYggdrasilRoutesWithDI(v1 *gin.RouterGroup, h *YggdrasilHandler) {
|
|
|
|
|
|
ygg := v1.Group("/yggdrasil")
|
|
|
|
|
|
{
|
|
|
|
|
|
ygg.GET("", h.GetMetaData)
|
|
|
|
|
|
ygg.POST("/minecraftservices/player/certificates", h.GetPlayerCertificates)
|
|
|
|
|
|
authserver := ygg.Group("/authserver")
|
2025-11-28 23:30:49 +08:00
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
authserver.POST("/authenticate", h.Authenticate)
|
|
|
|
|
|
authserver.POST("/validate", h.ValidToken)
|
|
|
|
|
|
authserver.POST("/refresh", h.RefreshToken)
|
|
|
|
|
|
authserver.POST("/invalidate", h.InvalidToken)
|
|
|
|
|
|
authserver.POST("/signout", h.SignOut)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
2025-12-02 19:43:39 +08:00
|
|
|
|
sessionServer := ygg.Group("/sessionserver")
|
2025-11-28 23:30:49 +08:00
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
sessionServer.GET("/session/minecraft/profile/:uuid", h.GetProfileByUUID)
|
|
|
|
|
|
sessionServer.POST("/session/minecraft/join", h.JoinServer)
|
|
|
|
|
|
sessionServer.GET("/session/minecraft/hasJoined", h.HasJoinedServer)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
2025-12-02 19:43:39 +08:00
|
|
|
|
api := ygg.Group("/api")
|
|
|
|
|
|
profiles := api.Group("/profiles")
|
2025-11-28 23:30:49 +08:00
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
profiles.POST("/minecraft", h.GetProfilesByName)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// registerSystemRoutes 注册系统路由
|
|
|
|
|
|
func registerSystemRoutes(v1 *gin.RouterGroup) {
|
|
|
|
|
|
system := v1.Group("/system")
|
|
|
|
|
|
{
|
|
|
|
|
|
system.GET("/config", func(c *gin.Context) {
|
|
|
|
|
|
// TODO: 实现从数据库读取系统配置
|
|
|
|
|
|
c.JSON(200, model.NewSuccessResponse(gin.H{
|
|
|
|
|
|
"site_name": "CarrotSkin",
|
|
|
|
|
|
"site_description": "A Minecraft Skin Station",
|
|
|
|
|
|
"registration_enabled": true,
|
|
|
|
|
|
"max_textures_per_user": 100,
|
|
|
|
|
|
"max_profiles_per_user": 5,
|
|
|
|
|
|
}))
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
2025-12-03 10:58:39 +08:00
|
|
|
|
|
|
|
|
|
|
// registerCustomSkinRoutes 注册CustomSkinAPI路由
|
|
|
|
|
|
// CustomSkinAPI 协议要求根地址必须以 / 结尾
|
|
|
|
|
|
// 路由格式:
|
|
|
|
|
|
// - {ROOT}/{USERNAME}.json - 获取玩家信息
|
|
|
|
|
|
// - {ROOT}/textures/{hash} - 获取资源文件
|
|
|
|
|
|
//
|
|
|
|
|
|
// 根路径为 /api/v1/csl/
|
|
|
|
|
|
func registerCustomSkinRoutes(v1 *gin.RouterGroup, h *CustomSkinHandler) {
|
|
|
|
|
|
// CustomSkinAPI 路由组
|
|
|
|
|
|
csl := v1.Group("/csl")
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取玩家信息: {ROOT}/{USERNAME}.json
|
|
|
|
|
|
csl.GET("/:username", h.GetPlayerInfo)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取资源文件: {ROOT}/textures/{hash}
|
|
|
|
|
|
csl.GET("/textures/:hash", h.GetTexture)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|