diff --git a/AGENTS.md b/AGENTS.md index 22ae164..f314a7a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -146,6 +146,7 @@ locales/ - **Font-Family**: `var(--font-family)` für Body, `var(--font-family-heading)` für Headlines - **Border-Radius**: `var(--radius-sm|md|lg|xl|full)` - **Breakpoint**: `@media (max-width: 768px)` - einziger Mobile-Breakpoint für alle Komponenten +- **Kontrast-Text**: `var(--color-success-text)` für Text auf Success-Hintergrund (Light: `#fff`, Dark: `#042F2E`) ### i18n - Schlüssel: `section.key` (z.B. `home.title`) @@ -159,7 +160,7 @@ locales/ 1. **SSH funktioniert nicht** mit diesem Repo auf gitea.pro - HTTPS mit Token nutzen 2. **Service Worker** kann lokale Änderungen cachen - bei Problemen Cache leeren 3. **i18n muss vor Shell laden** - siehe `app.js` (dynamischer Import) -4. **E2E Chat**: Per-listing Keypairs (nicht Account-Key) für Seller-Identität. Listing braucht `contact_public_key` Feld in Directus. TOFU Key-Pinning warnt bei Schlüsseländerung. Conversations/Messages nur über User-Rolle zugänglich (kein Public-Zugriff). +4. **E2E Chat**: Per-listing Keypairs (nicht Account-Key) für Seller-Identität. Listing braucht `contact_public_key` Feld in Directus. TOFU Key-Pinning warnt bei Schlüsseländerung. Conversations/Messages nur über User-Rolle zugänglich (kein Public-Zugriff). Crypto-Storage ist per Account namespaced (`kashilo_kp_`, `kashilo_salt_`, `kashilo_lk_`) — unterstützt mehrere Accounts im selben Browser, Migration vom alten Format automatisch. Chat-Widget pollt alle 5s nach neuen Nachrichten wenn geöffnet. Conversations haben ein `buyer_user` Feld (UUID, FK → directus_users). ## Nächste Schritte @@ -202,7 +203,7 @@ locales/ | `categories_translations` | ✓ | - | - | Für i18n | | `locations` | ✓ | ✓ | - | User kann neue Orte anlegen | | `languages` | ✓ | - | - | Für Sprachen-Liste | -| `conversations` | - | - | - | **Nur User-Rolle** (Auth-Pflicht, kein Public-Zugriff) | +| `conversations` | - | - | ✓ | **Nur User-Rolle** (Auth-Pflicht, kein Public-Zugriff). Update: `buyer_user` Feld | | `messages` | - | - | - | **Nur User-Rolle** (Auth-Pflicht, kein Public-Zugriff) | | `favorites` | ✓ | ✓ | - | User-Rolle: Filter `user = $CURRENT_USER`, Delete erlaubt | | `notifications` | ✓ | ✓ (via Flow/Webhook) | ✓ | User-Rolle: Filter `user_hash`, nur `read` updaten | @@ -214,8 +215,8 @@ locales/ | Flow | Trigger | Aktion | |------|---------|--------| | Archive Expired Listings | Schedule `*/15 * * * *` | `status → archived` wenn `expires_at < NOW` | -| Notify: Listing Published | Webhook (btcpay-webhook.php) | Creates notification when listing is published after payment | -| Notify: New Message | Event Hook `items.create` on `messages` | Creates notification for message recipient | +| ~~Notify: Listing Published~~ | ~~Webhook~~ | **Deaktiviert** — wird jetzt direkt von `btcpay-webhook.php` gehandhabt (mit Duplikat-Check) | +| Notify: New Message | Event Hook `items.create` on `messages` | Read Data (Conversation + Listing) → Run Script (Empfänger ermitteln) → Condition → Create Data (Notification). Benötigt `$full` Permissions auf Read Data und Create Data | Siehe `docs/DIRECTUS-SCHEMA.md` für vollständiges Schema. diff --git a/docs/DIRECTUS-SCHEMA.md b/docs/DIRECTUS-SCHEMA.md index 1f12034..c9a0934 100644 --- a/docs/DIRECTUS-SCHEMA.md +++ b/docs/DIRECTUS-SCHEMA.md @@ -141,10 +141,11 @@ Zero-Knowledge Chat-Konversationen zwischen anonymen Usern. | `public_key_1` | text | NaCl Public Key Teilnehmer 1 | | `public_key_2` | text | NaCl Public Key Teilnehmer 2 | | `status` | string | `active`, `closed` | +| `buyer_user` | UUID | FK → directus_users, Buyer's Directus user ID | | `date_created` | datetime | Erstellungsdatum | | `date_updated` | datetime | Letzte Nachricht | -**Hinweis:** Die `participant_hash_*` Felder ermöglichen anonyme Zuordnung ohne Directus-User-Accounts. +**Hinweis:** Die `participant_hash_*` Felder ermöglichen anonyme Zuordnung ohne Directus-User-Accounts. Das `buyer_user` Feld speichert die Directus-User-ID des Käufers und wird bei Conversation-Erstellung gesetzt. --- @@ -309,6 +310,34 @@ module.exports = async function(data) { **Hinweis:** Ohne diese Absicherung könnte jeder `views` auf beliebige Werte setzen. +### Archive Expired Listings + +**Zweck:** Archiviert abgelaufene Listings automatisch. + +| Schritt | Typ | Beschreibung | +|---------|-----|--------------| +| 1. Trigger | Schedule `*/15 * * * *` | Alle 15 Minuten | +| 2. Operation | Update Data | `status → archived` wenn `expires_at < NOW` | + +### ~~Notify: Listing Published~~ (Deaktiviert) + +**Status:** Deaktiviert — wird jetzt direkt von `btcpay-webhook.php` gehandhabt (mit Duplikat-Check: prüft auf existierende Notification bevor eine neue erstellt wird, verarbeitet nur `InvoiceSettled`). + +### Notify: New Message + +**Zweck:** Erstellt eine Benachrichtigung für den Empfänger einer neuen Chat-Nachricht. + +| Schritt | Typ | Beschreibung | +|---------|-----|--------------| +| 1. Trigger | Event Hook | `items.create` auf `messages` | +| 2. Read Data | Read Data | Conversation laden (anhand `conversation` ID aus Payload) | +| 3. Read Data | Read Data | Listing laden (anhand `listing_id` aus Conversation) | +| 4. Run Script | Run Script | Empfänger ermitteln (`sender_hash` ≠ `participant_hash_1/2` → anderer ist Empfänger) | +| 5. Condition | Filter Rule | Prüft ob Empfänger ermittelt wurde | +| 6. Create Data | Create Data | Notification erstellen für Empfänger | + +**Wichtig:** Die Read Data und Create Data Operations benötigen **`$full`** Permissions (nicht `$trigger`), da sie auf Collections zugreifen die nicht im Trigger-Context verfügbar sind. + --- ## User Role Permissions (Chat) @@ -320,11 +349,11 @@ Conversations und Messages sind **nicht** über die Public-Rolle zugänglich. Nu | Aktion | Erlaubt | Filter / Einschränkung | |--------|:-------:|------------------------| | Read | ✓ | Alle (Client filtert via `participant_hash`) | -| Create | ✓ | Keine Einschränkung | -| Update | ✓ | Nur `status` Feld, kein Row-Filter | +| Create | ✓ | Felder inkl. `buyer_user` | +| Update | ✓ | Nur `status`, `buyer_user` Felder, kein Row-Filter | | Delete | - | Nicht erlaubt | -**Hinweis:** Im Zero-Knowledge-Design gibt es kein server-verifizierbares Feld, das beide Teilnehmer identifiziert (`participant_hash` ist nicht an einen Directus-User gebunden). Deshalb kann kein `$CURRENT_USER`-Filter für Read/Update gesetzt werden. Die Absicherung erfolgt durch: (1) Authentifizierungspflicht (Public hat keinen Zugriff), (2) Client-seitige Filterung via `participant_hash`, (3) Update nur auf `status`-Feld beschränkt, (4) Conversation-UUIDs sind nicht erratbar. +**Hinweis:** Im Zero-Knowledge-Design gibt es kein server-verifizierbares Feld, das beide Teilnehmer identifiziert (`participant_hash` ist nicht an einen Directus-User gebunden). Deshalb kann kein `$CURRENT_USER`-Filter für Read/Update gesetzt werden. Die Absicherung erfolgt durch: (1) Authentifizierungspflicht (Public hat keinen Zugriff), (2) Client-seitige Filterung via `participant_hash`, (3) Update nur auf `status` und `buyer_user` Felder beschränkt, (4) Conversation-UUIDs sind nicht erratbar. ### messages (User-Rolle)