mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fotbal-club/internal/controllers"
|
||||
"fotbal-club/internal/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RegisterAnalyticsRoutes(router *gin.RouterGroup, db *gorm.DB) {
|
||||
analyticsController := controllers.NewAnalyticsController(db)
|
||||
|
||||
// Public tracking endpoint (no auth, rate-limited)
|
||||
router.POST("/analytics/track", middleware.RateLimit(120, time.Minute), analyticsController.TrackEvent)
|
||||
|
||||
// Analytics visitors endpoint (protected, used by dashboard widgets)
|
||||
router.GET("/analytics/visitors", middleware.JWTAuth(db), middleware.RoleAuth("admin"), analyticsController.GetVisitors)
|
||||
|
||||
// Protected admin routes
|
||||
admin := router.Group("/admin/analytics")
|
||||
admin.Use(middleware.JWTAuth(db))
|
||||
admin.Use(middleware.RoleAuth("admin"))
|
||||
{
|
||||
admin.GET("", analyticsController.GetAnalytics) // GET /admin/analytics (summary)
|
||||
admin.GET("/overview", analyticsController.GetAnalyticsOverview)
|
||||
admin.GET("/top-pages", analyticsController.GetTopPages)
|
||||
admin.GET("/top-articles", analyticsController.GetTopArticles)
|
||||
admin.GET("/top-interactions", analyticsController.GetTopInteractions)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fotbal-club/internal/controllers"
|
||||
"fotbal-club/internal/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func RegisterContactInfoRoutes(router *gin.RouterGroup, db *gorm.DB) {
|
||||
contactInfoController := controllers.NewContactInfoController(db)
|
||||
|
||||
// Public endpoints (no auth required)
|
||||
router.GET("/contacts", contactInfoController.GetPublicContacts)
|
||||
router.GET("/contacts/categories", contactInfoController.GetPublicContactCategories)
|
||||
|
||||
// Admin endpoints (require auth + admin role)
|
||||
admin := router.Group("/admin/contacts")
|
||||
admin.Use(middleware.JWTAuth(db))
|
||||
admin.Use(middleware.RoleAuth("admin"))
|
||||
{
|
||||
// Contact Categories CRUD
|
||||
admin.GET("/categories", contactInfoController.GetContactCategories)
|
||||
admin.POST("/categories", contactInfoController.CreateContactCategory)
|
||||
admin.PUT("/categories/:id", contactInfoController.UpdateContactCategory)
|
||||
admin.DELETE("/categories/:id", contactInfoController.DeleteContactCategory)
|
||||
|
||||
// Contacts CRUD
|
||||
admin.GET("", contactInfoController.GetContacts)
|
||||
admin.POST("", contactInfoController.CreateContact)
|
||||
admin.PUT("/:id", contactInfoController.UpdateContact)
|
||||
admin.DELETE("/:id", contactInfoController.DeleteContact)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fotbal-club/internal/controllers"
|
||||
"fotbal-club/internal/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func RegisterEventRoutes(router *gin.RouterGroup, db *gorm.DB) {
|
||||
eventController := controllers.EventController{DB: db}
|
||||
|
||||
events := router.Group("/events")
|
||||
{
|
||||
events.GET("", eventController.GetEvents)
|
||||
events.GET("/upcoming", eventController.GetUpcomingEvents)
|
||||
// Public single event endpoint
|
||||
events.GET(":id", eventController.GetEventByID)
|
||||
|
||||
// Protected routes
|
||||
authorized := events.Group("")
|
||||
authorized.Use(middleware.JWTAuth(db))
|
||||
{
|
||||
authorized.POST("", eventController.CreateEvent)
|
||||
authorized.PUT(":id", eventController.UpdateEvent)
|
||||
authorized.DELETE(":id", eventController.DeleteEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fotbal-club/internal/config"
|
||||
"fotbal-club/internal/controllers"
|
||||
"fotbal-club/internal/middleware"
|
||||
"fotbal-club/internal/services"
|
||||
"fotbal-club/pkg/email"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Global newsletter automation instance
|
||||
var newsletterAutomation *services.NewsletterAutomation
|
||||
|
||||
// SetNewsletterAutomation stores the newsletter automation instance
|
||||
func SetNewsletterAutomation(na *services.NewsletterAutomation) {
|
||||
newsletterAutomation = na
|
||||
}
|
||||
|
||||
// GetNewsletterAutomation returns the newsletter automation instance
|
||||
func GetNewsletterAutomation() *services.NewsletterAutomation {
|
||||
return newsletterAutomation
|
||||
}
|
||||
|
||||
// SetupRoutes configures all the routes for the application
|
||||
func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
|
||||
emailService := email.NewEmailService(config.AppConfig, 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)
|
||||
prefetchController := controllers.NewPrefetchController()
|
||||
seoController := controllers.NewSEOController(db)
|
||||
navigationController := controllers.NewNavigationController(db)
|
||||
pollController := controllers.NewPollController(db)
|
||||
clothingController := controllers.NewClothingController(db)
|
||||
pageElementConfigController := controllers.NewPageElementConfigController(db)
|
||||
|
||||
// API v1 group
|
||||
{
|
||||
// Health check
|
||||
api.GET("/health", baseController.HealthCheck)
|
||||
|
||||
// Image proxy (public) to work around CORS when reading images in Canvas on the frontend
|
||||
api.GET("/proxy/image", baseController.ProxyImage)
|
||||
|
||||
// Public SEO: metadata
|
||||
api.GET("/seo", seoController.GetPublicSEO)
|
||||
|
||||
// Public navigation endpoints
|
||||
api.GET("/navigation", navigationController.GetNavigationItems)
|
||||
api.GET("/social-links", navigationController.GetSocialLinks)
|
||||
|
||||
// Public page element configurations
|
||||
api.GET("/page-elements", pageElementConfigController.GetPageElementConfigs)
|
||||
|
||||
// Email tracking (public)
|
||||
api.GET("/email/open.gif", emailController.OpenPixel)
|
||||
api.GET("/email/click", emailController.ClickRedirect)
|
||||
api.GET("/email/spam", emailController.MarkSpam)
|
||||
api.GET("/email/unsubscribe", emailController.Unsubscribe)
|
||||
|
||||
// Initial setup (public)
|
||||
api.GET("/setup/status", baseController.SetupStatus)
|
||||
api.POST("/setup/initialize", baseController.SetupInitialize)
|
||||
// SMTP validation (public during setup; does not send email, only connects)
|
||||
api.POST("/setup/validate-smtp", baseController.ValidateSMTP)
|
||||
|
||||
// Auth routes
|
||||
auth := api.Group("/auth")
|
||||
{
|
||||
// Limit login attempts to mitigate brute force
|
||||
auth.POST("/login", middleware.RateLimit(15, time.Minute), authController.Login)
|
||||
// Logout clears auth cookie (stateless)
|
||||
auth.POST("/logout", authController.Logout)
|
||||
// Limit registration bursts
|
||||
auth.POST("/register", middleware.RateLimit(5, time.Hour), authController.Register)
|
||||
auth.GET("/check-email", authController.CheckEmail)
|
||||
// Password reset (public)
|
||||
auth.POST("/initiate-password-reset", passwordController.InitiatePasswordReset)
|
||||
auth.POST("/verify-reset-code", passwordController.VerifyResetCode)
|
||||
auth.POST("/complete-password-reset", passwordController.CompletePasswordReset)
|
||||
// Legacy password reset endpoints (deprecated)
|
||||
auth.POST("/forgot-password", passwordController.ForgotPassword)
|
||||
auth.POST("/forgot-password-admin", passwordController.AdminSendReset)
|
||||
auth.POST("/reset-password", passwordController.ResetPassword)
|
||||
auth.GET("/me", middleware.JWTAuth(db), authController.GetCurrentUser)
|
||||
// Initial admin setup helpers
|
||||
auth.GET("/admin/exists", authController.AdminExists)
|
||||
auth.POST("/make-admin", middleware.JWTAuth(db), authController.MakeAdmin)
|
||||
}
|
||||
|
||||
// Event routes (public)
|
||||
eventController := &controllers.EventController{DB: db}
|
||||
events := api.Group("/events")
|
||||
{
|
||||
events.GET("", eventController.GetEvents)
|
||||
events.GET("/upcoming", eventController.GetUpcomingEvents)
|
||||
events.GET("/:id", eventController.GetEventByID)
|
||||
}
|
||||
|
||||
// Protected routes (require authentication)
|
||||
protected := api.Group("")
|
||||
protected.Use(middleware.JWTAuth(db))
|
||||
{
|
||||
// AI endpoints (protected)
|
||||
ai := protected.Group("/ai")
|
||||
{
|
||||
ai.POST("/blog/generate", aiController.GenerateBlog)
|
||||
ai.POST("/about/generate", aiController.GenerateAboutPage)
|
||||
}
|
||||
|
||||
// User profile
|
||||
protected.GET("/me", authController.GetCurrentUser)
|
||||
|
||||
// Uploads are registered as a public endpoint below so the handler can
|
||||
// allow unauthenticated uploads during initial setup (when no admin exists).
|
||||
// The protected group remains for other authenticated endpoints.
|
||||
|
||||
// Events (protected)
|
||||
protectedEvents := protected.Group("/events")
|
||||
{
|
||||
protectedEvents.POST("", eventController.CreateEvent)
|
||||
protectedEvents.PUT("/:id", eventController.UpdateEvent)
|
||||
protectedEvents.DELETE("/:id", eventController.DeleteEvent)
|
||||
}
|
||||
|
||||
// Articles (protected - accessible by editors and admins)
|
||||
articles := protected.Group("/articles")
|
||||
{
|
||||
articles.POST("", baseController.CreateArticle)
|
||||
articles.PUT("/:id", baseController.UpdateArticle)
|
||||
articles.DELETE("/:id", baseController.DeleteArticle)
|
||||
// Link article to FACR match
|
||||
articles.POST("/:id/match-link", baseController.PutArticleMatchLink)
|
||||
articles.DELETE("/:id/match-link", baseController.DeleteArticleMatchLink)
|
||||
}
|
||||
|
||||
// Teams (protected)
|
||||
teams := protected.Group("/teams")
|
||||
{
|
||||
teams.POST("", baseController.CreateTeam)
|
||||
teams.PUT("/:id", baseController.UpdateTeam)
|
||||
teams.DELETE("/:id", baseController.DeleteTeam)
|
||||
}
|
||||
|
||||
// Players (protected)
|
||||
players := protected.Group("/players")
|
||||
{
|
||||
players.POST("", baseController.CreatePlayer)
|
||||
players.PUT("/:id", baseController.UpdatePlayer)
|
||||
players.DELETE("/:id", baseController.DeletePlayer)
|
||||
}
|
||||
|
||||
// Sponsors (protected CRUD)
|
||||
sponsors := protected.Group("/sponsors")
|
||||
{
|
||||
sponsors.POST("", baseController.CreateSponsor)
|
||||
sponsors.PUT("/:id", baseController.UpdateSponsor)
|
||||
sponsors.DELETE("/:id", baseController.DeleteSponsor)
|
||||
}
|
||||
|
||||
// Admin routes (single consolidated group)
|
||||
admin := protected.Group("/admin")
|
||||
admin.Use(middleware.RoleAuth("admin"))
|
||||
{
|
||||
// Admin-only endpoints for managing sponsors, etc. (user CRUD removed; no handlers defined)
|
||||
|
||||
// Competition aliases (admin)
|
||||
admin.GET("/competition-aliases", baseController.GetCompetitionAliases)
|
||||
admin.PUT("/competition-aliases/:code", baseController.PutCompetitionAlias)
|
||||
admin.DELETE("/competition-aliases/:code", baseController.DeleteCompetitionAlias)
|
||||
admin.POST("/competition-aliases/reorder", baseController.ReorderCompetitionAliases)
|
||||
|
||||
// Categories (admin)
|
||||
admin.POST("/categories", baseController.CreateCategory)
|
||||
admin.PUT("/categories/:id", baseController.UpdateCategory)
|
||||
admin.DELETE("/categories/:id", baseController.DeleteCategory)
|
||||
|
||||
// Settings (singleton)
|
||||
admin.GET("/settings", baseController.GetSettings)
|
||||
admin.PUT("/settings", baseController.UpdateSettings)
|
||||
|
||||
// About page (singleton)
|
||||
admin.GET("/about", aboutController.GetAdminAboutPage)
|
||||
admin.PUT("/about", aboutController.UpsertAboutPage)
|
||||
admin.DELETE("/about", aboutController.DeleteAboutPage)
|
||||
|
||||
// Scoreboard (singleton)
|
||||
admin.GET("/scoreboard", scoreboardController.GetAdmin)
|
||||
admin.PUT("/scoreboard", scoreboardController.PutAdmin)
|
||||
// Scoreboard timer controls
|
||||
admin.POST("/scoreboard/timer/start", scoreboardController.StartTimer)
|
||||
admin.POST("/scoreboard/timer/pause", scoreboardController.PauseTimer)
|
||||
admin.POST("/scoreboard/timer/reset", scoreboardController.ResetTimer)
|
||||
// Scoreboard advanced actions
|
||||
admin.POST("/scoreboard/swap-sides", scoreboardController.SwapSides)
|
||||
admin.POST("/scoreboard/second-half", scoreboardController.StartSecondHalf)
|
||||
// Presets: save/list/load
|
||||
admin.POST("/scoreboard/save", scoreboardController.SaveState)
|
||||
admin.GET("/scoreboard/saves", scoreboardController.ListSaves)
|
||||
admin.POST("/scoreboard/load", scoreboardController.LoadSaved)
|
||||
// Scoreboard sponsors & QR (admin-only)
|
||||
admin.GET("/scoreboard/sponsors", scoreboardController.ListSponsors)
|
||||
admin.POST("/scoreboard/sponsors/upload", scoreboardController.UploadSponsors)
|
||||
admin.DELETE("/scoreboard/sponsors", scoreboardController.DeleteSponsor)
|
||||
admin.GET("/scoreboard/qr", scoreboardController.GetQR)
|
||||
admin.POST("/scoreboard/qr", scoreboardController.UploadQR)
|
||||
|
||||
// Users (admin)
|
||||
admin.GET("/users", authController.ListUsers)
|
||||
// Create/Update/Delete users
|
||||
admin.POST("/users", authController.AdminCreateUser)
|
||||
admin.PUT("/users/:id", authController.AdminUpdateUser)
|
||||
admin.DELETE("/users/:id", authController.AdminDeleteUser)
|
||||
// Admin: send password reset email using special SMTP override
|
||||
admin.POST("/users/send-reset", passwordController.AdminSendReset)
|
||||
// Admin: reset password for a specific user ID
|
||||
admin.POST("/users/:id/reset-password", passwordController.AdminSendResetByID)
|
||||
|
||||
// Admin matches merged with overrides
|
||||
admin.GET("/matches", baseController.GetAdminMatches)
|
||||
|
||||
// Match & Team Logo Overrides
|
||||
overrides := admin.Group("")
|
||||
{
|
||||
// Match overrides
|
||||
overrides.GET("/match-overrides", baseController.GetMatchOverrides)
|
||||
overrides.PUT("/match-overrides/:external_match_id", baseController.PutMatchOverride)
|
||||
overrides.PATCH("/match-overrides/:external_match_id", baseController.PatchMatchOverride)
|
||||
|
||||
// Team logo overrides
|
||||
overrides.GET("/team-logo-overrides", baseController.GetTeamLogoOverrides)
|
||||
overrides.PUT("/team-logo-overrides/:external_team_id", baseController.PutTeamLogoOverride)
|
||||
overrides.PATCH("/team-logo-overrides/:external_team_id", baseController.PatchTeamLogoOverride)
|
||||
}
|
||||
|
||||
// Contact messages management
|
||||
contactMessages := admin.Group("/contact-messages")
|
||||
{
|
||||
contactMessages.GET("", contactController.GetContactMessages)
|
||||
contactMessages.GET("/:id", contactController.GetContactMessage)
|
||||
contactMessages.PATCH("/:id/read", contactController.MarkMessageAsRead)
|
||||
contactMessages.POST("/:id/forward", contactController.ForwardContactMessage)
|
||||
contactMessages.POST("/forward-all", contactController.ForwardAllContactMessages)
|
||||
contactMessages.DELETE("/:id", contactController.DeleteContactMessage)
|
||||
contactMessages.DELETE("", contactController.DeleteContactMessages) // Bulk delete
|
||||
}
|
||||
|
||||
// Newsletter management
|
||||
admin.GET("/newsletter/subscribers", contactController.GetNewsletterSubscribers)
|
||||
admin.POST("/newsletter/send", contactController.SendNewsletter)
|
||||
admin.POST("/newsletter/preview", contactController.PreviewNewsletter)
|
||||
admin.POST("/newsletter/test", contactController.SendNewsletterTest)
|
||||
// New: send prebuilt digest by type and toggle automation
|
||||
admin.POST("/newsletter/send-digest", contactController.SendNewsletterDigest)
|
||||
admin.PATCH("/newsletter/enable", contactController.UpdateNewsletterAutomation)
|
||||
// Removed deprecated SMTP test route (use /newsletter/test instead)
|
||||
admin.GET("/newsletter/status", contactController.GetNewsletterStatus)
|
||||
admin.GET("/newsletter/stats/recent", emailController.GetRecentEmailStats)
|
||||
admin.GET("/newsletter/stats/:id/events", emailController.GetEmailEventsForLog)
|
||||
admin.PATCH("/newsletter/subscribers/:id/preferences", contactController.UpdateNewsletterSubscriberPreferences)
|
||||
admin.DELETE("/newsletter/subscribers/:id", contactController.DeleteNewsletterSubscriber)
|
||||
admin.PATCH("/newsletter/subscribers/:id/status", contactController.UpdateNewsletterSubscriberStatus)
|
||||
|
||||
// Notifications (admin)
|
||||
notifications := admin.Group("/notifications")
|
||||
{
|
||||
notifications.POST("/competition", notificationsController.SendCompetitionNotification)
|
||||
notifications.POST("/match", notificationsController.SendMatchNotification)
|
||||
}
|
||||
|
||||
// Prefetch management (admin)
|
||||
prefetch := admin.Group("/prefetch")
|
||||
{
|
||||
prefetch.GET("/status", prefetchController.Status)
|
||||
prefetch.POST("/trigger", prefetchController.Trigger)
|
||||
}
|
||||
|
||||
// Cache RAW viewer (admin)
|
||||
cache := admin.Group("/cache")
|
||||
{
|
||||
cache.GET("/list", baseController.GetAdminCacheList)
|
||||
cache.GET("/file", baseController.GetAdminCacheFile)
|
||||
}
|
||||
|
||||
// Gallery management (admin)
|
||||
gallery := admin.Group("/gallery")
|
||||
{
|
||||
gallery.GET("/profile", galleryController.GetGalleryProfile) // Get Zonerama profile
|
||||
gallery.POST("/albums/fetch", galleryController.FetchAlbum) // Fetch single album
|
||||
gallery.DELETE("/albums/:id", galleryController.DeleteAlbum) // Delete album
|
||||
gallery.POST("/refresh", galleryController.RefreshFromZonerama) // Refresh from Zonerama
|
||||
}
|
||||
|
||||
// Alias endpoint for saving a single Zonerama album (keeps older frontend code working)
|
||||
admin.POST("/zonerama/save-album", galleryController.FetchAlbum)
|
||||
// Save or update a chosen Zonerama pick (photo) in unified cache
|
||||
admin.POST("/zonerama/pick", baseController.PutZoneramaPick)
|
||||
|
||||
// SEO admin
|
||||
admin.GET("/seo", seoController.GetSEOSettings)
|
||||
admin.PUT("/seo", seoController.UpdateSEOSettings)
|
||||
|
||||
// Files management (admin)
|
||||
files := admin.Group("/files")
|
||||
{
|
||||
files.GET("", filesController.GetAllFiles)
|
||||
files.GET("/unused", filesController.GetUnusedFiles)
|
||||
files.GET("/duplicates", filesController.GetDuplicateFiles)
|
||||
files.GET("/:id/usages", filesController.GetFileUsages)
|
||||
files.DELETE("/:id", filesController.DeleteFile)
|
||||
files.POST("/scan", filesController.ScanAndSyncFiles)
|
||||
}
|
||||
|
||||
// Navigation management (admin)
|
||||
navigation := admin.Group("/navigation")
|
||||
{
|
||||
navigation.GET("", navigationController.GetAllNavigationItems)
|
||||
navigation.POST("", navigationController.CreateNavigationItem)
|
||||
navigation.PUT("/:id", navigationController.UpdateNavigationItem)
|
||||
navigation.DELETE("/:id", navigationController.DeleteNavigationItem)
|
||||
navigation.POST("/reorder", navigationController.ReorderNavigationItems)
|
||||
navigation.POST("/seed", navigationController.SeedDefaultNavigation)
|
||||
}
|
||||
|
||||
// Social links management (admin)
|
||||
socialLinks := admin.Group("/social-links")
|
||||
{
|
||||
socialLinks.GET("", navigationController.GetAllSocialLinks)
|
||||
socialLinks.POST("", navigationController.CreateSocialLink)
|
||||
socialLinks.PUT("/:id", navigationController.UpdateSocialLink)
|
||||
socialLinks.DELETE("/:id", navigationController.DeleteSocialLink)
|
||||
socialLinks.POST("/reorder", navigationController.ReorderSocialLinks)
|
||||
}
|
||||
|
||||
// Clothing management (admin)
|
||||
clothing := admin.Group("/clothing")
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
// Polls management (admin)
|
||||
polls := admin.Group("/polls")
|
||||
{
|
||||
polls.GET("", pollController.GetPolls)
|
||||
polls.GET("/:id", pollController.GetPoll)
|
||||
polls.POST("", pollController.CreatePoll)
|
||||
polls.PUT("/:id", pollController.UpdatePoll)
|
||||
polls.DELETE("/:id", pollController.DeletePoll)
|
||||
polls.GET("/:id/stats", pollController.GetPollStats)
|
||||
}
|
||||
|
||||
// Page element configurations management (admin)
|
||||
pageElements := admin.Group("/page-elements")
|
||||
{
|
||||
pageElements.GET("", pageElementConfigController.GetAllPageElementConfigs)
|
||||
pageElements.POST("", pageElementConfigController.CreateOrUpdatePageElementConfig)
|
||||
pageElements.PUT("/:id", pageElementConfigController.UpdatePageElementConfig)
|
||||
pageElements.DELETE("/:id", pageElementConfigController.DeletePageElementConfig)
|
||||
pageElements.POST("/batch", pageElementConfigController.BatchUpdatePageElementConfigs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// Public scoreboard
|
||||
api.GET("/scoreboard", scoreboardController.GetPublic)
|
||||
api.GET("/scoreboard/colors/derive", scoreboardController.DeriveColors)
|
||||
|
||||
// Public core endpoints
|
||||
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)
|
||||
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)
|
||||
|
||||
}
|
||||
// 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)
|
||||
func SetupRootRoutes(r *gin.Engine, db *gorm.DB) {
|
||||
seoController := controllers.NewSEOController(db)
|
||||
r.GET("/robots.txt", seoController.GetRobotsTXT)
|
||||
r.GET("/sitemap.xml", seoController.GetSitemapXML)
|
||||
}
|
||||
Reference in New Issue
Block a user