mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
1074 lines
49 KiB
Go
1074 lines
49 KiB
Go
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 services
|
|
weatherService := services.NewWeatherService(db)
|
|
|
|
// Initialize controllers
|
|
healthController := &controllers.HealthController{DB: db}
|
|
baseController := &controllers.BaseController{DB: db}
|
|
authController := controllers.NewAuthController(db)
|
|
contactController := controllers.NewContactController(db, emailService)
|
|
passwordController := controllers.NewPasswordController(db, emailService)
|
|
aiController := controllers.NewAIController(db)
|
|
scoreboardController := controllers.NewScoreboardController(db)
|
|
aboutController := controllers.NewAboutController(db)
|
|
galleryController := controllers.NewGalleryController(db)
|
|
filesController := &controllers.FilesController{DB: db}
|
|
notificationsController := controllers.NewNotificationsController(db, emailService)
|
|
emailController := controllers.NewEmailController(db)
|
|
prefetchController := controllers.NewPrefetchController()
|
|
rembgController := controllers.NewRembgController()
|
|
seoController := controllers.NewSEOController(db)
|
|
navigationController := controllers.NewNavigationController(db)
|
|
pollController := controllers.NewPollController(db)
|
|
sweepstakesController := controllers.NewSweepstakesController(db, emailService)
|
|
i18nController := controllers.NewI18nController()
|
|
clothingController := controllers.NewClothingController(db)
|
|
pageElementConfigController := controllers.NewPageElementConfigController(db)
|
|
articleController := controllers.NewArticleController(db)
|
|
var eshopAdminController *controllers.EshopAdminController
|
|
if config.AppConfig.EshopEnabled {
|
|
eshopAdminController = controllers.NewEshopAdminController(db)
|
|
}
|
|
myuibrixController := &controllers.MyUIbrixController{DB: db}
|
|
editorPreviewController := controllers.NewEditorPreviewController(db)
|
|
shortLinkController := controllers.NewShortLinkController(db)
|
|
commentController := controllers.NewCommentController(db)
|
|
engagementController := controllers.NewEngagementController(db, emailService)
|
|
facrController := controllers.NewFACRController(db)
|
|
manualFACRAdminController := controllers.NewManualFACRAdminController(db)
|
|
youtubeController := controllers.NewYouTubeController(db)
|
|
umamiController := controllers.NewUmamiController()
|
|
imageProcessingController := &controllers.ImageProcessingController{}
|
|
errorController := controllers.NewErrorController(db)
|
|
directoryController := controllers.NewDirectoryController(db)
|
|
financialController := controllers.NewFinancialController(db)
|
|
invoiceController := controllers.NewInvoiceController(db)
|
|
ticketController := controllers.NewTicketController(db)
|
|
ticketCheckoutController := controllers.NewTicketCheckoutController(db)
|
|
qrCodeController := controllers.NewQRCodeController(db)
|
|
|
|
// Facility management controllers
|
|
facilityController := controllers.NewFacilityController()
|
|
equipmentController := controllers.NewEquipmentController()
|
|
maintenanceController := controllers.NewMaintenanceController()
|
|
facilityWeatherController := controllers.NewWeatherController("") // For facilities
|
|
adminWeatherController := controllers.NewAdminWeatherController(weatherService) // For admin dashboard
|
|
bookingCalendarController := controllers.NewBookingCalendarController()
|
|
|
|
// API v1 group
|
|
{
|
|
// Health check
|
|
api.GET("/health", baseController.HealthCheck)
|
|
api.GET("/health/live", healthController.Liveness)
|
|
api.GET("/health/ready", healthController.Readiness)
|
|
api.GET("/health/full", healthController.Health)
|
|
|
|
// CSRF token for cookie-based clients
|
|
api.GET("/csrf-token", middleware.GetCSRFToken)
|
|
|
|
// 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)
|
|
|
|
// Public shortlink creation for visitors (same-site only)
|
|
api.POST("/shortlinks/public", middleware.RateLimit(30, time.Minute), shortLinkController.PublicCreateShortLink)
|
|
|
|
// Public i18n endpoints
|
|
api.GET("/i18n/languages", i18nController.GetLanguages)
|
|
api.GET("/i18n/translations/:language", i18nController.GetTranslations)
|
|
|
|
// 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)
|
|
|
|
// Rembg batch (public status + start)
|
|
api.GET("/rembg/status", rembgController.Status)
|
|
api.POST("/rembg/start", rembgController.Start)
|
|
|
|
api.POST("/errors", middleware.RateLimit(120, time.Minute), errorController.Ingest)
|
|
|
|
// Weather endpoints (public)
|
|
api.GET("/weather", adminWeatherController.GetWeather)
|
|
api.GET("/weather/club", adminWeatherController.GetWeatherForClub)
|
|
api.GET("/weather/match", adminWeatherController.GetWeatherForMatch)
|
|
|
|
// 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.Use(middleware.JWTOptional(db))
|
|
{
|
|
events.GET("", eventController.GetEvents)
|
|
events.GET("/upcoming", eventController.GetUpcomingEvents)
|
|
events.GET("/:id", eventController.GetEventByID)
|
|
}
|
|
|
|
// Comments (public list; attach optional auth to personalize reactions)
|
|
api.GET("/comments", middleware.JWTOptional(db), commentController.GetComments)
|
|
|
|
// Engagement (public + protected)
|
|
api.GET("/engagement/rewards", engagementController.GetRewards)
|
|
|
|
// Protected routes (require authentication)
|
|
protected := api.Group("")
|
|
protected.Use(middleware.JWTAuth(db))
|
|
// CSRF protect state-changing requests when relying on cookies (Bearer tokens are auto-exempt)
|
|
protected.Use(middleware.CSRFProtection())
|
|
{
|
|
// User language preference
|
|
protected.POST("/i18n/user-language", i18nController.SetUserLanguage)
|
|
|
|
// 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", 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)
|
|
protected.DELETE("/comments/:id", commentController.DeleteComment)
|
|
// Comment reactions and unban request
|
|
protected.POST("/comments/:id/react", middleware.RateLimit(60, time.Minute), commentController.React)
|
|
protected.DELETE("/comments/:id/react", commentController.Unreact)
|
|
protected.POST("/comments/unban-request", middleware.RateLimit(5, time.Hour), commentController.CreateUnbanRequest)
|
|
protected.POST("/comments/:id/report", middleware.RateLimit(10, time.Hour), commentController.ReportComment)
|
|
|
|
// Editor preview endpoints (authenticated editors)
|
|
editor := protected.Group("/editor")
|
|
editor.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
// Real-time preview state
|
|
editor.GET("/preview/:session_id", editorPreviewController.GetPreviewState)
|
|
editor.POST("/preview/:session_id", editorPreviewController.UpdatePreviewState)
|
|
editor.POST("/preview/:session_id/apply", editorPreviewController.ApplyPreviewChanges)
|
|
editor.DELETE("/preview/:session_id", editorPreviewController.DiscardPreviewChanges)
|
|
// Validation and variants
|
|
editor.POST("/preview/validate", editorPreviewController.ValidatePreviewConfig)
|
|
editor.GET("/variants/:element_name", editorPreviewController.GetAvailableVariants)
|
|
}
|
|
|
|
// Editor-allowed admin navigation (authenticated editors)
|
|
protected.GET("/admin/navigation/editor", middleware.RoleAuth("editor"), navigationController.GetEditorAllowedAdminNav)
|
|
|
|
// Newsletter preferences token for current user
|
|
protected.GET("/newsletter/token/me", contactController.GetNewsletterTokenForUser)
|
|
|
|
// User profile update
|
|
protected.PUT("/me", authController.UpdateCurrentUser)
|
|
|
|
// AI endpoints (protected)
|
|
ai := protected.Group("/ai")
|
|
{
|
|
ai.POST("/blog/generate", aiController.GenerateBlog)
|
|
ai.POST("/about/generate", aiController.GenerateAboutPage)
|
|
ai.POST("/css/generate", aiController.GenerateCSS)
|
|
ai.POST("/instagram/generate", aiController.GenerateInstagram)
|
|
ai.POST("/instagram/images", aiController.GenerateInstagramImages)
|
|
ai.POST("/main-image/generate", aiController.GenerateMainImage)
|
|
ai.POST("/ocr/process", aiController.ProcessOCR)
|
|
ai.POST("/voice/transcribe", aiController.TranscribeAudio)
|
|
ai.POST("/test-parse", aiController.TestAIParse)
|
|
ai.POST("/translate", aiController.TranslateText)
|
|
ai.GET("/usage/status", aiController.GetAIUsageStatus)
|
|
}
|
|
|
|
// 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.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
protectedEvents.POST("", eventController.CreateEvent)
|
|
protectedEvents.PUT("/:id", eventController.UpdateEvent)
|
|
protectedEvents.DELETE("/:id", eventController.DeleteEvent)
|
|
}
|
|
|
|
// Shortlinks (protected for editors) - create/list
|
|
protectedShortlinks := protected.Group("/shortlinks")
|
|
protectedShortlinks.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
protectedShortlinks.POST("", shortLinkController.CreateShortLink)
|
|
protectedShortlinks.GET("", shortLinkController.ListShortLinks)
|
|
}
|
|
|
|
// Articles (protected - accessible by editors and admins)
|
|
articles := protected.Group("/articles")
|
|
articles.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
articles.POST("", articleController.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.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
teams.POST("", baseController.CreateTeam)
|
|
teams.PUT("/:id", baseController.UpdateTeam)
|
|
teams.DELETE("/:id", baseController.DeleteTeam)
|
|
}
|
|
|
|
// Players (protected)
|
|
players := protected.Group("/players")
|
|
players.Use(middleware.RoleAuth("editor"))
|
|
{
|
|
players.POST("", baseController.CreatePlayer)
|
|
players.PUT("/:id", baseController.UpdatePlayer)
|
|
players.DELETE("/:id", baseController.DeletePlayer)
|
|
}
|
|
|
|
// Sponsors (protected CRUD)
|
|
sponsors := protected.Group("/sponsors")
|
|
sponsors.Use(middleware.RoleAuth("admin"))
|
|
{
|
|
sponsors.POST("", baseController.CreateSponsor)
|
|
sponsors.PUT("/:id", baseController.UpdateSponsor)
|
|
sponsors.DELETE("/:id", baseController.DeleteSponsor)
|
|
}
|
|
|
|
// Banners (protected CRUD)
|
|
banners := protected.Group("/banners")
|
|
banners.Use(middleware.RoleAuth("admin"))
|
|
{
|
|
banners.POST("", baseController.CreateBanner)
|
|
banners.PUT("/:id", baseController.UpdateBanner)
|
|
banners.DELETE("/:id", baseController.DeleteBanner)
|
|
}
|
|
|
|
// Admin routes (single consolidated group)
|
|
admin := protected.Group("/admin")
|
|
admin.Use(middleware.RoleAuth("admin"))
|
|
{
|
|
// i18n management
|
|
i18nAdmin := admin.Group("/i18n")
|
|
{
|
|
i18nAdmin.GET("/languages", i18nController.AdminGetAllLanguages)
|
|
i18nAdmin.POST("/languages", i18nController.AdminCreateLanguage)
|
|
i18nAdmin.PUT("/languages/:id", i18nController.AdminUpdateLanguage)
|
|
i18nAdmin.DELETE("/languages/:id", i18nController.AdminDeleteLanguage)
|
|
i18nAdmin.GET("/translations", i18nController.AdminGetTranslations)
|
|
i18nAdmin.POST("/translations", i18nController.AdminCreateTranslation)
|
|
i18nAdmin.PUT("/translations/:id", i18nController.AdminUpdateTranslation)
|
|
i18nAdmin.DELETE("/translations/:id", i18nController.AdminDeleteTranslation)
|
|
}
|
|
|
|
// Errors
|
|
admin.GET("/errors", errorController.AdminList)
|
|
admin.GET("/errors/:id", errorController.AdminGet)
|
|
// External Error Review proxies
|
|
admin.GET("/errors/external", errorController.AdminListExternal)
|
|
admin.GET("/errors/external/:id", errorController.AdminGetExternal)
|
|
|
|
// Directory Service
|
|
admin.POST("/directory/register", directoryController.RegisterInstance)
|
|
admin.POST("/directory/heartbeat", directoryController.Heartbeat)
|
|
admin.GET("/directory/info", directoryController.GetInstanceInfo)
|
|
|
|
// 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)
|
|
}
|
|
|
|
// E-shop product management (admin)
|
|
if config.AppConfig.EshopEnabled {
|
|
eshop := admin.Group("/eshop")
|
|
{
|
|
// Products
|
|
eshop.GET("/products", eshopAdminController.AdminListProducts)
|
|
eshop.GET("/products/:id", eshopAdminController.AdminGetProduct)
|
|
eshop.POST("/products", eshopAdminController.AdminCreateProduct)
|
|
eshop.PUT("/products/:id", eshopAdminController.AdminUpdateProduct)
|
|
eshop.DELETE("/products/:id", eshopAdminController.AdminDeleteProduct)
|
|
// Variants
|
|
eshop.GET("/products/:id/variants", eshopAdminController.AdminListVariants)
|
|
eshop.POST("/products/:id/variants", eshopAdminController.AdminCreateVariant)
|
|
eshop.PUT("/variants/:id", eshopAdminController.AdminUpdateVariant)
|
|
eshop.DELETE("/variants/:id", eshopAdminController.AdminDeleteVariant)
|
|
}
|
|
}
|
|
// Admin-only endpoints for managing sponsors, etc. (user CRUD removed; no handlers defined)
|
|
|
|
// Manual FACR (manual club data mode) CSV templates & imports
|
|
manual := admin.Group("/manual")
|
|
{
|
|
manual.GET("/competitions", manualFACRAdminController.ListManualCompetitions)
|
|
manual.POST("/competitions", manualFACRAdminController.CreateManualCompetition)
|
|
manual.PUT("/competitions/:id", manualFACRAdminController.UpdateManualCompetition)
|
|
manual.DELETE("/competitions/:id", manualFACRAdminController.DeleteManualCompetition)
|
|
manual.GET("/matches/template", manualFACRAdminController.GetMatchesTemplateCSV)
|
|
manual.GET("/tables/template", manualFACRAdminController.GetTablesTemplateCSV)
|
|
manual.POST("/matches/import", manualFACRAdminController.ImportMatchesCSV)
|
|
manual.POST("/tables/import", manualFACRAdminController.ImportTablesCSV)
|
|
manual.POST("/matches/import-json", manualFACRAdminController.ImportMatchesJSON)
|
|
manual.POST("/tables/import-json", manualFACRAdminController.ImportTablesJSON)
|
|
}
|
|
|
|
// 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 removed: categories are derived from competition aliases
|
|
|
|
// 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.POST("/scoreboard/sponsors/prefill", scoreboardController.PrefillSponsorsFromPage)
|
|
admin.DELETE("/scoreboard/sponsors", scoreboardController.DeleteSponsor)
|
|
admin.GET("/scoreboard/qr", scoreboardController.GetQR)
|
|
admin.POST("/scoreboard/qr", scoreboardController.UploadQR)
|
|
admin.DELETE("/scoreboard/qr", scoreboardController.DeleteQR)
|
|
|
|
// 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/subscribers", contactController.CreateNewsletterSubscriber)
|
|
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.POST("/newsletter/smtp-test", contactController.AdminSmtpTest)
|
|
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("/usage", filesController.GetStorageUsage)
|
|
files.GET("/:id/usages", filesController.GetFileUsages)
|
|
files.DELETE("/:id", filesController.DeleteFile)
|
|
files.POST("/scan", filesController.ScanAndSyncFiles)
|
|
files.POST("/refresh-tracking", filesController.RefreshFileTracking)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// Admin QR codes management
|
|
qr := admin.Group("/qr-codes")
|
|
{
|
|
qr.GET("", qrCodeController.GetQRCodes)
|
|
qr.POST("", qrCodeController.CreateQRCode)
|
|
qr.GET("/:id", qrCodeController.GetQRCode)
|
|
qr.PUT("/:id", qrCodeController.UpdateQRCode)
|
|
qr.DELETE("/:id", qrCodeController.DeleteQRCode)
|
|
}
|
|
|
|
// Facility management (admin)
|
|
facilities := admin.Group("/facilities")
|
|
{
|
|
facilities.GET("", facilityController.GetFacilities)
|
|
facilities.GET("/:id", facilityController.GetFacility)
|
|
facilities.POST("", facilityController.CreateFacility)
|
|
facilities.PUT("/:id", facilityController.UpdateFacility)
|
|
facilities.DELETE("/:id", facilityController.DeleteFacility)
|
|
facilities.GET("/:id/bookings", facilityController.GetFacilityBookings)
|
|
}
|
|
|
|
// Equipment management (admin)
|
|
equipment := admin.Group("/equipment")
|
|
{
|
|
equipment.GET("", equipmentController.GetEquipment)
|
|
equipment.POST("", equipmentController.CreateEquipment)
|
|
equipment.PUT("/:id", equipmentController.UpdateEquipment)
|
|
equipment.DELETE("/:id", equipmentController.DeleteEquipment)
|
|
}
|
|
|
|
// Maintenance management (admin)
|
|
maintenance := admin.Group("/maintenance")
|
|
{
|
|
maintenance.GET("", maintenanceController.GetMaintenance)
|
|
maintenance.POST("", maintenanceController.CreateMaintenance)
|
|
maintenance.PUT("/:id", maintenanceController.UpdateMaintenance)
|
|
}
|
|
|
|
// 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)
|
|
polls.GET("/:id/votes", pollController.AdminListVotes)
|
|
}
|
|
|
|
// Engagement management (admin)
|
|
engagement := admin.Group("/engagement")
|
|
{
|
|
engagement.GET("/rewards", engagementController.AdminListRewards)
|
|
engagement.POST("/rewards", engagementController.AdminCreateReward)
|
|
engagement.PUT("/rewards/:id", engagementController.AdminUpdateReward)
|
|
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)
|
|
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)
|
|
}
|
|
|
|
// MyUIbrix optimization and validation endpoints (admin)
|
|
myuibrix := admin.Group("/myuibrix")
|
|
{
|
|
myuibrix.POST("/validate", myuibrixController.ValidateElementConfig)
|
|
myuibrix.POST("/validate-batch", myuibrixController.BatchValidateConfigs)
|
|
myuibrix.GET("/preview", myuibrixController.GetElementPreview)
|
|
myuibrix.GET("/optimize-layout", myuibrixController.OptimizePageLayout)
|
|
}
|
|
|
|
// Short links (admin)
|
|
shortlinks := admin.Group("/shortlinks")
|
|
{
|
|
shortlinks.POST("", shortLinkController.CreateShortLink)
|
|
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)
|
|
}
|
|
|
|
// Financial management (admin)
|
|
financial := admin.Group("/financial")
|
|
{
|
|
// Dashboard and overview
|
|
financial.GET("/dashboard", financialController.GetFinancialDashboard)
|
|
|
|
// Budget management
|
|
budgets := financial.Group("/budgets")
|
|
{
|
|
budgets.GET("", financialController.GetBudgets)
|
|
budgets.GET("/:id", financialController.GetBudget)
|
|
budgets.POST("", financialController.CreateBudget)
|
|
budgets.PUT("/:id", financialController.UpdateBudget)
|
|
budgets.DELETE("/:id", financialController.DeleteBudget)
|
|
budgets.GET("/categories", financialController.GetBudgetCategories)
|
|
budgets.GET("/overview", financialController.GetBudgetOverview)
|
|
}
|
|
|
|
// Sponsorship management
|
|
sponsorships := financial.Group("/sponsorships")
|
|
{
|
|
sponsorships.GET("", financialController.GetSponsorships)
|
|
sponsorships.GET("/:id", financialController.GetSponsorship)
|
|
sponsorships.POST("", financialController.CreateSponsorship)
|
|
sponsorships.PUT("/:id", financialController.UpdateSponsorship)
|
|
sponsorships.DELETE("/:id", financialController.DeleteSponsorship)
|
|
sponsorships.GET("/overview", financialController.GetSponsorshipOverview)
|
|
}
|
|
|
|
// Expense management
|
|
expenses := financial.Group("/expenses")
|
|
{
|
|
expenses.GET("", financialController.GetExpenses)
|
|
expenses.GET("/:id", financialController.GetExpense)
|
|
expenses.POST("", financialController.CreateExpense)
|
|
expenses.PUT("/:id", financialController.UpdateExpense)
|
|
expenses.DELETE("/:id", financialController.DeleteExpense)
|
|
expenses.PATCH("/:id/approve", financialController.ApproveExpense)
|
|
expenses.PATCH("/:id/reject", financialController.RejectExpense)
|
|
expenses.POST("/upload-receipt", financialController.UploadReceipt)
|
|
expenses.GET("/categories", financialController.GetExpenseCategories)
|
|
expenses.GET("/overview", financialController.GetExpenseOverview)
|
|
}
|
|
|
|
// Reports and analytics
|
|
reports := financial.Group("/reports")
|
|
{
|
|
reports.GET("", financialController.GetFinancialReports)
|
|
reports.POST("/generate", financialController.GenerateFinancialReport)
|
|
reports.GET("/:id", financialController.GetFinancialReport)
|
|
reports.DELETE("/:id", financialController.DeleteFinancialReport)
|
|
}
|
|
|
|
// Settings
|
|
financial.GET("/settings", financialController.GetFinancialSettings)
|
|
financial.PUT("/settings", financialController.UpdateFinancialSettings)
|
|
}
|
|
|
|
// Invoice management (admin)
|
|
invoices := admin.Group("/invoices")
|
|
{
|
|
// Invoice CRUD
|
|
invoices.GET("", invoiceController.GetInvoices)
|
|
invoices.GET("/:id", invoiceController.GetInvoice)
|
|
invoices.POST("", invoiceController.CreateInvoice)
|
|
invoices.PUT("/:id", invoiceController.UpdateInvoice)
|
|
invoices.DELETE("/:id", invoiceController.DeleteInvoice)
|
|
|
|
// Invoice operations
|
|
invoices.POST("/:id/generate-pdf", invoiceController.GenerateInvoicePDF)
|
|
invoices.POST("/:id/send", invoiceController.SendInvoice)
|
|
|
|
// Customer management
|
|
customers := invoices.Group("/customers")
|
|
{
|
|
customers.GET("", invoiceController.GetCustomers)
|
|
customers.GET("/:id", invoiceController.GetCustomer)
|
|
customers.POST("", invoiceController.CreateCustomer)
|
|
customers.PUT("/:id", invoiceController.UpdateCustomer)
|
|
customers.DELETE("/:id", invoiceController.DeleteCustomer)
|
|
customers.GET("/autofill", invoiceController.AutofillCustomerByICO)
|
|
}
|
|
|
|
// Settings
|
|
invoices.GET("/settings", invoiceController.GetInvoiceSettings)
|
|
invoices.PUT("/settings", invoiceController.UpdateInvoiceSettings)
|
|
invoices.GET("/ares-search/:ico", invoiceController.SearchSupplierByICO)
|
|
}
|
|
|
|
}
|
|
|
|
RegisterAnalyticsRoutes(api, db)
|
|
RegisterContactInfoRoutes(api, db)
|
|
|
|
api.POST("/upload", middleware.RateLimit(30, time.Minute), baseController.UploadImage)
|
|
|
|
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)
|
|
}
|
|
|
|
api.GET("/scoreboard", scoreboardController.GetPublic)
|
|
api.GET("/scoreboard/colors/derive", scoreboardController.DeriveColors)
|
|
api.GET("/scoreboard/sponsors", scoreboardController.ListSponsors)
|
|
api.GET("/scoreboard/qr", scoreboardController.GetQR)
|
|
|
|
api.GET("/settings", baseController.GetPublicSettings)
|
|
api.GET("/competition-aliases", baseController.GetPublicCompetitionAliases)
|
|
api.GET("/public/team-logo-overrides", baseController.GetPublicTeamLogoOverrides)
|
|
|
|
// Articles (public; use optional auth so admin/editor can see drafts in list when requesting published=false)
|
|
articlesPub := api.Group("/articles")
|
|
articlesPub.Use(middleware.JWTOptional(db))
|
|
{
|
|
articlesPub.GET("/featured", baseController.GetFeaturedArticles)
|
|
articlesPub.GET("", baseController.GetArticles)
|
|
articlesPub.GET("/slug/:slug", baseController.GetArticleBySlug)
|
|
articlesPub.GET("/:id", baseController.GetArticle)
|
|
articlesPub.POST("/:id/read", baseController.IncrementArticleRead)
|
|
articlesPub.POST("/:id/track-view", baseController.TrackArticleView)
|
|
articlesPub.GET("/:id/match-link", baseController.GetArticleMatchLink)
|
|
}
|
|
api.GET("/categories", baseController.GetCategories)
|
|
api.GET("/youtube/videos", youtubeController.GetYouTubeVideos)
|
|
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("/banners", baseController.GetBanners)
|
|
api.GET("/matches", baseController.GetMatches)
|
|
api.GET("/matches/history", baseController.GetMatchesHistory)
|
|
api.GET("/standings", baseController.GetStandings)
|
|
|
|
api.GET("/gallery/albums", galleryController.GetGalleryAlbums)
|
|
api.GET("/gallery/albums/:id", galleryController.GetGalleryAlbum)
|
|
api.GET("/gallery/proxy-image", galleryController.ProxyImage)
|
|
|
|
api.GET("/zonerama/album", baseController.GetZoneramaAlbum)
|
|
api.GET("/zonerama-album", baseController.GetZoneramaAlbum)
|
|
api.GET("/zonerama/picks", baseController.GetZoneramaPicks)
|
|
|
|
api.GET("/clothing", clothingController.GetClothing)
|
|
api.GET("/sweepstakes/current", sweepstakesController.GetCurrent)
|
|
api.GET("/sweepstakes/:id/visual", sweepstakesController.PublicVisualData)
|
|
|
|
pollsPub := api.Group("/polls")
|
|
pollsPub.Use(middleware.JWTOptional(db))
|
|
{
|
|
pollsPub.GET("", pollController.GetPolls)
|
|
pollsPub.GET("/:id", pollController.GetPoll)
|
|
pollsPub.POST("/:id/vote", middleware.RateLimit(10, time.Minute), pollController.Vote)
|
|
pollsPub.GET("/:id/results", pollController.GetPollResults)
|
|
}
|
|
|
|
api.POST("/contact", middleware.RateLimit(10, time.Minute), contactController.SubmitContactForm)
|
|
api.POST("/newsletter/subscribe", middleware.RateLimit(30, time.Minute), contactController.SubscribeToNewsletter)
|
|
api.POST("/newsletter/unsubscribe/:email", middleware.RateLimit(30, time.Minute), contactController.UnsubscribeFromNewsletter)
|
|
api.POST("/newsletter/setup", middleware.RateLimit(30, time.Minute), contactController.SetupNewsletterPreferences)
|
|
api.GET("/newsletter/preferences", contactController.GetNewsletterPreferencesByToken)
|
|
api.POST("/newsletter/preferences", contactController.SaveNewsletterPreferencesByToken)
|
|
api.POST("/newsletter/unsubscribe-token", contactController.UnsubscribeByToken)
|
|
|
|
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)
|
|
}
|
|
|
|
// Public facility management routes
|
|
api.GET("/facilities", facilityController.GetPublicFacilities)
|
|
api.GET("/facilities/calendar", bookingCalendarController.GetCalendarEvents)
|
|
api.POST("/facilities/bookings", middleware.JWTAuth(db), facilityController.CreateBooking)
|
|
api.GET("/facilities/:id", facilityController.GetFacility)
|
|
api.GET("/facilities/:id/availability", facilityController.GetFacilityAvailability)
|
|
api.GET("/facilities/:id/weather", facilityWeatherController.GetWeatherForecast)
|
|
|
|
// Public ticket endpoints
|
|
tickets := api.Group("/tickets")
|
|
{
|
|
tickets.GET("/available", ticketController.GetAvailableTickets)
|
|
tickets.GET("/campaigns", ticketController.GetCampaigns)
|
|
tickets.GET("/campaigns/:id", ticketController.GetCampaign)
|
|
// Ticket reservation and validation require auth
|
|
tickets.Use(middleware.JWTAuth(db))
|
|
{
|
|
tickets.POST("/reserve", ticketController.ReserveTickets)
|
|
tickets.POST("/:id/confirm", ticketController.ConfirmTicket)
|
|
tickets.POST("/:id/validate", ticketController.ValidateTicket)
|
|
}
|
|
}
|
|
|
|
// Ticket checkout (requires auth)
|
|
ticketCheckout := api.Group("/ticket-checkout")
|
|
ticketCheckout.Use(middleware.JWTAuth(db))
|
|
{
|
|
ticketCheckout.POST("/order", ticketCheckoutController.CreateTicketOrder)
|
|
ticketCheckout.POST("/orders/:order_id/complete", ticketCheckoutController.CompleteTicketOrder)
|
|
ticketCheckout.GET("/orders", ticketCheckoutController.GetTicketOrders)
|
|
}
|
|
|
|
// User tickets and QR codes (require auth)
|
|
userTickets := api.Group("/tickets")
|
|
userTickets.Use(middleware.JWTAuth(db))
|
|
{
|
|
userTickets.GET("/my-tickets", ticketController.GetMyTickets)
|
|
userTickets.GET("/:id/qr", qrCodeController.GenerateTicketQR)
|
|
userTickets.GET("/:id/qr-download", qrCodeController.DownloadTicketQR)
|
|
userTickets.POST("/validate-qr", qrCodeController.ValidateTicketFromQR)
|
|
}
|
|
|
|
api.GET("/umami/config", umamiController.GetUmamiConfig)
|
|
api.POST("/umami/initialize-setup", umamiController.InitializeUmamiSetup)
|
|
// Adblock-safe public alias for config (avoids 'umami' keyword)
|
|
api.GET("/insights/config", umamiController.GetUmamiConfig)
|
|
|
|
// ... (rest of the code remains the same)
|
|
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)
|
|
}
|
|
|
|
// Adblock-safe admin aliases (avoid 'umami'/'metrics' in path)
|
|
insights := api.Group("/admin/insights")
|
|
insights.Use(middleware.JWTAuth(db))
|
|
insights.Use(middleware.RoleAuth("admin"))
|
|
{
|
|
insights.POST("/initialize", umamiController.InitializeUmami)
|
|
insights.GET("/summary", umamiController.GetStats)
|
|
insights.GET("/breakdown/:type", umamiController.GetMetrics)
|
|
insights.GET("/pageviews", umamiController.GetPageviews)
|
|
}
|
|
|
|
// Accountant routes (role: accountant)
|
|
// Accountants have access to financial management, budgets, expenses, sponsorships, and invoices
|
|
accountant := api.Group("/accountant")
|
|
accountant.Use(middleware.JWTAuth(db))
|
|
accountant.Use(middleware.RoleAuth("accountant"))
|
|
{
|
|
// Financial dashboard
|
|
accountant.GET("/dashboard", financialController.GetFinancialDashboard)
|
|
|
|
// Budget management
|
|
budgets := accountant.Group("/budgets")
|
|
{
|
|
budgets.GET("", financialController.GetBudgets)
|
|
budgets.GET("/:id", financialController.GetBudget)
|
|
budgets.POST("", financialController.CreateBudget)
|
|
budgets.PUT("/:id", financialController.UpdateBudget)
|
|
budgets.DELETE("/:id", financialController.DeleteBudget)
|
|
budgets.GET("/categories", financialController.GetBudgetCategories)
|
|
budgets.GET("/overview", financialController.GetBudgetOverview)
|
|
}
|
|
|
|
// Sponsorship management
|
|
sponsorships := accountant.Group("/sponsorships")
|
|
{
|
|
sponsorships.GET("", financialController.GetSponsorships)
|
|
sponsorships.GET("/:id", financialController.GetSponsorship)
|
|
sponsorships.POST("", financialController.CreateSponsorship)
|
|
sponsorships.PUT("/:id", financialController.UpdateSponsorship)
|
|
sponsorships.DELETE("/:id", financialController.DeleteSponsorship)
|
|
sponsorships.GET("/overview", financialController.GetSponsorshipOverview)
|
|
}
|
|
|
|
// Expense management
|
|
expenses := accountant.Group("/expenses")
|
|
{
|
|
expenses.GET("", financialController.GetExpenses)
|
|
expenses.GET("/:id", financialController.GetExpense)
|
|
expenses.POST("", financialController.CreateExpense)
|
|
expenses.PUT("/:id", financialController.UpdateExpense)
|
|
expenses.DELETE("/:id", financialController.DeleteExpense)
|
|
expenses.PATCH("/:id/approve", financialController.ApproveExpense)
|
|
expenses.PATCH("/:id/reject", financialController.RejectExpense)
|
|
expenses.POST("/upload-receipt", financialController.UploadReceipt)
|
|
expenses.GET("/categories", financialController.GetExpenseCategories)
|
|
expenses.GET("/overview", financialController.GetExpenseOverview)
|
|
}
|
|
|
|
// Financial reports
|
|
reports := accountant.Group("/reports")
|
|
{
|
|
reports.GET("", financialController.GetFinancialReports)
|
|
reports.POST("/generate", financialController.GenerateFinancialReport)
|
|
reports.GET("/:id", financialController.GetFinancialReport)
|
|
reports.DELETE("/:id", financialController.DeleteFinancialReport)
|
|
}
|
|
|
|
// Financial settings
|
|
accountant.GET("/settings", financialController.GetFinancialSettings)
|
|
accountant.PUT("/settings", financialController.UpdateFinancialSettings)
|
|
|
|
// Invoice management
|
|
invoices := accountant.Group("/invoices")
|
|
{
|
|
// Invoice CRUD
|
|
invoices.GET("", invoiceController.GetInvoices)
|
|
invoices.GET("/:id", invoiceController.GetInvoice)
|
|
invoices.POST("", invoiceController.CreateInvoice)
|
|
invoices.PUT("/:id", invoiceController.UpdateInvoice)
|
|
invoices.DELETE("/:id", invoiceController.DeleteInvoice)
|
|
|
|
// Invoice operations
|
|
invoices.POST("/:id/generate-pdf", invoiceController.GenerateInvoicePDF)
|
|
invoices.POST("/:id/send", invoiceController.SendInvoice)
|
|
|
|
// Customer management
|
|
customers := invoices.Group("/customers")
|
|
{
|
|
customers.GET("", invoiceController.GetCustomers)
|
|
customers.GET("/:id", invoiceController.GetCustomer)
|
|
customers.POST("", invoiceController.CreateCustomer)
|
|
customers.PUT("/:id", invoiceController.UpdateCustomer)
|
|
customers.DELETE("/:id", invoiceController.DeleteCustomer)
|
|
customers.GET("/autofill", invoiceController.AutofillCustomerByICO)
|
|
}
|
|
|
|
// Invoice settings
|
|
invoices.GET("/settings", invoiceController.GetInvoiceSettings)
|
|
invoices.PUT("/settings", invoiceController.UpdateInvoiceSettings)
|
|
invoices.GET("/ares-search/:ico", invoiceController.SearchSupplierByICO)
|
|
}
|
|
|
|
// Ticket management (admin)
|
|
ticketsAdmin := admin.Group("/tickets")
|
|
{
|
|
// Campaigns
|
|
ticketsAdmin.GET("/campaigns", ticketController.AdminGetCampaigns)
|
|
ticketsAdmin.POST("/campaigns", ticketController.AdminCreateCampaign)
|
|
ticketsAdmin.GET("/campaigns/:id", ticketController.GetCampaign)
|
|
ticketsAdmin.PUT("/campaigns/:id", ticketController.AdminUpdateCampaign)
|
|
ticketsAdmin.DELETE("/campaigns/:id", ticketController.AdminDeleteCampaign)
|
|
|
|
// Ticket types
|
|
ticketsAdmin.GET("/types", ticketController.AdminGetTicketTypes)
|
|
ticketsAdmin.POST("/types", ticketController.AdminCreateTicketType)
|
|
ticketsAdmin.GET("/types/:id", ticketController.AdminGetTicketType)
|
|
ticketsAdmin.PUT("/types/:id", ticketController.AdminUpdateTicketType)
|
|
ticketsAdmin.DELETE("/types/:id", ticketController.AdminDeleteTicketType)
|
|
|
|
// Ticket management
|
|
ticketsAdmin.GET("", ticketController.AdminGetTickets)
|
|
ticketsAdmin.GET("/:id", ticketController.AdminGetTicket)
|
|
ticketsAdmin.PATCH("/:id/status", ticketController.AdminUpdateTicketStatus)
|
|
ticketsAdmin.POST("/:id/validate", ticketController.AdminValidateTicket)
|
|
ticketsAdmin.GET("/sales/overview", ticketController.AdminGetSalesOverview)
|
|
ticketsAdmin.GET("/sales/export", ticketController.AdminExportSales)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func SetupRootRoutes(r *gin.Engine, db *gorm.DB) {
|
|
seoController := controllers.NewSEOController(db)
|
|
shortLinkController := controllers.NewShortLinkController(db)
|
|
r.GET("/robots.txt", seoController.GetRobotsTXT)
|
|
r.GET("/sitemap.xml", seoController.GetSitemapXML)
|
|
// Public short-link redirects and generic tracked redirect
|
|
r.GET("/s/:code", shortLinkController.RedirectShort)
|
|
r.GET("/r", shortLinkController.RedirectAndTrack)
|
|
}
|