package main import ( "context" "encoding/json" "html/template" "log" "net/http" "path/filepath" "time" "sprintpadawan/lib" ) type PingResponse struct { Status string `json:"status"` Message string `json:"message"` } var templates *template.Template type contextKey string const userKey contextKey = "user" func main() { lib.InitDB() var err error templates, err = template.ParseGlob(filepath.Join("templates", "*.html")) if err != nil { log.Fatalf("failed to parse templates: %v", err) } mux := http.NewServeMux() // serve static assets mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) // public routes mux.HandleFunc("/login", handleLogin) mux.HandleFunc("/register", handleRegister) mux.HandleFunc("/logout", handleLogout) // private routes mux.HandleFunc("/", requireAuth(handleIndex)) mux.HandleFunc("/api/ping", requireAuth(handlePing)) mux.HandleFunc("/api/ping-partial", requireAuth(handlePingPartial)) addr := ":8080" log.Printf("Starting SprintPadawan server on http://localhost%s", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatalf("server error: %v", err) } } func requireAuth(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_token") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } user, err := lib.GetUserFromSession(cookie.Value) if err != nil || user == nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } ctx := context.WithValue(r.Context(), userKey, user) next.ServeHTTP(w, r.WithContext(ctx)) } } func handleLogin(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { templates.ExecuteTemplate(w, "login.html", nil) return } username := r.FormValue("username") password := r.FormValue("password") user, err := lib.GetUserByUsername(username) if err != nil || !lib.CheckPasswordHash(password, user.PasswordHash) { templates.ExecuteTemplate(w, "login.html", map[string]string{"Error": "Invalid credentials"}) return } sessionID, _ := lib.CreateSession(user.ID) http.SetCookie(w, &http.Cookie{ Name: "session_token", Value: sessionID, Expires: time.Now().Add(24 * time.Hour), HttpOnly: true, Path: "/", }) http.Redirect(w, r, "/", http.StatusSeeOther) } func handleRegister(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { templates.ExecuteTemplate(w, "register.html", nil) return } username := r.FormValue("username") password := r.FormValue("password") confirm := r.FormValue("confirm_password") if password != confirm { templates.ExecuteTemplate(w, "register.html", map[string]string{"Error": "Passwords do not match"}) return } err := lib.CreateUser(username, password) if err != nil { templates.ExecuteTemplate(w, "register.html", map[string]string{"Error": "Username taken"}) return } http.Redirect(w, r, "/login", http.StatusSeeOther) } func handleLogout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_token") if err == nil { lib.DeleteSession(cookie.Value) } http.SetCookie(w, &http.Cookie{ Name: "session_token", Value: "", Expires: time.Now().Add(-1 * time.Hour), Path: "/", }) http.Redirect(w, r, "/login", http.StatusSeeOther) } func handleIndex(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } user := r.Context().Value(userKey).(*lib.User) w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := templates.ExecuteTemplate(w, "index.html", user); err != nil { log.Printf("template error: %v", err) http.Error(w, "internal server error", http.StatusInternalServerError) } } func handlePing(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") resp := PingResponse{Status: "ok", Message: "pong"} if err := json.NewEncoder(w).Encode(resp); err != nil { log.Printf("json encode error: %v", err) } } func handlePingPartial(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := templates.ExecuteTemplate(w, "ping_result.html", PingResponse{Status: "ok", Message: "pong"}); err != nil { log.Printf("template error: %v", err) http.Error(w, "internal server error", http.StatusInternalServerError) } }