package server import ( "net/http" "git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/golib/hwsauth" "github.com/pkg/errors" "github.com/uptrace/bun" "git.haelnorr.com/h/oslstats/internal/config" "git.haelnorr.com/h/oslstats/internal/db" "git.haelnorr.com/h/oslstats/internal/discord" "git.haelnorr.com/h/oslstats/internal/handlers" "git.haelnorr.com/h/oslstats/internal/permissions" "git.haelnorr.com/h/oslstats/internal/rbac" "git.haelnorr.com/h/oslstats/internal/store" "git.haelnorr.com/h/oslstats/pkg/slapshotapi" ) func addRoutes( s *hws.Server, staticFS *http.FileSystem, cfg *config.Config, conn *db.DB, auth *hwsauth.Authenticator[*db.User, bun.Tx], store *store.Store, discordAPI *discord.APIClient, slapAPI *slapshotapi.SlapAPI, perms *rbac.Checker, ) error { // Create the routes baseRoutes := []hws.Route{ { Path: "/static/", Method: hws.MethodGET, Handler: http.StripPrefix("/static/", handlers.StaticFS(staticFS, s)), }, { Path: "/", Method: hws.MethodGET, Handler: handlers.Index(s, conn), }, } authRoutes := []hws.Route{ { Path: "/login", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: auth.LogoutReq(handlers.Login(s, conn, cfg, store, discordAPI)), }, { Path: "/auth/callback", Method: hws.MethodGET, Handler: auth.LogoutReq(handlers.Callback(s, auth, conn, cfg, store, discordAPI)), }, { Path: "/register", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: auth.LogoutReq(handlers.Register(s, auth, conn, slapAPI, cfg, store)), }, { Path: "/logout", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: auth.LoginReq(handlers.Logout(s, auth, conn, discordAPI)), }, { Path: "/profile", Method: hws.MethodGET, Handler: auth.LoginReq(handlers.ProfileRedirect(s)), }, } seasonRoutes := []hws.Route{ { Path: "/seasons", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonsPage(s, conn), }, { Path: "/seasons/new", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.SeasonsCreate)(handlers.NewSeason(s)), }, { Path: "/seasons/new", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.SeasonsCreate)(handlers.NewSeasonSubmit(s, conn)), }, { Path: "/seasons/{season_short_name}", Method: hws.MethodGET, Handler: handlers.SeasonPage(s, conn), }, { Path: "/seasons/{season_short_name}/edit", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.SeasonsUpdate)(handlers.SeasonEditPage(s, conn)), }, { Path: "/seasons/{season_short_name}/edit", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.SeasonsUpdate)(handlers.SeasonEditSubmit(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}", Method: hws.MethodGET, Handler: handlers.SeasonLeaguePage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/table", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonLeagueTablePage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/fixtures", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonLeagueFixturesPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/teams", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonLeagueTeamsPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/teams/{team_id}", Method: hws.MethodGET, Handler: handlers.SeasonLeagueTeamDetailPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/stats", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonLeagueStatsPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/finals", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeasonLeagueFinalsPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/finals/setup", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeasonLeagueFinalsSetupForm(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/finals/setup", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeasonLeagueFinalsSetupSubmit(s, conn)), }, { Path: "/seasons/{season_short_name}/add-league/{league_short_name}", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.SeasonsAddLeague)(handlers.SeasonAddLeague(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}", Method: hws.MethodDELETE, Handler: perms.RequirePermission(s, permissions.SeasonsRemoveLeague)(handlers.SeasonRemoveLeague(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/teams/add", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.TeamsAddToLeague)(handlers.SeasonLeagueAddTeam(s, conn)), }, // Free agent routes { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/free-agents", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.FreeAgentsListPage(s, conn), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/free-agents/register", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FreeAgentsAdd)(handlers.RegisterFreeAgent(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/free-agents/unregister", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FreeAgentsRemove)(handlers.UnregisterFreeAgent(s, conn)), }, } leagueRoutes := []hws.Route{ { Path: "/leagues", Method: hws.MethodGET, Handler: handlers.LeaguesList(s, conn), }, { Path: "/leagues/new", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.LeaguesCreate)(handlers.NewLeague(s)), }, { Path: "/leagues/new", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.LeaguesCreate)(handlers.NewLeagueSubmit(s, conn)), }, } fixturesRoutes := []hws.Route{ { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/fixtures/manage", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.SeasonLeagueManageFixturesPage(s, conn)), }, { Path: "/fixtures/generate", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesCreate)(handlers.GenerateFixtures(s, conn)), }, { Path: "/fixtures/update-game-weeks", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.UpdateFixtures(s, conn)), }, { Path: "/seasons/{season_short_name}/leagues/{league_short_name}/fixtures", Method: hws.MethodDELETE, Handler: perms.RequirePermission(s, permissions.FixturesDelete)(handlers.SeasonLeagueDeleteFixtures(s, conn)), }, { Path: "/fixtures/{fixture_id}", Method: hws.MethodGET, Handler: handlers.FixtureDetailPage(s, conn), }, { Path: "/fixtures/{fixture_id}", Method: hws.MethodDELETE, Handler: perms.RequirePermission(s, permissions.FixturesDelete)(handlers.DeleteFixture(s, conn)), }, // Fixture detail tab routes { Path: "/fixtures/{fixture_id}/overview", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.FixtureDetailOverviewPage(s, conn), }, { Path: "/fixtures/{fixture_id}/preview", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.FixtureDetailPreviewPage(s, conn), }, { Path: "/fixtures/{fixture_id}/analysis", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.FixtureDetailAnalysisPage(s, conn), }, { Path: "/fixtures/{fixture_id}/scheduling", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.FixtureDetailSchedulePage(s, conn), }, // Fixture scheduling routes { Path: "/fixtures/{fixture_id}/schedule", Method: hws.MethodPOST, Handler: handlers.ProposeSchedule(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/{schedule_id}/accept", Method: hws.MethodPOST, Handler: handlers.AcceptSchedule(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/{schedule_id}/reject", Method: hws.MethodPOST, Handler: handlers.RejectSchedule(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/{schedule_id}/withdraw", Method: hws.MethodPOST, Handler: handlers.WithdrawSchedule(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/postpone", Method: hws.MethodPOST, Handler: handlers.PostponeSchedule(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/reschedule", Method: hws.MethodPOST, Handler: handlers.RescheduleFixture(s, conn), }, { Path: "/fixtures/{fixture_id}/schedule/cancel", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.CancelSchedule(s, conn)), }, // Fixture free agent nomination routes { Path: "/fixtures/{fixture_id}/free-agents/nominate", Method: hws.MethodPOST, Handler: handlers.NominateFreeAgentHandler(s, conn), }, { Path: "/fixtures/{fixture_id}/free-agents/{player_id}/remove", Method: hws.MethodPOST, Handler: handlers.RemoveFreeAgentNominationHandler(s, conn), }, // Match result management routes (all require fixtures.manage permission) { Path: "/fixtures/{fixture_id}/results/upload", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.UploadMatchLogsPage(s, conn)), }, { Path: "/fixtures/{fixture_id}/results/upload", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.UploadMatchLogs(s, conn)), }, { Path: "/fixtures/{fixture_id}/results/review", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.ReviewMatchResult(s, conn)), }, { Path: "/fixtures/{fixture_id}/results/finalize", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.FinalizeMatchResult(s, conn)), }, { Path: "/fixtures/{fixture_id}/results/discard", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.DiscardMatchResult(s, conn)), }, // Forfeit route { Path: "/fixtures/{fixture_id}/forfeit", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.FixturesManage)(handlers.ForfeitFixture(s, conn)), }, // Series detail page routes { Path: "/series/{series_id}", Method: hws.MethodGET, Handler: handlers.SeriesDetailPage(s, conn), }, { Path: "/series/{series_id}/overview", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeriesDetailOverviewPage(s, conn), }, { Path: "/series/{series_id}/preview", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeriesDetailPreviewPage(s, conn), }, { Path: "/series/{series_id}/analysis", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeriesDetailAnalysisPage(s, conn), }, { Path: "/series/{series_id}/scheduling", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.SeriesDetailSchedulePage(s, conn), }, // Series scheduling routes { Path: "/series/{series_id}/schedule", Method: hws.MethodPOST, Handler: handlers.ProposeSeriesSchedule(s, conn), }, { Path: "/series/{series_id}/schedule/{schedule_id}/accept", Method: hws.MethodPOST, Handler: handlers.AcceptSeriesSchedule(s, conn), }, { Path: "/series/{series_id}/schedule/{schedule_id}/reject", Method: hws.MethodPOST, Handler: handlers.RejectSeriesSchedule(s, conn), }, { Path: "/series/{series_id}/schedule/{schedule_id}/withdraw", Method: hws.MethodPOST, Handler: handlers.WithdrawSeriesScheduleHandler(s, conn), }, { Path: "/series/{series_id}/schedule/postpone", Method: hws.MethodPOST, Handler: handlers.PostponeSeriesSchedule(s, conn), }, { Path: "/series/{series_id}/schedule/reschedule", Method: hws.MethodPOST, Handler: handlers.RescheduleSeriesHandler(s, conn), }, { Path: "/series/{series_id}/schedule/cancel", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.CancelSeriesScheduleHandler(s, conn)), }, // Series result management routes { Path: "/series/{series_id}/results/upload", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeriesUploadResultPage(s, conn)), }, { Path: "/series/{series_id}/results/upload", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeriesUploadResults(s, conn)), }, { Path: "/series/{series_id}/results/review", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeriesReviewResults(s, conn)), }, { Path: "/series/{series_id}/results/finalize", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeriesFinalizeResults(s, conn)), }, { Path: "/series/{series_id}/results/discard", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.PlayoffsManage)(handlers.SeriesDiscardResults(s, conn)), }, } playerRoutes := []hws.Route{ { Path: "/players/{player_id}", Method: hws.MethodGET, Handler: handlers.PlayerViewStats(s, conn), }, { Path: "/players/{player_id}/stats", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.PlayerViewStats(s, conn), }, { Path: "/players/{player_id}/teams", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.PlayerViewTeams(s, conn), }, { Path: "/players/{player_id}/seasons", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.PlayerViewSeasons(s, conn), }, { Path: "/players/{player_id}/stats/filter", Method: hws.MethodPOST, Handler: handlers.PlayerStatsFilter(s, conn), }, { Path: "/players/{player_id}/link-slapid", Method: hws.MethodPOST, Handler: auth.LoginReq(handlers.LinkPlayerSlapID(s, conn, slapAPI)), }, } teamRoutes := []hws.Route{ { Path: "/teams", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: handlers.TeamsPage(s, conn), }, { Path: "/teams/new", Method: hws.MethodGET, Handler: perms.RequirePermission(s, permissions.TeamsCreate)(handlers.NewTeamPage(s)), }, { Path: "/teams/new", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.TeamsCreate)(handlers.NewTeamSubmit(s, conn)), }, { Path: "/teams/manage_roster", Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.TeamsManagePlayers)(handlers.ManageTeamRoster(s, conn)), }, { Path: "/teams/{team_id}", Method: hws.MethodGET, Handler: handlers.TeamDetailPage(s, conn), }, } htmxRoutes := []hws.Route{ { Path: "/htmx/isusernameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.User)(nil), "username"), }, { Path: "/htmx/isseasonnameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.Season)(nil), "name"), }, { Path: "/htmx/isseasonshortnameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.Season)(nil), "short_name"), }, { Path: "/htmx/isleaguenameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.League)(nil), "name"), }, { Path: "/htmx/isleagueshortnameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.League)(nil), "short_name"), }, { Path: "/htmx/isteamnameunique", Method: hws.MethodPOST, Handler: handlers.IsUnique(s, conn, (*db.Team)(nil), "name"), }, { Path: "/htmx/isteamshortnamesunique", Method: hws.MethodPOST, Handler: handlers.IsTeamShortNamesUnique(s, conn), }, } wsRoutes := []hws.Route{ { Path: "/ws/notifications", Method: hws.MethodGET, Handler: handlers.NotificationWS(s, cfg), }, } // Admin routes adminRoutes := []hws.Route{ { Path: "/notification-tester", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: perms.RequireAdmin(s)(handlers.NotifyTester(s)), }, // Full page routes (for direct navigation and refreshes) { Path: "/admin", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminDashboard(s, conn)), }, { Path: "/admin/users", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: perms.RequireAdmin(s)(handlers.AdminUsersPage(s, conn)), }, { Path: "/admin/roles", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: perms.RequireAdmin(s)(handlers.AdminRoles(s, conn)), }, { Path: "/admin/audit", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminAuditLogsPage(s, conn)), }, { Path: "/admin/audit", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminAuditLogsList(s, conn)), }, // Role management routes { Path: "/admin/roles/create", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminRoleCreateForm(s)), }, { Path: "/admin/roles/create", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminRoleCreate(s, conn)), }, { Path: "/admin/roles/{id}/manage", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminRoleManage(s, conn)), }, { Path: "/admin/roles/{id}", Method: hws.MethodDELETE, Handler: perms.RequireAdmin(s)(handlers.AdminRoleDelete(s, conn)), }, { Path: "/admin/roles/{id}/delete-confirm", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminRoleDeleteConfirm(s, conn)), }, { Path: "/admin/roles/{id}/permissions", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminRolePermissionsModal(s, conn)), }, { Path: "/admin/roles/{id}/permissions", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminRolePermissionsUpdate(s, conn)), }, { Path: "/admin/roles/{id}/preview-start", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminPreviewRoleStart(s, conn, cfg.HWSAuth.SSL)), }, { Path: "/admin/roles/preview-stop", Method: hws.MethodPOST, Handler: perms.RequireActualAdmin(s)(handlers.AdminPreviewRoleStop(s)), }, { Path: "/admin/audit/filter", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminAuditLogsFilter(s, conn)), }, { Path: "/admin/audit/{id}", Method: hws.MethodGET, Handler: perms.RequireAdmin(s)(handlers.AdminAuditLogDetail(s, conn)), }, } routes := append(baseRoutes, htmxRoutes...) routes = append(routes, wsRoutes...) routes = append(routes, authRoutes...) routes = append(routes, adminRoutes...) routes = append(routes, seasonRoutes...) routes = append(routes, leagueRoutes...) routes = append(routes, fixturesRoutes...) routes = append(routes, teamRoutes...) routes = append(routes, playerRoutes...) // Register the routes with the server err := s.AddRoutes(routes...) if err != nil { return errors.Wrap(err, "server.AddRoutes") } return nil }