nsfwapp/backend/routes.go
2026-03-14 18:00:28 +01:00

197 lines
6.3 KiB
Go

// backend\routes.go
package main
import (
"fmt"
"net/http"
"net/url"
"strings"
)
// routes.go (package main)
func registerRoutes(mux *http.ServeMux, auth *AuthManager) *ModelStore {
// --------------------------
// 1) Public Auth Endpoints
// --------------------------
mux.HandleFunc("/api/auth/login", authLoginHandler(auth))
mux.HandleFunc("/api/auth/logout", authLogoutHandler(auth))
mux.HandleFunc("/api/auth/me", authMeHandler(auth))
// 2FA (Authenticator/TOTP)
mux.HandleFunc("/api/auth/2fa/setup", auth2FASetupHandler(auth))
mux.HandleFunc("/api/auth/2fa/enable", auth2FAEnableHandler(auth))
// mux.HandleFunc("/api/auth/2fa/disable", auth2FADisableHandler(auth))
// --------------------------
// 2) Protected API Mux
// --------------------------
api := http.NewServeMux()
api.HandleFunc("/api/cookies", cookiesHandler)
api.HandleFunc("/api/events/stream", eventsStream)
api.HandleFunc("/api/perf/stream", perfStreamHandler)
api.HandleFunc("/api/status/disk", diskStatusHandler)
api.HandleFunc("/api/autostart/state", autostartStateHandler)
api.HandleFunc("/api/autostart/state/stream", autostartStateStreamHandler)
api.HandleFunc("/api/autostart/pause", autostartPauseQuickHandler)
api.HandleFunc("/api/autostart/resume", autostartResumeHandler)
api.HandleFunc("/api/settings", recordSettingsHandler)
api.HandleFunc("/api/settings/browse", settingsBrowse)
api.HandleFunc("/api/settings/cleanup", settingsCleanupHandler)
api.HandleFunc("/api/record", startRecordingFromRequest)
api.HandleFunc("/api/record/status", recordStatus)
api.HandleFunc("/api/record/stop", recordStop)
api.HandleFunc("/api/record/postwork/remove", recordRemoveQueuedPostwork)
api.HandleFunc("/api/preview", recordPreview)
api.HandleFunc("/api/preview/live", recordPreviewLive)
api.HandleFunc("/api/preview-scrubber/", recordPreviewScrubberFrame)
api.HandleFunc("/api/preview-sprite/", recordPreviewSprite)
api.HandleFunc("/api/record/list", recordList)
api.HandleFunc("/api/record/done/meta", recordDoneMeta)
api.HandleFunc("/api/record/video", recordVideo)
api.HandleFunc("/api/record/split", recordSplitVideo)
api.HandleFunc("/api/record/analyze", recordAnalyzeVideo)
api.HandleFunc("/api/record/done", recordDoneList)
api.HandleFunc("/api/record/delete", recordDeleteVideo)
api.HandleFunc("/api/record/toggle-hot", recordToggleHot)
api.HandleFunc("/api/record/keep", recordKeepVideo)
api.HandleFunc("/api/record/unkeep", recordUnkeepVideo)
api.HandleFunc("/api/record/restore", recordRestoreVideo)
api.HandleFunc("/api/record/prepare-split", recordPrepareSplit)
api.HandleFunc("/api/chaturbate/online", chaturbateOnlineHandler)
api.HandleFunc("/api/chaturbate/biocontext", chaturbateBioContextHandler)
api.HandleFunc("/api/generated/teaser", generatedTeaser)
api.HandleFunc("/api/generated/cover", generatedCover)
api.HandleFunc("/api/generated/coverinfo/list", generatedCoverInfoList)
// Tasks
api.HandleFunc("/api/tasks/status", tasksStatusHandler)
api.HandleFunc("/api/tasks/generate-assets", tasksGenerateAssets)
// --------------------------
// 3) ModelStore (Postgres)
// DSN kommt aus Settings: databaseUrl + gespeichertes Passwort
// --------------------------
dsn, err := buildPostgresDSNFromSettings()
if err != nil {
fmt.Println("⚠️ models DSN:", err)
}
fmt.Println("📦 Models DB (Postgres):", sanitizeDSNForLog(dsn))
store := NewModelStore(dsn)
if err := store.Load(); err != nil {
fmt.Println("⚠️ models load:", err)
}
setCoverModelStore(store)
RegisterModelAPI(api, store)
setChaturbateOnlineModelStore(store)
// --------------------------
// 4) Mount Protected API
// --------------------------
// /api/auth/* ist schon public am root mux und gewinnt als längeres Pattern.
mux.Handle("/api/", requireAuth(auth, api, false))
// --------------------------
// 5) Mount Protected SPA (/)
// --------------------------
frontend, ok := makeFrontendHandler()
if ok && frontend != nil {
// allowPaths: login + assets müssen öffentlich sein, sonst Redirect-Loop
mux.Handle("/", requireAuth(auth, frontend, true,
"/login",
"/assets/",
"/favicon.ico",
"/manifest.webmanifest",
"/robots.txt",
"/service-worker.js",
))
}
return store
}
// buildPostgresDSNFromSettings baut eine DSN aus den Recorder-Settings:
// - databaseUrl kann bereits "postgres://user:pass@host/db" sein
// - oder ohne Passwort, dann wird das verschlüsselt gespeicherte Passwort eingesetzt
func buildPostgresDSNFromSettings() (string, error) {
// Settings sind bereits durch loadSettings() in main() geladen.
s := getSettings()
dbURL := strings.TrimSpace(s.DatabaseURL)
if dbURL == "" {
return "", fmt.Errorf("databaseUrl ist leer")
}
// Wenn databaseUrl ein Passwort enthält: verwenden,
// aber gleichzeitig safe sein, falls da Altbestand drin ist.
u, err := url.Parse(dbURL)
if err != nil {
return "", fmt.Errorf("databaseUrl ungültig: %w", err)
}
// 1) Wenn URL bereits Passwort enthält -> nur verwenden, wenn es NICHT der Placeholder ist
if u.User != nil {
if pw, hasPw := u.User.Password(); hasPw {
pw = strings.TrimSpace(pw)
if pw != "" && pw != "****" {
return u.String(), nil
}
// sonst: Placeholder -> ignorieren und unten aus EncryptedDBPassword einsetzen
}
}
// 2) Passwort fehlt -> aus EncryptedDBPassword holen
enc := strings.TrimSpace(s.EncryptedDBPassword)
if enc == "" {
// kein Passwort gespeichert -> URL ohne Passwort ist ok (z.B. trust/peer auth)
return u.String(), nil
}
plainPw, err := decryptSettingString(enc)
if err != nil {
return "", fmt.Errorf("db password decrypt failed: %w", err)
}
plainPw = strings.TrimSpace(plainPw)
if plainPw == "" {
return u.String(), nil
}
// 3) Username muss in databaseUrl vorhanden sein, sonst kann man kein Passwort einsetzen
user := ""
if u.User != nil {
user = u.User.Username()
}
if strings.TrimSpace(user) == "" {
return "", fmt.Errorf("databaseUrl enthält keinen Username, kann Passwort nicht einsetzen")
}
u.User = url.UserPassword(user, plainPw)
return u.String(), nil
}
// sanitizeDSNForLog entfernt Passwort aus DSN, damit du es gefahrlos loggen kannst.
func sanitizeDSNForLog(dsn string) string {
dsn = strings.TrimSpace(dsn)
if dsn == "" {
return ""
}
u, err := url.Parse(dsn)
if err != nil {
return "<invalid dsn>"
}
if u.User != nil {
u.User = url.UserPassword(u.User.Username(), "****")
}
return u.String()
}