This commit is contained in:
Tomas Dvorak
2025-11-02 21:31:00 +01:00
parent b9cea0cd77
commit 087f30e82c
130 changed files with 20104 additions and 34330 deletions
+56 -120
View File
@@ -32,15 +32,12 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
// Initialize controllers
baseController := &controllers.BaseController{DB: db}
authController := controllers.NewAuthController(db)
facrController := controllers.NewFACRController(db)
contactController := controllers.NewContactController(db, emailService)
passwordController := controllers.NewPasswordController(db, emailService)
aiController := controllers.NewAIController(db)
scoreboardController := controllers.NewScoreboardController(db)
youtubeController := controllers.NewYouTubeController(db)
aboutController := controllers.NewAboutController(db)
galleryController := controllers.NewGalleryController(db)
umamiController := controllers.NewUmamiController()
filesController := &controllers.FilesController{DB: db}
notificationsController := controllers.NewNotificationsController(db, emailService)
emailController := controllers.NewEmailController(db)
@@ -48,15 +45,15 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
seoController := controllers.NewSEOController(db)
navigationController := controllers.NewNavigationController(db)
pollController := controllers.NewPollController(db)
sweepstakesController := controllers.NewSweepstakesController(db, emailService)
clothingController := controllers.NewClothingController(db)
pageElementConfigController := controllers.NewPageElementConfigController(db)
imageProcessingController := &controllers.ImageProcessingController{}
articleController := controllers.NewArticleController(db)
myuibrixController := &controllers.MyUIbrixController{DB: db}
editorPreviewController := controllers.NewEditorPreviewController(db)
shortLinkController := controllers.NewShortLinkController(db)
commentController := controllers.NewCommentController(db)
engagementController := controllers.NewEngagementController(db)
engagementController := controllers.NewEngagementController(db, emailService)
// API v1 group
{
@@ -79,6 +76,8 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
// Public page element configurations
api.GET("/page-elements", pageElementConfigController.GetPageElementConfigs)
api.GET("/clothing", clothingController.GetClothing)
// Public shortlink creation for visitors (same-site only)
api.POST("/shortlinks/public", middleware.RateLimit(30, time.Minute), shortLinkController.PublicCreateShortLink)
@@ -139,11 +138,22 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
// CSRF protect state-changing requests when relying on cookies (Bearer tokens are auto-exempt)
protected.Use(middleware.CSRFProtection())
{
// Sweepstakes (protected)
protected.POST("/sweepstakes/:id/enter", middleware.RateLimit(30, time.Minute), sweepstakesController.Enter)
protected.POST("/sweepstakes/:id/played", sweepstakesController.MarkVisualPlayed)
protected.GET("/sweepstakes/my-winnings", sweepstakesController.MyWinnings)
// Engagement leaderboard (auth)
protected.GET("/engagement/leaderboard", engagementController.GetLeaderboard)
// Engagement profile & actions
protected.GET("/engagement/profile", engagementController.GetProfile)
protected.PATCH("/engagement/profile", engagementController.PatchProfile)
protected.PATCH("/engagement/avatar", engagementController.PatchAvatar)
protected.POST("/engagement/redeem", engagementController.Redeem)
protected.POST("/engagement/redeem", middleware.RateLimit(5, time.Hour), engagementController.Redeem)
protected.GET("/engagement/achievements", engagementController.GetAchievements)
protected.GET("/engagement/transactions", engagementController.GetMyTransactions)
// Engagement new actions
protected.POST("/engagement/checkin", middleware.RateLimit(10, time.Minute), engagementController.Checkin)
protected.POST("/engagement/article-read", middleware.RateLimit(60, time.Minute), engagementController.ArticleRead)
// Comments (create/update/delete)
protected.POST("/comments", middleware.RateLimit(20, time.Minute), commentController.CreateComment)
protected.PUT("/comments/:id", commentController.UpdateComment)
@@ -259,12 +269,14 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
admin := protected.Group("/admin")
admin.Use(middleware.RoleAuth("admin"))
{
// Comments moderation
// Comments
commentsAdmin := admin.Group("/comments")
{
commentsAdmin.GET("", commentController.AdminList)
commentsAdmin.PATCH("/:id/status", commentController.AdminUpdateStatus)
commentsAdmin.POST("/ban", commentController.AdminBanUser)
commentsAdmin.GET("/bans", commentController.AdminListBans)
commentsAdmin.POST("/bans/:id/lift", commentController.AdminLiftBan)
commentsAdmin.GET("/unban-requests", commentController.AdminListUnban)
commentsAdmin.POST("/unban-requests/:id/resolve", commentController.AdminResolveUnban)
}
@@ -446,10 +458,10 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
{
clothing.GET("", clothingController.GetClothingAdmin)
clothing.GET("/:id", clothingController.GetClothingByID)
clothing.POST("", clothingController.CreateClothing)
clothing.PUT("/:id", clothingController.UpdateClothing)
clothing.DELETE("/:id", clothingController.DeleteClothing)
clothing.POST("/reorder", clothingController.UpdateClothingOrder)
clothing.POST("", clothingController.CreateClothing)
clothing.PUT("/:id", clothingController.UpdateClothing)
clothing.DELETE("/:id", clothingController.DeleteClothing)
clothing.POST("/reorder", clothingController.UpdateClothingOrder)
}
// Polls management (admin)
@@ -473,6 +485,10 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
engagement.DELETE("/rewards/:id", engagementController.AdminDeleteReward)
engagement.GET("/redemptions", engagementController.AdminListRedemptions)
engagement.PATCH("/redemptions/:id", engagementController.AdminUpdateRedemptionStatus)
engagement.GET("/leaderboard", engagementController.AdminGetLeaderboard)
engagement.GET("/transactions", engagementController.AdminListTransactions)
engagement.POST("/adjust", engagementController.AdminAdjustPoints)
engagement.GET("/profile/:user_id", engagementController.AdminGetUserProfile)
}
// Page element configurations management (admin)
@@ -501,120 +517,40 @@ func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
shortlinks.GET("", shortLinkController.ListShortLinks)
shortlinks.GET("/:id/stats", shortLinkController.GetShortLinkStats)
}
// Sweepstakes management (admin)
sw := admin.Group("/sweepstakes")
{
sw.GET("", sweepstakesController.AdminList)
sw.POST("", sweepstakesController.AdminCreate)
sw.PUT("/:id", sweepstakesController.AdminUpdate)
sw.DELETE("/:id", sweepstakesController.AdminDelete)
sw.GET("/:id/entries", sweepstakesController.AdminEntries)
sw.GET("/:id/winners", sweepstakesController.AdminWinners)
sw.PATCH("/:id/winners/:winner_id", sweepstakesController.AdminUpdateWinner)
sw.PATCH("/:id/winners/:winner_id/prize", sweepstakesController.AdminSetWinnerPrize)
sw.GET("/:id/visual", sweepstakesController.AdminVisualData)
// Prizes
sw.GET("/:id/prizes", sweepstakesController.AdminListPrizes)
sw.POST("/:id/prizes", sweepstakesController.AdminCreatePrize)
sw.PUT("/:id/prizes/:prize_id", sweepstakesController.AdminUpdatePrize)
sw.DELETE("/:id/prizes/:prize_id", sweepstakesController.AdminDeletePrize)
sw.POST("/:id/prizes/reorder", sweepstakesController.AdminReorderPrizes)
// Finalize (draw winners)
sw.POST("/:id/finalize", sweepstakesController.AdminFinalize)
}
}
// Protected routes end
}
// Register analytics routes (public tracking + admin endpoints)
RegisterAnalyticsRoutes(api, db)
// Umami analytics routes
api.GET("/umami/config", umamiController.GetUmamiConfig)
// Public setup endpoint (no auth required - called during initial setup)
api.POST("/umami/initialize-setup", umamiController.InitializeUmamiSetup)
umami := api.Group("/admin/umami")
umami.Use(middleware.JWTAuth(db))
umami.Use(middleware.RoleAuth("admin"))
{
umami.POST("/initialize", umamiController.InitializeUmami)
umami.GET("/stats", umamiController.GetStats)
umami.GET("/metrics/:type", umamiController.GetMetrics)
umami.GET("/pageviews", umamiController.GetPageviews)
}
// Register contact info routes (public + admin endpoints)
RegisterContactInfoRoutes(api, db)
// Public API routes
// Allow uploads publicly so initial setup can upload a club logo before an admin exists.
api.POST("/upload", middleware.RateLimit(30, time.Minute), baseController.UploadImage)
// Image processing endpoints (protected for editors)
// Note: Define a dedicated group with required middleware to avoid referencing
// the out-of-scope `protected` variable from above.
imageProcessing := api.Group("/image-processing")
imageProcessing.Use(middleware.JWTAuth(db))
imageProcessing.Use(middleware.CSRFProtection())
imageProcessing.Use(middleware.RoleAuth("editor"))
{
imageProcessing.POST("/process", imageProcessingController.ProcessImage)
imageProcessing.POST("/crop-upload", imageProcessingController.CropAndUpload)
imageProcessing.POST("/quick-edit", imageProcessingController.QuickEdit)
}
// Public scoreboard
api.GET("/scoreboard", scoreboardController.GetPublic)
api.GET("/scoreboard/colors/derive", scoreboardController.DeriveColors)
// ... (rest of the code remains the same)
api.GET("/settings", baseController.GetPublicSettings)
api.GET("/competition-aliases", baseController.GetPublicCompetitionAliases)
api.GET("/public/team-logo-overrides", baseController.GetPublicTeamLogoOverrides)
// Articles (public)
api.GET("/articles/featured", baseController.GetFeaturedArticles)
api.GET("/articles", baseController.GetArticles)
api.GET("/articles/:id", baseController.GetArticle)
api.GET("/articles/slug/:slug", baseController.GetArticleBySlug)
api.POST("/articles/:id/read", baseController.IncrementArticleRead)
api.POST("/articles/:id/track-view", baseController.TrackArticleView)
// Public read-only access to article-match link
api.GET("/articles/:id/match-link", baseController.GetArticleMatchLink)
// Public categories
api.GET("/categories", baseController.GetCategories)
// Public YouTube cached videos
api.GET("/youtube/videos", youtubeController.GetYouTubeVideos)
// Public About page
api.GET("/about", aboutController.GetPublicAboutPage)
api.GET("/teams", baseController.GetTeams)
api.GET("/teams/:id", baseController.GetTeam)
api.GET("/players", baseController.GetPlayers)
api.GET("/players/:id", baseController.GetPlayer)
api.GET("/sponsors", baseController.GetSponsors)
// Public banners
api.GET("/banners", baseController.GetBanners)
api.GET("/matches", baseController.GetMatches)
api.GET("/matches/history", baseController.GetMatchesHistory)
api.GET("/standings", baseController.GetStandings)
// Gallery (public): albums and photos
api.GET("/gallery/albums", galleryController.GetGalleryAlbums) // Get all albums
api.GET("/gallery/albums/:id", galleryController.GetGalleryAlbum) // Get single album with photos
api.GET("/gallery/proxy-image", galleryController.ProxyImage) // Proxy Zonerama images to avoid CORS
// Legacy Zonerama endpoints (keep for backwards compatibility)
api.GET("/zonerama/album", baseController.GetZoneramaAlbum)
// Alias to support hyphenated path used by some frontend calls
api.GET("/zonerama-album", baseController.GetZoneramaAlbum)
api.GET("/zonerama/picks", baseController.GetZoneramaPicks)
// Clothing (public) - active items only
api.GET("/clothing", clothingController.GetClothing)
// Polls (public)
api.GET("/polls", pollController.GetPolls)
api.GET("/polls/:id", pollController.GetPoll)
api.POST("/polls/:id/vote", middleware.RateLimit(10, time.Minute), pollController.Vote)
api.GET("/polls/:id/results", pollController.GetPollResults)
// Contact form and newsletter endpoints (public) rate limited to prevent abuse
api.POST("/contact", middleware.RateLimit(10, time.Minute), contactController.SubmitContactForm)
api.POST("/newsletter/subscribe", middleware.RateLimit(30, time.Minute), contactController.SubscribeToNewsletter)
api.POST("/newsletter/setup", middleware.RateLimit(30, time.Minute), contactController.SetupNewsletterPreferences)
api.POST("/newsletter/unsubscribe/:email", middleware.RateLimit(30, time.Minute), contactController.UnsubscribeFromNewsletter)
// Token-based management (no auth)
api.GET("/newsletter/preferences", contactController.GetNewsletterPreferencesByToken)
api.POST("/newsletter/preferences", contactController.SaveNewsletterPreferencesByToken)
api.POST("/newsletter/unsubscribe-token", contactController.UnsubscribeByToken)
// Public sweepstakes endpoints
api.GET("/sweepstakes/current", sweepstakesController.GetCurrent)
api.GET("/sweepstakes/:id/visual", sweepstakesController.PublicVisualData)
}
// FACR scraper endpoints (integrated): /api/v1/facr/*
facr := api.Group("/facr")
{
facr.GET("/club/search", facrController.SearchClubs)
facr.GET("/club/:type/:id", facrController.GetClubInfo)
facr.GET("/club/:type/:id/table", facrController.GetClubTables)
}
}
// SetupRootRoutes registers endpoints at the root (no /api prefix)