SMTP-Server, Port, Verschluesselung, Zugang und Absender unter Mehr ->
Erinnerungen, gespeichert in der neuen Tabelle app_setting. Das Passwort
liegt mit AES-256-GCM verschluesselt (Schluessel aus BETTER_AUTH_SECRET),
wird nie angezeigt und nicht exportiert. Die .env hat weiter Vorrang. Der
Zeitplan laeuft jetzt immer und prueft je Durchlauf, ob ein Server da ist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unten Start, Katalog, Sammlung, Wuensche und ein Plus mit allen Wegen zu
einem neuen Set. Konto, Design und Stammdaten auf der Seite "Mehr" statt
im Konto-Menue und in sieben Reitern. Bei mehreren Sammlern waehlt "Fuer
wen?" in der Kopfzeile das Kind fuer die ganze App, gemerkt pro Geraet.
Behebt nebenbei, dass Mitglieder die Stammdaten nicht erreichten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruecken Termin und offene Wuensche zusammen, geht eine Nachricht an den
Besitzer der Liste - standardmaessig 14 Tage vorher, hoechstens einmal
pro Woche je Liste. Ist alles reserviert, bleibt es still: eine
Erinnerung ohne Handlungsbedarf ist nur Laerm.
Der Zeitplan haengt an Next' instrumentation-Hook und prueft stuendlich,
nicht taeglich - so kann ein Neustart keinen Tag ueberspringen, und die
Sperrfrist je Liste verhindert trotzdem Mehrfachversand. Der Zeitstempel
wird erst nach erfolgreichem Versand gesetzt, damit ein Fehlversuch
wiederholt wird.
Ohne SMTP_HOST und SMTP_FROM ist die Funktion komplett aus: kein
Zeitplan, kein Versand, und die Verwaltungsseite erklaert stattdessen,
was in die .env gehoert. Mit Konfiguration gibt es dort Knoepfe fuer
Verbindungstest, Testnachricht und sofortige Pruefung.
12 Tests fuer die Entscheidungsregeln. Einer davon hat einen echten
Fehler gefunden: die Sperrfrist rechnete mit umgedrehtem Vorzeichen und
haette jede Erinnerung dauerhaft unterdrueckt.
Refs Phase 3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Katalog bleibt gemeinsam - ein Set existiert einmal, unabhaengig
davon, wer es hat. Besitz und Wunschlisten gehoeren dagegen je einem
Sammler. Damit beantwortet "hat er das schon?" jetzt die Frage "hat *er*
das schon?": ein Set, das nur die Schwester besitzt, faellt fuer den
Bruder wieder unter "fehlt noch" und darf auf seine Wunschliste.
Wichtig fuer den Alltag: Solange es nur einen Sammler gibt, taucht die
Unterscheidung nirgends in der Oberflaeche auf. Kein Umschalter, keine
Abfrage beim Hinzufuegen, keine Badges - die App bleibt exakt so
einfach wie vorher. Erst der zweite Sammler schaltet all das frei.
Die Besitzlogik liegt weiterhin an genau einer Stelle
(src/lib/ownership.ts) und hat nur eine Dimension dazubekommen. Das
automatische "erfuellt" auf Wunschlisten fragt jetzt ebenfalls je Liste:
ein Wunsch auf Max' Liste ist erfuellt, wenn Max das Set bekommt - nicht
wenn Mia es hat. Listen ohne Sammler verhalten sich wie bisher.
Die Migration legt einen Sammler "Sammlung" an und ordnet ihm alles
Vorhandene zu; umbenennen laesst er sich unter Verwaltung -> Sammler.
Der Seed uebernimmt ihn statt einen dritten anzulegen.
Refs Phase 3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Unter Verwaltung -> Daten laedt der Admin alles als ZIP (Daten plus
hochgeladene Dateien), nur die Daten als JSON oder die Sammlung als CSV
- letzteres mit Semikolon und BOM, damit deutsches Excel es ohne
Rueckfragen oeffnet. Die CSV steht auch Mitgliedern offen, das
vollstaendige Archiv nur Administratoren.
Bewusst nicht im Export: Konten, Passwoerter und Passkeys. Ein Export
voller Passwort-Hashes im Download-Ordner waere ein Risiko und kein
Backup - das bleibt ./data. Ebenfalls draussen: Reservierungen und
Teilen-Links, die zur jeweiligen Instanz gehoeren.
Der Import ersetzt alle Anwendungsdaten in einer Transaktion, statt zu
vermischen: Zusammenfuehren zweier Sammlungen braeuchte Konfliktregeln
fuer jede Tabelle und waere im Ergebnis nicht vorhersagbar. Deshalb ist
er Administratoren vorbehalten und verlangt ein getipptes ERSETZEN.
Wunschlisten gehoeren danach dem importierenden Admin und bekommen
frische, zunaechst nicht geteilte Links. Pfade aus dem Archiv werden
gegen Ausbrueche aus dem Dateiverzeichnis geprueft.
10 Tests decken den Rundweg ab: Export, Loeschen, Import, Ersetzen statt
Vermischen, und dass ein fremdes Archiv abgelehnt wird, ohne etwas zu
loeschen.
Refs Phase 2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nach dem ersten Nutzer bleibt die Registrierung geschlossen; weitere
Konten entstehen nur ueber einen Einladungslink. Der Klartext-Token
wird genau einmal angezeigt, in der Datenbank liegt sein SHA-256-Hash.
Optional laesst sich eine Einladung an eine E-Mail-Adresse binden, die
Rolle und die Gueltigkeit sind waehlbar.
Die Freigabe zum Anlegen eines Kontos laeuft ueber AsyncLocalStorage:
Der Server-Action, der den Token geprueft hat, umschliesst damit den
signUp-Aufruf, und nur in diesem Kontext laesst der user-create-Hook von
better-auth die Registrierung zu. Die Rolle kommt aus der Einladung und
kann nicht ueber das Formular gesetzt werden.
Der Smoke-Test deckt den ganzen Weg ab: Admin erstellt die Einladung,
der zweite Elternteil registriert sich damit, bekommt die Rolle
"Mitglied" (und wird von der Einladungsverwaltung weggeleitet), und der
Link funktioniert kein zweites Mal.
Refs Phase 2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gemeinsame CrudSection fuer die drei kleinen Stammdatentabellen; sie
unterscheiden sich nur in den Formularfeldern. Loeschen ist bewusst
blockiert, solange Sets am Hersteller oder Thema haengen - Standorte
duerfen weg, die Sammlungseintraege verlieren dann nur den Ort.
Dazu die gemeinsamen Bausteine: zod-Schemata fuer alle Formulare,
einheitliches ActionResult, Slug-Erzeugung mit deutschen Umlauten,
Geld als Cent und Datumsformatierung dd.MM.yyyy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>