169 lines
6.5 KiB
Go
169 lines
6.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
|
|
"dealroom/internal/fireworks"
|
|
"dealroom/internal/model"
|
|
"dealroom/internal/worker"
|
|
)
|
|
|
|
type Handler struct {
|
|
db *sql.DB
|
|
config *Config
|
|
extractor *worker.Extractor
|
|
fw *fireworks.Client
|
|
}
|
|
|
|
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 {
|
|
fw := fireworks.NewClient()
|
|
ext := worker.NewExtractor(db, fw)
|
|
ext.Start()
|
|
return &Handler{
|
|
db: db,
|
|
config: config,
|
|
extractor: ext,
|
|
fw: fw,
|
|
}
|
|
}
|
|
|
|
// enqueueExtraction submits a job to the background extraction worker.
|
|
func (h *Handler) enqueueExtraction(responseID, filePath, dealID string) {
|
|
if h.extractor != nil {
|
|
h.extractor.Enqueue(worker.ExtractionJob{
|
|
ResponseID: responseID,
|
|
FilePath: filePath,
|
|
DealID: dealID,
|
|
})
|
|
}
|
|
}
|
|
|
|
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("/signup", h.handleSignupPage)
|
|
mux.HandleFunc("/auth/login", h.handleLogin)
|
|
mux.HandleFunc("/auth/signup", h.handleSignup)
|
|
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))
|
|
mux.HandleFunc("/subscription", h.requireAuth(h.handleSubscription))
|
|
|
|
// 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))
|
|
|
|
// Team & Invites
|
|
mux.HandleFunc("/team", h.requireAuth(h.handleTeam))
|
|
mux.HandleFunc("/invites/create", h.requireAuth(h.handleInviteCreate))
|
|
mux.HandleFunc("/invites/accept", h.handleInviteAcceptPage)
|
|
mux.HandleFunc("/invites/accept-submit", h.handleInviteAccept)
|
|
|
|
// Deal creation & folder management
|
|
mux.HandleFunc("/deals/create", h.requireAuth(h.handleCreateDeal))
|
|
mux.HandleFunc("/deals/folders/create", h.requireAuth(h.handleFolderCreate))
|
|
mux.HandleFunc("/deals/folders/rename", h.requireAuth(h.handleFolderRename))
|
|
mux.HandleFunc("/deals/folders/delete", h.requireAuth(h.handleFolderDelete))
|
|
mux.HandleFunc("/deals/folders/reorder", h.requireAuth(h.handleFolderReorder))
|
|
mux.HandleFunc("/deals/folders/reparent", h.requireAuth(h.handleFolderReparent))
|
|
|
|
// File management
|
|
mux.HandleFunc("/deals/files/upload", h.requireAuth(h.handleFileUpload))
|
|
mux.HandleFunc("/deals/files/delete", h.requireAuth(h.handleFileDelete))
|
|
mux.HandleFunc("/deals/files/download/", h.requireAuth(h.handleFileDownload))
|
|
mux.HandleFunc("/deals/files/comments/", h.requireAuth(h.handleFileComments))
|
|
mux.HandleFunc("/deals/search/", h.requireAuth(h.handleDealSearch))
|
|
mux.HandleFunc("/analytics/buyers", h.requireAuth(h.handleAnalyticsBuyers))
|
|
|
|
// Deal update
|
|
mux.HandleFunc("/deals/update", h.requireAuth(h.handleDealUpdate))
|
|
|
|
// API endpoints
|
|
mux.HandleFunc("/api/contacts/search", h.requireAuth(h.handleContactSearch))
|
|
mux.HandleFunc("/api/industries", h.requireAuth(h.handleIndustryList))
|
|
|
|
// Request list upload
|
|
mux.HandleFunc("/deals/requests/upload", h.requireAuth(h.handleRequestListUpload))
|
|
|
|
// HTMX partials
|
|
mux.HandleFunc("/htmx/request-comment", h.requireAuth(h.handleUpdateComment))
|
|
|
|
// Responses & AI matching
|
|
mux.HandleFunc("/deals/responses/statement", h.requireAuth(h.handleCreateStatement))
|
|
mux.HandleFunc("/deals/responses/confirm", h.requireAuth(h.handleConfirmLink))
|
|
mux.HandleFunc("/deals/responses/reject", h.requireAuth(h.handleRejectLink))
|
|
mux.HandleFunc("/deals/responses/pending/", h.requireAuth(h.handlePendingLinks))
|
|
mux.HandleFunc("/deals/assignment-rules/save", h.requireAuth(h.handleSaveAssignmentRules))
|
|
mux.HandleFunc("/deals/assignment-rules/", h.requireAuth(h.handleGetAssignmentRules))
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
}
|