package main import ( "context" "gralias/config" "gralias/crons" "gralias/handlers" "gralias/repos" "log/slog" "net/http" "os" "os/signal" "path/filepath" "strings" "syscall" "time" ) var cfg *config.Config func init() { cfg = config.LoadConfigOrDefault("") } // GzipFileServer serves pre-compressed .gz files if available func GzipFileServer(root http.FileSystem) http.Handler { fs := http.FileServer(root) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Check if client accepts gzip if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { // Check for .gz version of the file gzPath := r.URL.Path + ".gz" if file, err := root.Open(gzPath); err == nil { file.Close() // Set headers for gzip w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Type", getContentType(r.URL.Path)) r.URL.Path = gzPath } } fs.ServeHTTP(w, r) }) } // Helper to set correct Content-Type func getContentType(path string) string { switch filepath.Ext(path) { case ".css": return "text/css" case ".js": return "application/javascript" default: return "" // http.FileServer will detect it } } func ListenToRequests(port string) *http.Server { mux := http.NewServeMux() server := &http.Server{ Handler: handlers.LogRequests(handlers.GetSession(mux)), Addr: ":" + port, // ReadTimeout: time.Second * 5, // does this timeout conflict with sse connection? WriteTimeout: 0, // sse streaming } // fs := http.FileServer(http.Dir("assets/")) fs := http.Dir("assets/") mux.Handle("GET /assets/", http.StripPrefix("/assets/", GzipFileServer(fs))) // mux.HandleFunc("GET /ping", handlers.HandlePing) mux.HandleFunc("GET /", handlers.HandleHome) mux.HandleFunc("POST /login", handlers.HandleFrontLogin) mux.HandleFunc("POST /join-team", handlers.HandleJoinTeam) mux.HandleFunc("GET /end-turn", handlers.HandleEndTurn) mux.HandleFunc("POST /room-create", handlers.HandleCreateRoom) mux.HandleFunc("GET /start-game", handlers.HandleStartGame) mux.HandleFunc("GET /room-join", handlers.HandleJoinRoom) mux.HandleFunc("POST /give-clue", handlers.HandleGiveClue) mux.HandleFunc("GET /room/exit", handlers.HandleExit) //elements mux.HandleFunc("GET /actionhistory", handlers.HandleActionHistory) mux.HandleFunc("GET /room/createform", handlers.HandleShowCreateForm) mux.HandleFunc("GET /room/hideform", handlers.HandleHideCreateForm) mux.HandleFunc("GET /word/show-color", handlers.HandleShowColor) mux.HandleFunc("POST /check/name", handlers.HandleNameCheck) mux.HandleFunc("GET /add-bot", handlers.HandleAddBot) mux.HandleFunc("GET /remove-bot", handlers.HandleRemoveBot) mux.HandleFunc("GET /mark-card", handlers.HandleMarkCard) // special mux.HandleFunc("GET /renotify-bot", handlers.HandleRenotifyBot) // sse mux.Handle("GET /sub/sse", handlers.Notifier) slog.Info("Listening", "addr", port) return server } func main() { // Setup graceful shutdown stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) // repo := repos.NewRepoProvider(cfg.DBPath) repo := repos.RP defer repo.Close() cm := crons.NewCronManager(repo, slog.Default()) cm.Start() server := ListenToRequests(cfg.ServerConfig.Port) go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { panic(err) } }() <-stop slog.Info("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { slog.Error("server shutdown failed", "error", err) } }