package handlers import ( "context" "go-backend/internal/auth" "go-backend/internal/database/sqlc" "go-backend/internal/services" "html/template" "log" "net/http" ) // UserService and AuthService are defined here, on the consumer side, so // handlers depend only on the methods they actually use and tests can swap // in fakes without touching the services package. type UserService interface { FindByID(ctx context.Context, id string) (*sqlc.UserAuth, error) FindByUsername(ctx context.Context, username string) (*sqlc.UserAuth, error) CreateUser(ctx context.Context, userInput sqlc.InsertUserParams) (*sqlc.UserAuth, error) PatchUser(ctx context.Context, id string, input services.PatchUserInput) (*sqlc.UserAuth, error) } type AuthService interface { CreateSession(ctx context.Context, userID string) (*services.SessionWithToken, error) ValidateSessionToken(ctx context.Context, token string) (*sqlc.UserSession, bool, error) DeleteSession(ctx context.Context, token string) error } type Handler struct { users UserService auth AuthService templateCache map[string]*template.Template } // PageData wraps every page render so base.tmpl always has a stable place // to check whether a user is logged in (.User), regardless of what // page-specific data (.Data) the page template itself needs. type PageData struct { User *sqlc.UserAuth Data any } func NewHandler(users UserService, auth AuthService, templateCache map[string]*template.Template) *Handler { return &Handler{ users: users, auth: auth, templateCache: templateCache, } } // render looks up the pre-parsed template for page (e.g. "index.tmpl") and // executes it. It writes an error response itself on failure. func (h *Handler) render(w http.ResponseWriter, page string, data PageData) { ts, ok := h.templateCache[page] if !ok { log.Printf("template %s not found in cache", page) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if err := ts.ExecuteTemplate(w, "base", data); err != nil { log.Print(err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } func (h *Handler) HomePageHandler(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } session, bump, err := h.auth.ValidateSessionToken(r.Context(), cookie.Value) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if session == nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } if bump { http.SetCookie(w, auth.NewSessionCookie(cookie.Value, false)) } user, err := h.users.FindByID(r.Context(), session.UserID) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.render(w, "index.tmpl", PageData{User: user, Data: user.Username}) } func (h *Handler) LoginPageHandler(w http.ResponseWriter, r *http.Request) { cookie, _ := r.Cookie("session") if cookie != nil { session, bump, err := h.auth.ValidateSessionToken(r.Context(), cookie.Value) if err != nil { log.Print(err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if bump { http.SetCookie(w, auth.NewSessionCookie(cookie.Value, false)) } if session != nil { http.Redirect(w, r, "/", http.StatusSeeOther) return } } h.render(w, "login.tmpl", PageData{}) } func (h *Handler) SignUpPageHandler(w http.ResponseWriter, r *http.Request) { cookie, _ := r.Cookie("session") if cookie != nil { session, bump, err := h.auth.ValidateSessionToken(r.Context(), cookie.Value) if err != nil { log.Print(err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if bump { http.SetCookie(w, auth.NewSessionCookie(cookie.Value, false)) } if session != nil { http.Redirect(w, r, "/", http.StatusSeeOther) return } } h.render(w, "signup.tmpl", PageData{}) } func (h *Handler) SettingsPageHandler(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } session, bump, err := h.auth.ValidateSessionToken(r.Context(), cookie.Value) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if session == nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } if bump { http.SetCookie(w, auth.NewSessionCookie(cookie.Value, false)) } user, err := h.users.FindByID(r.Context(), session.UserID) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.render(w, "settings.tmpl", PageData{User: user, Data: user}) }