package handler import ( "database/sql" "net/http" "dealroom/internal/model" ) type Handler struct { db *sql.DB config *Config } type Config struct { BaseURL string SessionKey string K25APIURL string K25APIKey string SMTPHost string SMTPUser string SMTPPass string } func New(db *sql.DB, _ interface{}, config *Config) *Handler { return &Handler{ db: db, config: config, } } func (h *Handler) RegisterRoutes(mux *http.ServeMux) { // Static files mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) // Auth mux.HandleFunc("/login", h.handleLoginPage) mux.HandleFunc("/auth/login", h.handleLogin) mux.HandleFunc("/auth/logout", h.handleLogout) mux.HandleFunc("/auth/view-toggle", h.requireAuth(h.handleViewToggle)) // Pages (auth required) mux.HandleFunc("/", h.requireAuth(h.handleDashboard)) mux.HandleFunc("/deals", h.requireAuth(h.handleDealRooms)) mux.HandleFunc("/deals/", h.requireAuth(h.handleDealRoom)) mux.HandleFunc("/requests", h.requireAuth(h.handleRequestList)) mux.HandleFunc("/contacts", h.requireAuth(h.handleContacts)) mux.HandleFunc("/audit", h.requireAuth(h.handleAuditLog)) mux.HandleFunc("/analytics", h.requireAuth(h.handleAnalytics)) // Admin CRUD mux.HandleFunc("/admin", h.requireAdmin(h.handleAdmin)) mux.HandleFunc("/admin/contacts", h.requireAdmin(h.handleAdminContacts)) mux.HandleFunc("/admin/contacts/edit", h.requireAdmin(h.handleAdminContactForm)) mux.HandleFunc("/admin/contacts/save", h.requireAdmin(h.handleAdminContactSave)) mux.HandleFunc("/admin/contacts/delete", h.requireAdmin(h.handleAdminContactDelete)) mux.HandleFunc("/admin/deals", h.requireAdmin(h.handleAdminDeals)) mux.HandleFunc("/admin/deals/edit", h.requireAdmin(h.handleAdminDealForm)) mux.HandleFunc("/admin/deals/save", h.requireAdmin(h.handleAdminDealSave)) mux.HandleFunc("/admin/deals/delete", h.requireAdmin(h.handleAdminDealDelete)) mux.HandleFunc("/admin/users", h.requireAdmin(h.handleAdminUsers)) mux.HandleFunc("/admin/users/edit", h.requireAdmin(h.handleAdminUserForm)) mux.HandleFunc("/admin/users/save", h.requireAdmin(h.handleAdminUserSave)) mux.HandleFunc("/admin/users/delete", h.requireAdmin(h.handleAdminUserDelete)) mux.HandleFunc("/admin/organizations", h.requireAdmin(h.handleAdminOrgs)) mux.HandleFunc("/admin/organizations/edit", h.requireAdmin(h.handleAdminOrgForm)) mux.HandleFunc("/admin/organizations/save", h.requireAdmin(h.handleAdminOrgSave)) mux.HandleFunc("/admin/organizations/delete", h.requireAdmin(h.handleAdminOrgDelete)) // Deal creation mux.HandleFunc("/deals/create", h.requireAuth(h.handleCreateDeal)) // HTMX partials mux.HandleFunc("/htmx/request-comment", h.requireAuth(h.handleUpdateComment)) } // Middleware func (h *Handler) requireAuth(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } var profile model.Profile err = h.db.QueryRow(` SELECT p.id, p.email, p.full_name, p.avatar_url, p.organization_id, p.role FROM sessions s JOIN profiles p ON s.user_id = p.id WHERE s.token = ? AND s.expires_at > datetime('now') `, cookie.Value).Scan(&profile.ID, &profile.Email, &profile.FullName, &profile.AvatarURL, &profile.OrganizationID, &profile.Role) if err != nil { http.SetCookie(w, &http.Cookie{Name: "session", Value: "", MaxAge: -1, Path: "/"}) http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Check view-as-buyer toggle (only applies to sellers) if profile.Role == "owner" || profile.Role == "admin" { if c, err := r.Cookie("view_as_buyer"); err == nil && c.Value == "1" { profile.ViewAsBuyer = true } } ctx := setProfile(r.Context(), &profile) next.ServeHTTP(w, r.WithContext(ctx)) } }