You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
6.0 KiB
Go
165 lines
6.0 KiB
Go
package router
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/denisovdennis/autohero/internal/game"
|
|
"github.com/denisovdennis/autohero/internal/handler"
|
|
"github.com/denisovdennis/autohero/internal/storage"
|
|
"github.com/denisovdennis/autohero/internal/world"
|
|
)
|
|
|
|
// Deps holds all dependencies needed by the router.
|
|
type Deps struct {
|
|
Engine *game.Engine
|
|
Hub *handler.Hub
|
|
PgPool *pgxpool.Pool
|
|
BotToken string
|
|
AdminBasicAuthUsername string
|
|
AdminBasicAuthPassword string
|
|
AdminBasicAuthRealm string
|
|
Logger *slog.Logger
|
|
ServerStartedAt time.Time
|
|
}
|
|
|
|
// New creates the chi router with all routes wired.
|
|
func New(deps Deps) *chi.Mux {
|
|
r := chi.NewRouter()
|
|
|
|
// Middleware stack.
|
|
r.Use(middleware.RequestID)
|
|
r.Use(middleware.RealIP)
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(middleware.Heartbeat("/ping"))
|
|
r.Use(corsMiddleware)
|
|
|
|
// Health.
|
|
healthH := handler.NewHealthHandler()
|
|
r.Get("/health", healthH.Health)
|
|
|
|
// Stores (PostgreSQL-backed persistence).
|
|
heroStore := storage.NewHeroStore(deps.PgPool, deps.Logger)
|
|
|
|
// WebSocket (needs heroStore to resolve telegram ID → hero DB ID).
|
|
wsH := handler.NewWSHandler(deps.Hub, heroStore, deps.Logger)
|
|
r.Get("/ws", wsH.HandleWS)
|
|
logStore := storage.NewLogStore(deps.PgPool)
|
|
questStore := storage.NewQuestStore(deps.PgPool)
|
|
gearStore := storage.NewGearStore(deps.PgPool)
|
|
achievementStore := storage.NewAchievementStore(deps.PgPool)
|
|
taskStore := storage.NewDailyTaskStore(deps.PgPool)
|
|
worldSvc := world.NewService()
|
|
|
|
// Auth endpoint (no auth middleware required - this IS the auth).
|
|
authH := handler.NewAuthHandler(deps.BotToken, heroStore, deps.Logger)
|
|
r.Post("/api/v1/auth/telegram", authH.TelegramAuth)
|
|
|
|
// Admin routes protected with HTTP Basic authentication.
|
|
adminH := handler.NewAdminHandler(heroStore, deps.Engine, deps.Hub, deps.PgPool, deps.Logger)
|
|
r.Route("/admin", func(r chi.Router) {
|
|
r.Use(handler.BasicAuthMiddleware(handler.BasicAuthConfig{
|
|
Username: deps.AdminBasicAuthUsername,
|
|
Password: deps.AdminBasicAuthPassword,
|
|
Realm: deps.AdminBasicAuthRealm,
|
|
}))
|
|
r.Get("/heroes", adminH.ListHeroes)
|
|
r.Get("/heroes/{heroId}", adminH.GetHero)
|
|
r.Post("/heroes/{heroId}/set-level", adminH.SetHeroLevel)
|
|
r.Post("/heroes/{heroId}/set-gold", adminH.SetHeroGold)
|
|
r.Post("/heroes/{heroId}/set-hp", adminH.SetHeroHP)
|
|
r.Post("/heroes/{heroId}/add-potions", adminH.AddPotions)
|
|
r.Post("/heroes/{heroId}/revive", adminH.ReviveHero)
|
|
r.Post("/heroes/{heroId}/reset", adminH.ResetHero)
|
|
r.Post("/heroes/{heroId}/reset-buff-charges", adminH.ResetBuffCharges)
|
|
r.Delete("/heroes/{heroId}", adminH.DeleteHero)
|
|
r.Get("/engine/status", adminH.EngineStatus)
|
|
r.Get("/engine/combats", adminH.ActiveCombats)
|
|
r.Get("/ws/connections", adminH.WSConnections)
|
|
r.Get("/info", adminH.ServerInfo)
|
|
})
|
|
|
|
// API v1 (authenticated routes).
|
|
gameH := handler.NewGameHandler(deps.Engine, heroStore, logStore, worldSvc, deps.Logger, deps.ServerStartedAt, questStore, gearStore, achievementStore, taskStore)
|
|
mapsH := handler.NewMapsHandler(worldSvc, deps.Logger)
|
|
questH := handler.NewQuestHandler(questStore, heroStore, logStore, deps.Logger)
|
|
npcH := handler.NewNPCHandler(questStore, heroStore, gearStore, logStore, deps.Logger)
|
|
achieveH := handler.NewAchievementHandler(achievementStore, heroStore, deps.Logger)
|
|
taskH := handler.NewDailyTaskHandler(taskStore, heroStore, deps.Logger)
|
|
r.Route("/api/v1", func(r chi.Router) {
|
|
// Apply Telegram auth middleware to all routes in this group.
|
|
// Disabled for now to allow development without a bot token.
|
|
// r.Use(handler.TelegramAuthMiddleware(deps.BotToken))
|
|
|
|
r.Get("/hero", gameH.GetHero)
|
|
r.Get("/hero/init", gameH.InitHero)
|
|
r.Post("/hero/name", gameH.SetHeroName)
|
|
r.Post("/hero/buff/{buffType}", gameH.ActivateBuff)
|
|
r.Post("/hero/encounter", gameH.RequestEncounter)
|
|
r.Post("/hero/victory", gameH.ReportVictory)
|
|
r.Post("/hero/revive", gameH.ReviveHero)
|
|
r.Post("/hero/purchase-buff-refill", gameH.PurchaseBuffRefill)
|
|
r.Get("/hero/loot", gameH.GetLoot)
|
|
r.Get("/hero/log", gameH.GetAdventureLog)
|
|
r.Post("/hero/use-potion", gameH.UsePotion)
|
|
r.Get("/weapons", gameH.GetWeapons)
|
|
r.Get("/armor", gameH.GetArmor)
|
|
r.Get("/maps/{mapId}", mapsH.GetMap)
|
|
|
|
// Quest system routes.
|
|
r.Get("/towns", questH.ListTowns)
|
|
r.Get("/towns/{townId}/npcs", questH.ListNPCsByTown)
|
|
r.Get("/npcs/{npcId}/quests", questH.ListQuestsByNPC)
|
|
r.Post("/hero/quests/{questId}/accept", questH.AcceptQuest)
|
|
r.Get("/hero/quests", questH.ListHeroQuests)
|
|
r.Post("/hero/quests/{questId}/claim", questH.ClaimQuestReward)
|
|
r.Delete("/hero/quests/{questId}", questH.AbandonQuest)
|
|
|
|
// NPC interaction routes.
|
|
r.Post("/hero/npc-interact", npcH.InteractNPC)
|
|
r.Get("/hero/nearby-npcs", npcH.NearbyNPCs)
|
|
r.Post("/hero/npc-alms", npcH.NPCAlms)
|
|
r.Post("/hero/npc-heal", npcH.HealHero)
|
|
r.Post("/hero/npc-buy-potion", npcH.BuyPotion)
|
|
|
|
// Gear routes.
|
|
r.Get("/hero/gear", gameH.GetHeroGear)
|
|
r.Get("/hero/equipment", gameH.GetHeroGear) // backward compat
|
|
r.Get("/gear/catalog", gameH.GetGearCatalog)
|
|
|
|
// Achievement routes.
|
|
r.Get("/hero/achievements", achieveH.GetHeroAchievements)
|
|
|
|
// Daily/weekly task routes.
|
|
r.Get("/hero/tasks", taskH.ListHeroTasks)
|
|
r.Post("/hero/tasks/{taskId}/claim", taskH.ClaimTask)
|
|
|
|
// Shared world routes.
|
|
r.Get("/hero/nearby", gameH.NearbyHeroes)
|
|
})
|
|
|
|
return r
|
|
}
|
|
|
|
// corsMiddleware adds CORS headers to all responses.
|
|
func corsMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Telegram-Init-Data")
|
|
w.Header().Set("Access-Control-Max-Age", "86400")
|
|
|
|
if r.Method == http.MethodOptions {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|