feat: DB DI 전환 + download 하위 호환성 + race condition 수정
- middleware(Auth, Idempotency)를 클로저 팩토리 패턴으로 DI 전환 - database.DB/RDB 전역 변수 제거, ConnectMySQL/Redis 값 반환으로 변경 - download API X-API-Version 헤더 + 하위 호환성 규칙 문서화 - SaveGameData PlayTimeDelta 원자적 UPDATE (race condition 해소) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,9 @@ func Register(
|
||||
healthCheck fiber.Handler,
|
||||
readyCheck fiber.Handler,
|
||||
chainUserLimiter fiber.Handler,
|
||||
authMw fiber.Handler,
|
||||
serverAuthMw fiber.Handler,
|
||||
idempotencyReqMw fiber.Handler,
|
||||
) {
|
||||
// Swagger UI
|
||||
app.Get("/swagger/*", swagger.HandlerDefault)
|
||||
@@ -38,13 +41,13 @@ func Register(
|
||||
|
||||
// ── Internal API (Rate Limit 제외, API Key 인증만) ──────────────
|
||||
// 반드시 /api 그룹보다 먼저 등록해야 apiLimiter를 우회함
|
||||
internalApi := app.Group("/api/internal", apiBodyLimit, middleware.ServerAuth)
|
||||
internalApi := app.Group("/api/internal", apiBodyLimit, serverAuthMw)
|
||||
|
||||
// Internal - Boss Raid
|
||||
br := internalApi.Group("/bossraid")
|
||||
br.Post("/entry", brH.RequestEntry)
|
||||
br.Post("/start", brH.StartRaid)
|
||||
br.Post("/complete", middleware.IdempotencyRequired, brH.CompleteRaid)
|
||||
br.Post("/complete", idempotencyReqMw, brH.CompleteRaid)
|
||||
br.Post("/fail", brH.FailRaid)
|
||||
br.Get("/room", brH.GetRoom)
|
||||
br.Post("/validate-entry", brH.ValidateEntryToken)
|
||||
@@ -64,8 +67,8 @@ func Register(
|
||||
|
||||
// Internal - Chain
|
||||
internalChain := internalApi.Group("/chain")
|
||||
internalChain.Post("/reward", middleware.IdempotencyRequired, chainH.InternalGrantReward)
|
||||
internalChain.Post("/mint", middleware.IdempotencyRequired, chainH.InternalMintAsset)
|
||||
internalChain.Post("/reward", idempotencyReqMw, chainH.InternalGrantReward)
|
||||
internalChain.Post("/mint", idempotencyReqMw, chainH.InternalMintAsset)
|
||||
internalChain.Get("/balance", chainH.InternalGetBalance)
|
||||
internalChain.Get("/assets", chainH.InternalGetAssets)
|
||||
internalChain.Get("/inventory", chainH.InternalGetInventory)
|
||||
@@ -78,15 +81,15 @@ func Register(
|
||||
a.Post("/register", authLimiter, authH.Register)
|
||||
a.Post("/login", authLimiter, authH.Login)
|
||||
a.Post("/refresh", authLimiter, authH.Refresh)
|
||||
a.Post("/logout", middleware.Auth, authH.Logout)
|
||||
a.Post("/logout", authMw, authH.Logout)
|
||||
// /verify moved to internal API (ServerAuth) — see internal section below
|
||||
a.Get("/ssafy/login", authH.SSAFYLoginURL)
|
||||
a.Post("/ssafy/callback", authLimiter, authH.SSAFYCallback)
|
||||
a.Post("/launch-ticket", middleware.Auth, authH.CreateLaunchTicket)
|
||||
a.Post("/launch-ticket", authMw, authH.CreateLaunchTicket)
|
||||
a.Post("/redeem-ticket", authLimiter, authH.RedeemLaunchTicket)
|
||||
|
||||
// Users (admin only)
|
||||
u := api.Group("/users", middleware.Auth, middleware.AdminOnly)
|
||||
u := api.Group("/users", authMw, middleware.AdminOnly)
|
||||
u.Get("/", authH.GetAllUsers)
|
||||
u.Patch("/:id/role", authH.UpdateRole)
|
||||
u.Delete("/:id", authH.DeleteUser)
|
||||
@@ -94,20 +97,20 @@ func Register(
|
||||
// Announcements
|
||||
ann := api.Group("/announcements")
|
||||
ann.Get("/", annH.GetAll)
|
||||
ann.Post("/", middleware.Auth, middleware.AdminOnly, annH.Create)
|
||||
ann.Put("/:id", middleware.Auth, middleware.AdminOnly, annH.Update)
|
||||
ann.Delete("/:id", middleware.Auth, middleware.AdminOnly, annH.Delete)
|
||||
ann.Post("/", authMw, middleware.AdminOnly, annH.Create)
|
||||
ann.Put("/:id", authMw, middleware.AdminOnly, annH.Update)
|
||||
ann.Delete("/:id", authMw, middleware.AdminOnly, annH.Delete)
|
||||
|
||||
// Download
|
||||
dl := api.Group("/download")
|
||||
dl.Get("/info", dlH.GetInfo)
|
||||
dl.Get("/file", dlH.ServeFile)
|
||||
dl.Get("/launcher", dlH.ServeLauncher)
|
||||
dl.Post("/upload/game", middleware.Auth, middleware.AdminOnly, dlH.Upload)
|
||||
dl.Post("/upload/launcher", middleware.Auth, middleware.AdminOnly, dlH.UploadLauncher)
|
||||
dl.Post("/upload/game", authMw, middleware.AdminOnly, dlH.Upload)
|
||||
dl.Post("/upload/launcher", authMw, middleware.AdminOnly, dlH.UploadLauncher)
|
||||
|
||||
// Chain - Queries (authenticated)
|
||||
ch := api.Group("/chain", middleware.Auth)
|
||||
ch := api.Group("/chain", authMw)
|
||||
ch.Get("/wallet", chainH.GetWalletInfo)
|
||||
ch.Get("/balance", chainH.GetBalance)
|
||||
ch.Get("/assets", chainH.GetAssets)
|
||||
@@ -117,22 +120,22 @@ func Register(
|
||||
ch.Get("/market/:id", chainH.GetMarketListing)
|
||||
|
||||
// Chain - User Transactions (authenticated, per-user rate limited, idempotency-protected)
|
||||
ch.Post("/transfer", chainUserLimiter, middleware.IdempotencyRequired, chainH.Transfer)
|
||||
ch.Post("/asset/transfer", chainUserLimiter, middleware.IdempotencyRequired, chainH.TransferAsset)
|
||||
ch.Post("/market/list", chainUserLimiter, middleware.IdempotencyRequired, chainH.ListOnMarket)
|
||||
ch.Post("/market/buy", chainUserLimiter, middleware.IdempotencyRequired, chainH.BuyFromMarket)
|
||||
ch.Post("/market/cancel", chainUserLimiter, middleware.IdempotencyRequired, chainH.CancelListing)
|
||||
ch.Post("/inventory/equip", chainUserLimiter, middleware.IdempotencyRequired, chainH.EquipItem)
|
||||
ch.Post("/inventory/unequip", chainUserLimiter, middleware.IdempotencyRequired, chainH.UnequipItem)
|
||||
ch.Post("/transfer", chainUserLimiter, idempotencyReqMw, chainH.Transfer)
|
||||
ch.Post("/asset/transfer", chainUserLimiter, idempotencyReqMw, chainH.TransferAsset)
|
||||
ch.Post("/market/list", chainUserLimiter, idempotencyReqMw, chainH.ListOnMarket)
|
||||
ch.Post("/market/buy", chainUserLimiter, idempotencyReqMw, chainH.BuyFromMarket)
|
||||
ch.Post("/market/cancel", chainUserLimiter, idempotencyReqMw, chainH.CancelListing)
|
||||
ch.Post("/inventory/equip", chainUserLimiter, idempotencyReqMw, chainH.EquipItem)
|
||||
ch.Post("/inventory/unequip", chainUserLimiter, idempotencyReqMw, chainH.UnequipItem)
|
||||
|
||||
// Chain - Admin Transactions (admin only, idempotency-protected)
|
||||
chainAdmin := api.Group("/chain/admin", middleware.Auth, middleware.AdminOnly)
|
||||
chainAdmin.Post("/mint", middleware.IdempotencyRequired, chainH.MintAsset)
|
||||
chainAdmin.Post("/reward", middleware.IdempotencyRequired, chainH.GrantReward)
|
||||
chainAdmin.Post("/template", middleware.IdempotencyRequired, chainH.RegisterTemplate)
|
||||
chainAdmin := api.Group("/chain/admin", authMw, middleware.AdminOnly)
|
||||
chainAdmin.Post("/mint", idempotencyReqMw, chainH.MintAsset)
|
||||
chainAdmin.Post("/reward", idempotencyReqMw, chainH.GrantReward)
|
||||
chainAdmin.Post("/template", idempotencyReqMw, chainH.RegisterTemplate)
|
||||
|
||||
// Player Profile (authenticated)
|
||||
p := api.Group("/player", middleware.Auth)
|
||||
p := api.Group("/player", authMw)
|
||||
p.Get("/profile", playerH.GetProfile)
|
||||
p.Put("/profile", playerH.UpdateProfile)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user