turash/bugulma/backend/internal/server/server.go
2025-12-15 10:06:41 +01:00

550 lines
24 KiB
Go

package server
import (
"context"
"fmt"
"log"
"bugulma/backend/internal/analysis/regulatory"
"bugulma/backend/internal/analysis/risk"
"bugulma/backend/internal/analysis/transport"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/financial"
"bugulma/backend/internal/geospatial"
"bugulma/backend/internal/graph"
"bugulma/backend/internal/handler"
"bugulma/backend/internal/matching"
"bugulma/backend/internal/middleware"
"bugulma/backend/internal/repository"
"bugulma/backend/internal/routes"
"bugulma/backend/internal/service"
"bugulma/backend/pkg/config"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// StartServer initializes and starts the Bugulma backend server
func StartServer(port string) error {
// Load .env file
if err := godotenv.Load(); err != nil {
log.Printf("Warning: .env file not found, using environment variables")
}
// Load configuration
cfg := config.Load()
// Override port if provided
if port != "" {
cfg.ServerPort = port
}
// Initialize database
db, err := initializeDatabase(cfg)
if err != nil {
return fmt.Errorf("failed to initialize database: %w", err)
}
// Initialize repositories
userRepo, orgRepo, siteRepo, _, resourceFlowRepo, matchRepo, negotiationHistoryRepo, sharedAssetRepo, proposalRepo, geoFeatureRepo, productRepo, serviceRepo, communityListingRepo, publicTransportRepo := initializeRepositories(db)
// Seed default data
if err := seedDefaultData(userRepo); err != nil {
log.Printf("Warning: Failed to seed default data: %v", err)
}
// (Public transport seeding will occur after services are initialized)
// Initialize graph database (optional)
graphSyncService, graphHandler, graphTraversalHandler, orgGraphRepo, siteGraphRepo, addressGraphRepo, flowGraphRepo, matchGraphRepo, sharedAssetGraphRepo := initializeGraphDatabase(cfg)
// Initialize services
orgService, siteService, resourceFlowService, geospatialService, sharedAssetService, analyticsService, authService, matchingSvc, cacheService, websocketService, aiService, proposalService, spatialMatcher, environmentalSvc, facilityOptimizer, economicService, userService, adminService, i18nService, subscriptionService, verificationService, productRepo, serviceRepo, communityListingRepo, publicTransportService := initializeServices(
cfg, userRepo, orgRepo, siteRepo, resourceFlowRepo, matchRepo, negotiationHistoryRepo, sharedAssetRepo, proposalRepo, geoFeatureRepo,
orgGraphRepo, siteGraphRepo, addressGraphRepo, flowGraphRepo, matchGraphRepo, sharedAssetGraphRepo, db,
productRepo, serviceRepo, communityListingRepo,
)
// Store geographical services for future API endpoints
_ = spatialMatcher // Will be used in future API handlers
_ = environmentalSvc // Will be used in future API handlers
_ = facilityOptimizer // Will be used in future API handlers
// Seed public transport data (if repository and imported files present)
if publicTransportRepo != nil && publicTransportService != nil {
if err := service.ImportPublicTransportData(context.Background(), publicTransportService, publicTransportRepo); err != nil {
log.Printf("Warning: failed to import public transport data: %v", err)
}
}
// Set graph repositories on services that need them
if orgGraphRepo != nil {
orgService.SetGraphRepository(orgGraphRepo)
}
// Initialize GTFS repository and schedule service
gtfsRepo := repository.NewPublicTransportGTFSRepository(db)
scheduleService := service.NewScheduleService(gtfsRepo, publicTransportRepo)
// Initialize handlers (orgHandler is created inside initializeHandlers)
// Create SettingsService early so we can inject it into handlers and the router
settingsService := service.NewSettingsService(repository.NewSystemSettingsRepository(db))
orgHandler, siteHandler, resourceFlowHandler, proposalHandler, matchingHandler, authHandler, sharedAssetHandler, geospatialHandler, analyticsHandler, aiHandler, heritageHandler, userHandler, orgAdminHandler, i18nHandler, contentHandler, adminHandler, settingsHandler, subscriptionHandler, discoveryHandler, publicTransportHandler := initializeHandlers(
cfg, orgService, siteService, resourceFlowService, matchingSvc, authService, sharedAssetService, proposalService,
geospatialService, analyticsService, aiService, cacheService, db, userService, adminService, i18nService, subscriptionService, verificationService, publicTransportService, scheduleService, settingsService,
)
// Setup router (pass settings service so maintenance middleware can be registered)
router := setupRouter(authService, settingsService)
// Initialize event-driven matching service with proper dependencies
// Note: eventBus is created in initializeServices, but we need to recreate it here for event-driven service
eventBus, err := service.NewRedisEventBus("redis://localhost:6379")
if err != nil {
log.Printf("Warning: Failed to create Redis event bus for event-driven matching: %v", err)
eventBus = nil
}
eventDrivenMatchingService := service.NewEventDrivenMatchingService(
eventBus, matchingSvc, resourceFlowRepo, matchRepo, websocketService.GetHub(), economicService,
)
if eventBus != nil {
if err := eventDrivenMatchingService.StartEventProcessing(context.Background()); err != nil {
log.Printf("Warning: Failed to start event-driven matching: %v", err)
}
}
// Setup routes
setupRoutes(router, orgHandler, siteHandler, resourceFlowHandler, proposalHandler, matchingHandler, authHandler,
sharedAssetHandler, geospatialHandler, analyticsHandler, aiHandler, heritageHandler, graphHandler,
graphTraversalHandler, websocketService, orgService, siteService, geospatialService, graphSyncService,
userHandler, orgAdminHandler, i18nHandler, contentHandler, adminHandler, settingsHandler, subscriptionHandler, authService, discoveryHandler, publicTransportHandler)
// Start server
log.Printf("Server starting on port %s", cfg.ServerPort)
if err := router.Run(fmt.Sprintf(":%s", cfg.ServerPort)); err != nil {
return fmt.Errorf("failed to start server: %w", err)
}
return nil
}
// initializeDatabase sets up the database connection and runs migrations
func initializeDatabase(cfg *config.Config) (*gorm.DB, error) {
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
cfg.PostgresHost,
cfg.PostgresPort,
cfg.PostgresUser,
cfg.PostgresPassword,
cfg.PostgresDB,
cfg.PostgresSSLMode,
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to PostgreSQL: %w", err)
}
log.Println("Successfully connected to PostgreSQL database")
// Run PostGIS-specific migrations FIRST (before AutoMigrate)
if err := domain.RunPostGISMigrations(db); err != nil {
log.Printf("Warning: Failed to run PostGIS migrations: %v", err)
log.Println("Continuing without PostGIS spatial features")
} else {
log.Println("PostGIS migrations completed successfully")
}
// Run search migrations (pg_trgm extension and indexes)
if err := domain.RunSearchMigrations(db); err != nil {
log.Printf("Warning: Failed to run search migrations: %v", err)
log.Println("Continuing without fuzzy search features")
} else {
log.Println("Search migrations completed successfully")
}
// Auto-migrate database schema
if err := domain.AutoMigrate(db); err != nil {
return nil, fmt.Errorf("failed to migrate database: %w", err)
}
// Create additional indexes after AutoMigrate
if err := domain.CreateIndexes(db); err != nil {
log.Printf("Warning: Failed to create indexes: %v", err)
} else {
log.Println("Indexes created successfully")
}
log.Println("Database migration completed successfully")
return db, nil
}
// initializeRepositories creates all repository instances
func initializeRepositories(db *gorm.DB) (domain.UserRepository, domain.OrganizationRepository, domain.SiteRepository, domain.AddressRepository, domain.ResourceFlowRepository, domain.MatchRepository, domain.NegotiationHistoryRepository, domain.SharedAssetRepository, domain.ProposalRepository, domain.GeographicalFeatureRepository, domain.ProductRepository, domain.ServiceRepository, domain.CommunityListingRepository, domain.PublicTransportRepository) {
orgRepo := repository.NewOrganizationRepository(db)
siteRepo := repository.NewSiteRepository(db)
addressRepo := repository.NewAddressRepository(db)
resourceFlowRepo := repository.NewResourceFlowRepository(db)
matchRepo := repository.NewMatchRepository(db)
negotiationHistoryRepo := repository.NewNegotiationHistoryRepository(db)
sharedAssetRepo := repository.NewSharedAssetRepository(db)
proposalRepo := repository.NewProposalRepository(db)
userRepo := repository.NewUserRepository(db)
geoFeatureRepo := repository.NewGeographicalFeatureRepository(db)
productRepo := repository.NewProductRepository(db)
serviceRepo := repository.NewServiceRepository(db)
communityListingRepo := repository.NewCommunityListingRepository(db)
publicTransportRepo := repository.NewPublicTransportRepository(db)
_ = repository.NewResourceFlowVersionRepository(db)
return userRepo, orgRepo, siteRepo, addressRepo, resourceFlowRepo, matchRepo, negotiationHistoryRepo, sharedAssetRepo, proposalRepo, geoFeatureRepo, productRepo, serviceRepo, communityListingRepo, publicTransportRepo
}
// seedDefaultData seeds initial data like admin user
func seedDefaultData(userRepo domain.UserRepository) error {
_, err := userRepo.GetByEmail(context.Background(), "admin@turash.dev")
if err == nil {
return nil
}
adminUser := &domain.User{
ID: "admin-1",
Email: "admin@turash.dev",
Name: "Admin User",
Password: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // "password" hashed with bcrypt
Role: domain.UserRoleAdmin,
}
return userRepo.Create(context.Background(), adminUser)
}
// initializeGraphDatabase initializes Neo4j graph database if enabled
func initializeGraphDatabase(cfg *config.Config) (*service.GraphSyncService, *handler.GraphHandler, *handler.GraphTraversalHandler, *repository.GraphOrganizationRepository, *repository.GraphSiteRepository, *repository.GraphAddressRepository, *repository.GraphResourceFlowRepository, *repository.GraphMatchRepository, *repository.GraphSharedAssetRepository) {
if !cfg.Neo4jEnabled {
return nil, nil, nil, nil, nil, nil, nil, nil, nil
}
neo4jURI := cfg.Neo4jURI
if neo4jURI == "" {
log.Println("Warning: Neo4j URI not configured, graph database disabled")
return nil, nil, nil, nil, nil, nil, nil, nil, nil
}
auth := neo4j.BasicAuth(cfg.Neo4jUsername, cfg.Neo4jPassword, "")
driver, err := neo4j.NewDriverWithContext(neo4jURI, auth)
if err != nil {
log.Printf("Warning: Failed to create Neo4j driver: %v", err)
log.Println("Continuing without graph database features")
return nil, nil, nil, nil, nil, nil, nil, nil, nil
}
// Verify connectivity
ctx := context.Background()
if err := driver.VerifyConnectivity(ctx); err != nil {
log.Printf("Warning: Failed to connect to Neo4j: %v", err)
log.Println("Continuing without graph database features")
return nil, nil, nil, nil, nil, nil, nil, nil, nil
}
log.Println("Successfully connected to Neo4j graph database")
// Initialize graph repositories
orgGraphRepo := repository.NewGraphOrganizationRepository(driver, cfg.Neo4jDatabase)
siteGraphRepo := repository.NewGraphSiteRepository(driver, cfg.Neo4jDatabase)
addressGraphRepo := repository.NewGraphAddressRepository(driver, cfg.Neo4jDatabase)
flowGraphRepo := repository.NewGraphResourceFlowRepository(driver, cfg.Neo4jDatabase)
matchGraphRepo := repository.NewGraphMatchRepository(driver, cfg.Neo4jDatabase)
sharedAssetGraphRepo := repository.NewGraphSharedAssetRepository(driver, cfg.Neo4jDatabase)
productGraphRepo := repository.NewGraphProductRepository(driver, cfg.Neo4jDatabase)
serviceGraphRepo := repository.NewGraphServiceRepository(driver, cfg.Neo4jDatabase)
// Initialize graph sync service
graphSyncService := service.NewGraphSyncService(
orgGraphRepo, siteGraphRepo, addressGraphRepo, flowGraphRepo,
matchGraphRepo, sharedAssetGraphRepo, productGraphRepo, serviceGraphRepo,
)
// Initialize graph calculator for traversal service
graphCalculator := graph.NewCalculatorWithDefaults(driver, cfg.Neo4jDatabase)
// Initialize services
graphTraversalService := service.NewGraphTraversalService(graphCalculator)
// Initialize handlers
graphHandler := handler.NewGraphHandler(driver, cfg.Neo4jDatabase, graphSyncService)
graphTraversalHandler := handler.NewGraphTraversalHandler(graphTraversalService)
return graphSyncService, graphHandler, graphTraversalHandler, orgGraphRepo, siteGraphRepo, addressGraphRepo, flowGraphRepo, matchGraphRepo, sharedAssetGraphRepo
}
// initializeServices creates all service instances
func initializeServices(
cfg *config.Config,
userRepo domain.UserRepository,
orgRepo domain.OrganizationRepository,
siteRepo domain.SiteRepository,
resourceFlowRepo domain.ResourceFlowRepository,
matchRepo domain.MatchRepository,
negotiationHistoryRepo domain.NegotiationHistoryRepository,
sharedAssetRepo domain.SharedAssetRepository,
proposalRepo domain.ProposalRepository,
geoFeatureRepo domain.GeographicalFeatureRepository,
orgGraphRepo *repository.GraphOrganizationRepository,
siteGraphRepo *repository.GraphSiteRepository,
addressGraphRepo *repository.GraphAddressRepository,
flowGraphRepo *repository.GraphResourceFlowRepository,
matchGraphRepo *repository.GraphMatchRepository,
sharedAssetGraphRepo *repository.GraphSharedAssetRepository,
db *gorm.DB,
productRepo domain.ProductRepository,
serviceRepo domain.ServiceRepository,
communityListingRepo domain.CommunityListingRepository,
) (*service.OrganizationService, *service.SiteService, *service.ResourceFlowService, *service.GeospatialService, *service.SharedAssetService, *service.AnalyticsService, *service.AuthService, *matching.Service, service.CacheService, *service.WebSocketService, *service.AIService, *service.ProposalService, *service.SpatialResourceMatcher, *service.EnvironmentalImpactService, *service.FacilityLocationOptimizer, *service.EconomicService, *service.UserService, *service.AdminService, *service.I18nService, *service.SubscriptionService, *service.VerificationService, domain.ProductRepository, domain.ServiceRepository, domain.CommunityListingRepository, *service.PublicTransportService) {
// Create event bus first
eventBus, err := service.NewRedisEventBus("redis://localhost:6379")
if err != nil {
log.Printf("Warning: Failed to create Redis event bus, using in-memory: %v", err)
eventBus = nil
}
// Create services
orgService := service.NewOrganizationService(orgRepo, nil) // Graph repo set later if available
siteService := service.NewSiteService(siteRepo)
resourceFlowService := service.NewResourceFlowService(resourceFlowRepo, eventBus)
authService := service.NewAuthService(userRepo, cfg.JWTSecret)
sharedAssetService := service.NewSharedAssetService(sharedAssetRepo)
geospatialService := service.NewGeospatialService(db, geoFeatureRepo)
analyticsService := service.NewAnalyticsService(db, orgRepo, siteRepo, resourceFlowRepo, matchRepo, sharedAssetRepo)
proposalService := service.NewProposalService(proposalRepo)
// Initialize geospatial calculator
geoCalc := geospatial.NewCalculatorWithDefaults()
// Initialize geographical transportation service
geoTransportSvc := service.NewTransportationService(geoCalc)
// Initialize geographical services
spatialMatcher := service.NewSpatialResourceMatcher(geoFeatureRepo, siteRepo, resourceFlowRepo, geospatialService, geoTransportSvc, geoCalc)
environmentalSvc := service.NewEnvironmentalImpactService(geoFeatureRepo, siteRepo, geospatialService, geoCalc)
facilityOptimizer := service.NewFacilityLocationOptimizer(geoFeatureRepo, siteRepo, geospatialService, spatialMatcher, environmentalSvc, geoTransportSvc)
// Create cache and websocket services
var cacheService service.CacheService
redisCache, err := service.NewRedisCacheService(cfg.RedisURL)
if err != nil {
log.Printf("Warning: Failed to initialize Redis cache, using in-memory cache: %v", err)
cacheService = service.NewMemoryCacheService()
} else {
log.Println("Successfully initialized Redis cache service")
cacheService = redisCache
}
websocketService := service.NewWebSocketService()
// Create analysis services for matching
riskSvc := risk.NewService()
transportSvc := transport.NewService()
regulatorySvc := regulatory.NewService()
// Create matching service
matchingSvc := matching.NewService(
matchRepo, negotiationHistoryRepo, resourceFlowRepo, siteRepo, orgRepo,
productRepo, serviceRepo, communityListingRepo,
riskSvc, transportSvc, regulatorySvc, eventBus,
)
// Create financial calculator and economic service
financialConfig := financial.DefaultConfig()
financialCalculator := financial.NewCalculator(financialConfig)
economicService := service.NewEconomicService(financialCalculator)
aiService := service.NewAIService()
// Create additional services needed by handlers
userService := service.NewUserService(userRepo)
// Create trust service
trustRepo := repository.NewTrustMetricsRepository(db)
verifiedRepo := repository.NewVerifiedDataRepository(db)
historicalRepo := repository.NewHistoricalSuccessRepository(db)
trustService := service.NewTrustService(trustRepo, verifiedRepo, historicalRepo)
// Create i18n service - simplified initialization
locRepo := repository.NewLocalizationRepository(db)
i18nService := service.NewI18nService(nil, locRepo, nil, nil) // Simplified - can be enhanced later
// Create subscription service
subscriptionRepo := repository.NewSubscriptionRepository(db)
// UsageTrackingRepository can be nil for now - create if needed later
subscriptionService := service.NewSubscriptionService(subscriptionRepo, nil, userRepo)
// Create verification service
verificationService := service.NewVerificationService(trustService, orgService)
// Create admin service
adminService := service.NewAdminService(orgService, userService, verificationService, i18nService)
// Initialize public transport service (reads data/bugulma_public_transport_enriched.json if available)
publicTransportService, err := service.NewPublicTransportService("data")
if err != nil {
log.Printf("Warning: failed to initialize public transport service: %v", err)
publicTransportService = nil
}
return orgService, siteService, resourceFlowService, geospatialService, sharedAssetService, analyticsService, authService, matchingSvc, cacheService, websocketService, aiService, proposalService, spatialMatcher, environmentalSvc, facilityOptimizer, economicService, userService, adminService, i18nService, subscriptionService, verificationService, productRepo, serviceRepo, communityListingRepo, publicTransportService
}
// initializeHandlers creates all HTTP handler instances
func initializeHandlers(
cfg *config.Config,
orgService *service.OrganizationService,
siteService *service.SiteService,
resourceFlowService *service.ResourceFlowService,
matchingSvc *matching.Service,
authService *service.AuthService,
sharedAssetService *service.SharedAssetService,
proposalService *service.ProposalService,
geospatialService *service.GeospatialService,
analyticsService *service.AnalyticsService,
aiService *service.AIService,
cacheService service.CacheService,
db *gorm.DB,
userService *service.UserService,
adminService *service.AdminService,
i18nService *service.I18nService,
subscriptionService *service.SubscriptionService,
verificationService *service.VerificationService,
publicTransportService *service.PublicTransportService,
scheduleService *service.ScheduleService,
settingsService *service.SettingsService,
) (*handler.OrganizationHandler, *handler.SiteHandler, *handler.ResourceFlowHandler, *handler.ProposalHandler, *handler.MatchingHandler, *handler.AuthHandler, *handler.SharedAssetHandler, *handler.GeospatialHandler, *handler.AnalyticsHandler, *handler.AIHandler, *handler.HeritageHandler, *handler.UserHandler, *handler.OrganizationAdminHandler, *handler.I18nHandler, *handler.ContentHandler, *handler.AdminHandler, *handler.SettingsAdminHandler, *handler.SubscriptionHandler, *handler.DiscoveryHandler, *handler.PublicTransportHandler) {
imageService := service.NewImageService(cfg)
orgHandler := handler.NewOrganizationHandler(orgService, imageService, resourceFlowService, matchingSvc, proposalService)
siteHandler := handler.NewSiteHandler(siteService)
resourceFlowHandler := handler.NewResourceFlowHandler(resourceFlowService)
proposalHandler := handler.NewProposalHandler(proposalService)
matchingHandler := handler.NewMatchingHandler(matchingSvc, nil)
authHandler := handler.NewAuthHandler(authService)
sharedAssetHandler := handler.NewSharedAssetHandler(sharedAssetService)
geospatialHandler := handler.NewGeospatialHandler(geospatialService)
analyticsHandler := handler.NewAnalyticsHandler(analyticsService)
aiHandler := handler.NewAIHandler(aiService)
heritageHandler := handler.NewHeritageHandler(repository.NewHeritageRepository(db))
// Additional handlers
userHandler := handler.NewUserHandler(userService)
orgAdminHandler := handler.NewOrganizationAdminHandler(orgHandler, verificationService, adminService)
i18nHandler := handler.NewI18nHandler(i18nService)
contentHandler := handler.NewContentHandler(service.NewContentService(
repository.NewStaticPageRepository(db),
repository.NewAnnouncementRepository(db),
repository.NewMediaAssetRepository(db),
))
adminHandler := handler.NewAdminHandler(adminService, analyticsService)
settingsHandler := handler.NewSettingsAdminHandler(settingsService)
subscriptionHandler := handler.NewSubscriptionHandler(subscriptionService)
discoveryHandler := handler.NewDiscoveryHandler(matchingSvc)
publicTransportHandler := handler.NewPublicTransportHandler(publicTransportService, scheduleService)
return orgHandler, siteHandler, resourceFlowHandler, proposalHandler, matchingHandler, authHandler, sharedAssetHandler, geospatialHandler, analyticsHandler, aiHandler, heritageHandler, userHandler, orgAdminHandler, i18nHandler, contentHandler, adminHandler, settingsHandler, subscriptionHandler, discoveryHandler, publicTransportHandler
}
// setupRouter configures the Gin router with middleware
func setupRouter(authService *service.AuthService, settingsSvc *service.SettingsService) *gin.Engine {
router := gin.Default()
// Serve static files
router.Static("/static", "./static")
// CORS middleware
router.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// Context middleware for extracting user/org info from JWT or headers
router.Use(middleware.ContextMiddleware(authService))
// Maintenance middleware: block non-whitelisted requests when maintenance mode is enabled
router.Use(middleware.MaintenanceMiddleware(settingsSvc, []string{"/api/v1/admin", "/health", "/metrics"}))
return router
}
// setupRoutes configures all API routes using the organized routes package
func setupRoutes(
router *gin.Engine,
orgHandler *handler.OrganizationHandler,
siteHandler *handler.SiteHandler,
resourceHandler *handler.ResourceFlowHandler,
proposalHandler *handler.ProposalHandler,
matchingHandler *handler.MatchingHandler,
authHandler *handler.AuthHandler,
sharedAssetHandler *handler.SharedAssetHandler,
geospatialHandler *handler.GeospatialHandler,
analyticsHandler *handler.AnalyticsHandler,
aiHandler *handler.AIHandler,
heritageHandler *handler.HeritageHandler,
graphHandler *handler.GraphHandler,
graphTraversalHandler *handler.GraphTraversalHandler,
websocketService *service.WebSocketService,
orgService *service.OrganizationService,
siteService *service.SiteService,
geospatialService *service.GeospatialService,
graphSyncService *service.GraphSyncService,
userHandler *handler.UserHandler,
orgAdminHandler *handler.OrganizationAdminHandler,
i18nHandler *handler.I18nHandler,
contentHandler *handler.ContentHandler,
adminHandler *handler.AdminHandler,
settingsHandler *handler.SettingsAdminHandler,
subscriptionHandler *handler.SubscriptionHandler,
authService *service.AuthService,
discoveryHandler *handler.DiscoveryHandler,
publicTransportHandler *handler.PublicTransportHandler,
) {
routes.RegisterAllRoutes(
router,
orgHandler,
siteHandler,
resourceHandler,
proposalHandler,
matchingHandler,
authHandler,
sharedAssetHandler,
geospatialHandler,
analyticsHandler,
aiHandler,
heritageHandler,
graphHandler,
graphTraversalHandler,
websocketService,
userHandler,
orgAdminHandler,
i18nHandler,
contentHandler,
adminHandler,
settingsHandler,
subscriptionHandler,
authService,
discoveryHandler,
publicTransportHandler,
)
}