package main import ( "crypto/rand" "encoding/hex" "fmt" "log" "net/http" "chat/src/auth" "chat/src/config" "chat/src/db" "chat/src/handlers" "chat/src/metrics" "chat/src/ws" ) func generateShortID() string { b := make([]byte, 6) rand.Read(b) return hex.EncodeToString(b) } func main() { instanceID := fmt.Sprintf("chat-%s", generateShortID()) rateLimitMax := config.RateLimitMax() rateLimitWindow := config.RateLimitWindow() log.Printf("Rate limit: %d messages per %v", rateLimitMax, rateLimitWindow) // Load persisted runtime configuration (Matomo + HLS settings) if err := config.LoadRuntimeConfig(); err != nil { log.Printf("Warning: failed to load runtime config: %v", err) } // Initialize Redis redisWrapper := db.InitRedis(config.RedisAddress()) // Initialize DB dbClient, dbDriver, err := db.InitDB(config.DatabaseURL()) if err != nil { log.Fatalf("Failed to initialize database: %v", err) } // Initialize session store sessionStore := auth.NewSessionStore() userSessions := auth.NewUserSessionStore() // Initialize WebSocket hub hub := ws.NewHub(dbClient.DB, redisWrapper, instanceID, rateLimitMax, rateLimitWindow, config.AdminUsername()) // Start Redis pub/sub subscriber go hub.SubscribeRoomMessages() log.Printf("Instance ID: %s", instanceID) // Build dependencies deps := &handlers.Dependencies{ DB: dbClient.DB, Redis: redisWrapper, Hub: hub, SessionStore: sessionStore, UserSessions: userSessions, DBDriver: dbDriver, } // Auth middleware requireAdmin := auth.RequireAdmin(sessionStore, config.AdminSessionCookieName) // Register routes http.HandleFunc("/ws", hub.HandleConnection) http.HandleFunc("/api/auth/register", metrics.WrapRequestTiming(handlers.HandleUserRegister(deps))) http.HandleFunc("/api/auth/login", metrics.WrapRequestTiming(handlers.HandleUserLogin(deps))) http.HandleFunc("/api/auth/session", metrics.WrapRequestTiming(handlers.HandleUserSession(deps))) http.HandleFunc("/api/auth/logout", metrics.WrapRequestTiming(handlers.HandleUserLogout(deps))) http.HandleFunc("/api/dm/history", metrics.WrapRequestTiming(handlers.HandleUserDMHistory(deps))) http.HandleFunc("/api/dm/search", metrics.WrapRequestTiming(handlers.HandleUserDMSearch(deps))) http.HandleFunc("/api/admin/login", metrics.WrapRequestTiming(handlers.HandleAdminLogin(deps))) http.HandleFunc("/api/admin/logout", metrics.WrapRequestTiming(handlers.HandleAdminLogout(deps))) http.HandleFunc("/api/admin/session", metrics.WrapRequestTiming(handlers.HandleAdminSession(deps))) http.HandleFunc("/api/admin/overview", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminOverview(deps)))) http.HandleFunc("/api/admin/playlist", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminPlaylist(deps)))) http.HandleFunc("/api/admin/moderation/logs", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminModerationLogs(deps)))) http.HandleFunc("/api/admin/moderation/actions", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminModerationAction(deps)))) http.HandleFunc("/api/admin/analytics", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminAnalytics(deps)))) http.HandleFunc("/api/admin/system", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminSystemSummary(deps)))) http.HandleFunc("/api/admin/config", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminConfig(deps)))) http.HandleFunc("/api/admin/restart", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminRestart(deps)))) http.HandleFunc("/api/admin/users", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminUsers(deps)))) http.HandleFunc("/api/admin/schedule", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminSchedule(deps)))) http.HandleFunc("/api/admin/schedule/overrides", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminScheduleOverrides(deps)))) http.HandleFunc("/api/schedule", metrics.WrapRequestTiming(handlers.HandlePublicSchedule(deps))) http.HandleFunc("/api/matomo", metrics.WrapRequestTiming(handlers.HandlePublicMatomo())) http.HandleFunc("/api/admin/media", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminMedia(deps)))) http.HandleFunc("/api/admin/media/uploads", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminMediaUploads(deps)))) http.HandleFunc("/api/admin/media/uploads/", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminMediaUploads(deps)))) http.HandleFunc("/api/admin/media/", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminMediaByID(deps)))) http.HandleFunc("/api/admin/chat-log/bulk", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminChatLogBulk(deps)))) http.HandleFunc("/api/admin/chat-log/export", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminChatLogExport(deps)))) http.HandleFunc("/api/admin/chat-log/", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminChatLogByID(deps)))) http.HandleFunc("/api/admin/chat-log", metrics.WrapRequestTiming(requireAdmin(handlers.HandleAdminChatLog(deps)))) http.HandleFunc("/metrics", metrics.HandleMetrics(dbClient.DB, redisWrapper, hub, sessionStore, dbDriver, instanceID)) http.HandleFunc("/health", metrics.WrapRequestTiming(metrics.HandleHealth())) http.HandleFunc("/", metrics.WrapRequestTiming(metrics.HandleNotFound())) port := config.ChatPort() log.Printf("Chat service starting on :%s", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatalf("Server failed: %v", err) } }