197 lines
6.3 KiB
Go
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()
|
|
}
|