Alle ausstehenden Aenderungen (Bilder, Texte) fuer den Server-Umzug uebernommen

This commit is contained in:
2026-08-05 18:05:53 +02:00
parent 9af0698056
commit e331965bf1
87 changed files with 8296 additions and 309 deletions
@@ -0,0 +1,152 @@
-- =====================================================================
-- DOGFATHER × VanVan Supporter-Abo — Datenbankschema
-- Setzt "Dogfather_VanVan_Supporter_Abo.odt" um (Abschnitt 28 empfiehlt
-- diese Struktur; hier leicht angepasst an die Konventionen des restlichen
-- Workers: TEXT-IDs, ISO-Zeitstempel, Wiederverwendung von audit_log statt
-- eines eigenen Änderungsprotokolls).
--
-- Bewusst eine EIGENE, von der internen Team-Zugangsverwaltung (users/
-- roles/sessions) komplett GETRENNTE Nutzerwelt — Supporter sind
-- öffentliche Fans/Kund:innen, keine Teammitglieder mit Rechten.
-- =====================================================================
-- Öffentliche Supporter-Accounts. Nur Name, TikTok-Name, E-Mail bei
-- Registrierung (siehe Abschnitt 5/30 — Datensparsamkeit).
CREATE TABLE IF NOT EXISTS supporter_users (
id TEXT PRIMARY KEY,
display_name TEXT NOT NULL,
tiktok_username TEXT, -- ohne "@", normalisiert
email TEXT NOT NULL UNIQUE,
email_verified INTEGER NOT NULL DEFAULT 0,
pending_email TEXT, -- neue E-Mail, bis sie bestätigt wird (Abschnitt 25)
status TEXT NOT NULL DEFAULT 'active', -- active | deleted
-- Versanddaten werden ERST bei der ersten Prämien-Einlösung abgefragt
-- (Abschnitt 17/30), deshalb hier bewusst alles NULL-fähig.
ship_name TEXT,
ship_street TEXT,
ship_zip TEXT,
ship_city TEXT,
ship_country TEXT,
ship_save_address INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
last_login_at TEXT
);
-- Einmalcodes für E-Mail-Bestätigung UND Magic-Link-Login (Abschnitt 5/116).
CREATE TABLE IF NOT EXISTS supporter_auth_codes (
id TEXT PRIMARY KEY,
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
purpose TEXT NOT NULL, -- verify_email | login
code_hash TEXT NOT NULL,
expires_at TEXT NOT NULL,
used_at TEXT,
created_at TEXT NOT NULL
);
-- Sessions für den öffentlichen Supporter-Bereich — eigenes, von der
-- internen Team-Session-Tabelle getrenntes Token-System.
CREATE TABLE IF NOT EXISTS supporter_sessions (
token_hash TEXT PRIMARY KEY,
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
created_at TEXT NOT NULL,
last_active_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
-- Eine (aktuell: genau eine) Abo-Stufe pro Supporter.
CREATE TABLE IF NOT EXISTS supporter_subscriptions (
id TEXT PRIMARY KEY,
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
paypal_subscription_id TEXT UNIQUE, -- erst nach PayPal-Freigabe gesetzt
paypal_plan_id TEXT,
status TEXT NOT NULL DEFAULT 'pending', -- pending | active | suspended | cancelled | expired
started_at TEXT,
next_payment_at TEXT,
paid_period_end_at TEXT, -- bis wann Zugang nach Kündigung bleibt
cancelled_at TEXT,
-- Fortschritt (Abschnitt 11-13): Gesamtzahl erfolgreich angerechneter
-- Monate bleibt für IMMER erhalten (auch über Kündigung/Reaktivierung
-- hinweg, Abschnitt 23) — daraus werden Zyklus/Meilenstein/Kollektion
-- rein rechnerisch abgeleitet (siehe lib/supporter-cycle.js), NICHT
-- redundant gespeichert, damit nichts auseinanderlaufen kann.
total_paid_months INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
-- Jede EINZELNE erfolgreiche/fehlgeschlagene PayPal-Zahlung. Idempotenz
-- über paypal_event_id: jedes Webhook-Ereignis wird nur einmal verarbeitet
-- (Abschnitt 8, "doppelt empfangenes PayPal-Ereignis: keine doppelte
-- Anrechnung").
CREATE TABLE IF NOT EXISTS supporter_payments (
id TEXT PRIMARY KEY,
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
subscription_id TEXT NOT NULL REFERENCES supporter_subscriptions(id),
paypal_transaction_id TEXT,
paypal_event_id TEXT UNIQUE,
amount TEXT,
currency TEXT DEFAULT 'EUR',
payment_status TEXT NOT NULL, -- completed | failed | refunded | reversed
is_sandbox INTEGER NOT NULL DEFAULT 0, -- Testzahlungen zählen NIE als echter Monat
counted_for_progress INTEGER NOT NULL DEFAULT 0,
paid_at TEXT NOT NULL,
created_at TEXT NOT NULL
);
-- Supporter-Kollektionen (Abschnitt 14) — jeder abgeschlossene 30-Monats-
-- Zyklus bekommt ein neues Design. Zyklus-Nummer 1 = Kollektion 01 usw.
-- collection_number ist der 1-basierte Zyklus-Index (global, nicht pro
-- Supporter — alle Supporter im selben Zyklus teilen sich EIN Design).
CREATE TABLE IF NOT EXISTS supporter_collections (
id TEXT PRIMARY KEY,
collection_number INTEGER NOT NULL UNIQUE,
name TEXT NOT NULL,
theme TEXT,
description TEXT,
cover_image TEXT,
small_teddy_design TEXT,
medium_teddy_design TEXT,
large_teddy_design TEXT,
mug_design TEXT,
card_design TEXT,
hoodie_design TEXT,
available_hoodie_sizes TEXT DEFAULT '["S","M","L","XL","XXL"]', -- JSON-Array
status TEXT NOT NULL DEFAULT 'draft', -- draft | published
created_at TEXT NOT NULL
);
-- Eine Zeile pro freigeschaltetem Meilenstein/Prämie eines Supporters.
-- milestone_month ∈ {6,12,18,24,30}, cycle_number = welcher 30er-Zyklus.
CREATE TABLE IF NOT EXISTS supporter_premiums (
id TEXT PRIMARY KEY,
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
collection_id TEXT NOT NULL REFERENCES supporter_collections(id),
cycle_number INTEGER NOT NULL,
milestone_month INTEGER NOT NULL, -- 6 | 12 | 18 | 24 | 30
premium_type TEXT NOT NULL, -- teddy_klein | teddy_mittel | teddy_gross | tasse_karte | hoodie
unlocked_at TEXT NOT NULL,
redeem_status TEXT NOT NULL DEFAULT 'verfuegbar',
-- verfuegbar | eingeloest | wird_geprueft | wird_vorbereitet
-- versandbereit | versendet | zugestellt | erhalten | problem | ersatz_wird_vorbereitet
redeemed_at TEXT,
hoodie_size TEXT,
hoodie_variant TEXT,
ship_name TEXT, ship_street TEXT, ship_zip TEXT, ship_city TEXT, ship_country TEXT,
tracking_number TEXT,
shipped_at TEXT,
delivered_at TEXT,
admin_note TEXT,
UNIQUE (supporter_id, cycle_number, milestone_month)
);
CREATE INDEX IF NOT EXISTS idx_supp_sub_supporter ON supporter_subscriptions(supporter_id);
CREATE INDEX IF NOT EXISTS idx_supp_pay_supporter ON supporter_payments(supporter_id);
CREATE INDEX IF NOT EXISTS idx_supp_pay_sub ON supporter_payments(subscription_id);
CREATE INDEX IF NOT EXISTS idx_supp_premiums_supporter ON supporter_premiums(supporter_id);
CREATE INDEX IF NOT EXISTS idx_supp_sessions_supporter ON supporter_sessions(supporter_id);
CREATE INDEX IF NOT EXISTS idx_supp_codes_supporter ON supporter_auth_codes(supporter_id);
-- Kollektion 01 direkt mit anlegen, damit das System sofort nutzbar ist —
-- Designs/Bilder können jederzeit im Admin-Bereich nachgepflegt werden.
INSERT OR IGNORE INTO supporter_collections (id, collection_number, name, theme, description, status, created_at)
VALUES ('collection-01', 1, 'Der Anfang', 'Babyblau & Silber', 'Die allererste Supporter-Kollektion von Dogfather × VanVan.', 'published', datetime('now'));