package main import ( "html/template" "io/fs" "log" "net/http" "os" "chessstars/internal/auth" "chessstars/internal/courses" "chessstars/internal/game" "chessstars/internal/web" ) func env(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def } func main() { authDBPath := env("AUTH_DB_PATH", "/data/auth.db") coursesDir := env("COURSES_DIR", "/data/courses") addr := ":" + env("PORT", "8080") authStore, err := auth.Open(authDBPath) if err != nil { log.Fatalf("open auth db: %v", err) } defer authStore.Close() courseStore, err := courses.Open(coursesDir) if err != nil { log.Fatalf("open courses dir: %v", err) } defer courseStore.Close() startAuthCleaner(authStore) gameManager := game.NewManager() startGameCleaner(gameManager) authHandlers := auth.NewHandlers(authStore) courseHandlers := courses.NewHandlers(courseStore) gameHandlers := game.NewHandlers(gameManager, authStore) tmpl, err := template.ParseFS(web.TemplatesFS, "templates/*.html") if err != nil { log.Fatalf("parse templates: %v", err) } mux := http.NewServeMux() // API: auth mux.HandleFunc("POST /api/register", authHandlers.Register) mux.HandleFunc("POST /api/login", authHandlers.Login) mux.HandleFunc("POST /api/logout", authHandlers.Logout) mux.HandleFunc("GET /api/me", authHandlers.Me) mux.HandleFunc("PUT /api/me", authHandlers.UpdateMe) mux.HandleFunc("GET /api/users/{username}", authHandlers.PublicProfile) mux.HandleFunc("GET /api/users/{username}/export", authHandlers.ExportProfile) mux.HandleFunc("GET /api/leaderboard", authHandlers.Leaderboard) // API: courses mux.HandleFunc("POST /api/courses", courseHandlers.CreateCourse) mux.HandleFunc("GET /api/courses", courseHandlers.ListPublicCourses) mux.HandleFunc("GET /api/me/courses", courseHandlers.ListMyCourses) mux.HandleFunc("GET /api/courses/{id}", courseHandlers.GetCourse) mux.HandleFunc("POST /api/courses/{id}/chapters", courseHandlers.AddChapter) mux.HandleFunc("GET /api/courses/{id}/chapters/{chapterID}", courseHandlers.GetChapter) // API: games mux.HandleFunc("POST /api/games", gameHandlers.Create) mux.HandleFunc("GET /api/games/open", gameHandlers.ListOpen) mux.HandleFunc("GET /api/games/all", gameHandlers.ListAll) mux.HandleFunc("GET /api/me/games", gameHandlers.ListMine) mux.HandleFunc("GET /api/games/{id}", gameHandlers.Get) mux.HandleFunc("POST /api/games/{id}/join", gameHandlers.Join) mux.HandleFunc("POST /api/games/{id}/move", gameHandlers.Move) mux.HandleFunc("POST /api/games/{id}/legal", gameHandlers.Legal) mux.HandleFunc("GET /api/games/{id}/legal-moves", gameHandlers.LegalFrom) mux.HandleFunc("GET /api/games/{id}/moves", gameHandlers.Moves) mux.HandleFunc("GET /api/games/{id}/history/{index}", gameHandlers.HistoryAt) // Static assets + pages staticSub, err := fs.Sub(web.StaticFS, "static") if err != nil { log.Fatalf("static fs: %v", err) } mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticSub)))) pageHandler := func(name string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := tmpl.ExecuteTemplate(w, name, nil); err != nil { http.Error(w, "template error", http.StatusInternalServerError) } } } mux.HandleFunc("GET /", pageHandler("index.html")) mux.HandleFunc("GET /play", pageHandler("play.html")) mux.HandleFunc("GET /courses", pageHandler("courses.html")) mux.HandleFunc("GET /account", pageHandler("account.html")) mux.HandleFunc("GET /leaderboard", pageHandler("leaderboard.html")) handler := auth.Middleware(authStore)(mux) log.Printf("chessstars listening on %s", addr) if err := http.ListenAndServe(addr, handler); err != nil { log.Fatal(err) } }