package main import ( "context" "doudizhu-server/internal/captcha" "doudizhu-server/internal/db" "doudizhu-server/internal/game" "doudizhu-server/internal/handlers" "doudizhu-server/internal/redis" "doudizhu-server/internal/ws" "log" "net/http" "os" "os/signal" "strconv" "syscall" "time" ) func main() { redisAddr := getEnv("REDIS_ADDR", "localhost:6379") redisClient, err := redis.NewClient(redis.Config{ Addr: redisAddr, Password: "", DB: 0, }) if err != nil { log.Fatalf("Failed to connect to Redis: %v", err) } defer redisClient.Close() log.Println("Connected to Redis:", redisAddr) dbHost := getEnv("DB_HOST", "localhost") dbPort, _ := strconv.Atoi(getEnv("DB_PORT", "5432")) dbUser := getEnv("DB_USER", "postgres") dbPassword := getEnv("DB_PASSWORD", "postgres") dbName := getEnv("DB_NAME", "doudizhu") database, err := db.New(db.Config{ Host: dbHost, Port: dbPort, User: dbUser, Password: dbPassword, Database: dbName, }) if err != nil { log.Fatalf("Failed to connect to PostgreSQL: %v", err) } defer database.Close() log.Println("Connected to PostgreSQL:", dbHost) gameMgr := game.NewGameManager(redisClient) hub := ws.NewHub(gameMgr) go hub.Run() captchaMgr := captcha.NewManager(redisClient) h := handlers.NewHandler(gameMgr, hub, redisClient, database, captchaMgr) mux := http.NewServeMux() mux.HandleFunc("/api/auth/captcha", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { h.GetCaptcha(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/auth/register", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.Register(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/auth/login", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.Login(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/auth/validate", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { h.ValidateToken(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/auth/logout", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.Logout(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/rooms", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.CreateRoom(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/rooms/current", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { h.GetCurrentRoom(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/rooms/leave", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.LeaveRoom(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/rooms/", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { h.JoinRoom(w, r) } else { http.NotFound(w, r) } }) mux.HandleFunc("/api/ws", h.WebSocket) srv := &http.Server{ Addr: ":8080", Handler: mux, } go func() { log.Println("App server starting on :8080") if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal(err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("Server shutdown error: %v", err) } hub.Stop() gameMgr.Stop() log.Println("Server stopped") } func getEnv(key, defaultVal string) string { if val := os.Getenv(key); val != "" { return val } return defaultVal }