docs: update AGENTS.md and DIRECTUS-SCHEMA.md with crypto namespacing, chat polling, buyer_user field and notification flow
This commit is contained in:
@@ -146,6 +146,7 @@ locales/
|
|||||||
- **Font-Family**: `var(--font-family)` für Body, `var(--font-family-heading)` für Headlines
|
- **Font-Family**: `var(--font-family)` für Body, `var(--font-family-heading)` für Headlines
|
||||||
- **Border-Radius**: `var(--radius-sm|md|lg|xl|full)`
|
- **Border-Radius**: `var(--radius-sm|md|lg|xl|full)`
|
||||||
- **Breakpoint**: `@media (max-width: 768px)` - einziger Mobile-Breakpoint für alle Komponenten
|
- **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
|
### i18n
|
||||||
- Schlüssel: `section.key` (z.B. `home.title`)
|
- 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
|
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
|
2. **Service Worker** kann lokale Änderungen cachen - bei Problemen Cache leeren
|
||||||
3. **i18n muss vor Shell laden** - siehe `app.js` (dynamischer Import)
|
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_<hash>`, `kashilo_salt_<hash>`, `kashilo_lk_<hash>`) — 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
|
## Nächste Schritte
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@ locales/
|
|||||||
| `categories_translations` | ✓ | - | - | Für i18n |
|
| `categories_translations` | ✓ | - | - | Für i18n |
|
||||||
| `locations` | ✓ | ✓ | - | User kann neue Orte anlegen |
|
| `locations` | ✓ | ✓ | - | User kann neue Orte anlegen |
|
||||||
| `languages` | ✓ | - | - | Für Sprachen-Liste |
|
| `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) |
|
| `messages` | - | - | - | **Nur User-Rolle** (Auth-Pflicht, kein Public-Zugriff) |
|
||||||
| `favorites` | ✓ | ✓ | - | User-Rolle: Filter `user = $CURRENT_USER`, Delete erlaubt |
|
| `favorites` | ✓ | ✓ | - | User-Rolle: Filter `user = $CURRENT_USER`, Delete erlaubt |
|
||||||
| `notifications` | ✓ | ✓ (via Flow/Webhook) | ✓ | User-Rolle: Filter `user_hash`, nur `read` updaten |
|
| `notifications` | ✓ | ✓ (via Flow/Webhook) | ✓ | User-Rolle: Filter `user_hash`, nur `read` updaten |
|
||||||
@@ -214,8 +215,8 @@ locales/
|
|||||||
| Flow | Trigger | Aktion |
|
| Flow | Trigger | Aktion |
|
||||||
|------|---------|--------|
|
|------|---------|--------|
|
||||||
| Archive Expired Listings | Schedule `*/15 * * * *` | `status → archived` wenn `expires_at < NOW` |
|
| 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: Listing Published~~ | ~~Webhook~~ | **Deaktiviert** — wird jetzt direkt von `btcpay-webhook.php` gehandhabt (mit Duplikat-Check) |
|
||||||
| Notify: New Message | Event Hook `items.create` on `messages` | Creates notification for message recipient |
|
| 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.
|
Siehe `docs/DIRECTUS-SCHEMA.md` für vollständiges Schema.
|
||||||
|
|
||||||
|
|||||||
@@ -141,10 +141,11 @@ Zero-Knowledge Chat-Konversationen zwischen anonymen Usern.
|
|||||||
| `public_key_1` | text | NaCl Public Key Teilnehmer 1 |
|
| `public_key_1` | text | NaCl Public Key Teilnehmer 1 |
|
||||||
| `public_key_2` | text | NaCl Public Key Teilnehmer 2 |
|
| `public_key_2` | text | NaCl Public Key Teilnehmer 2 |
|
||||||
| `status` | string | `active`, `closed` |
|
| `status` | string | `active`, `closed` |
|
||||||
|
| `buyer_user` | UUID | FK → directus_users, Buyer's Directus user ID |
|
||||||
| `date_created` | datetime | Erstellungsdatum |
|
| `date_created` | datetime | Erstellungsdatum |
|
||||||
| `date_updated` | datetime | Letzte Nachricht |
|
| `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.
|
**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)
|
## 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 |
|
| Aktion | Erlaubt | Filter / Einschränkung |
|
||||||
|--------|:-------:|------------------------|
|
|--------|:-------:|------------------------|
|
||||||
| Read | ✓ | Alle (Client filtert via `participant_hash`) |
|
| Read | ✓ | Alle (Client filtert via `participant_hash`) |
|
||||||
| Create | ✓ | Keine Einschränkung |
|
| Create | ✓ | Felder inkl. `buyer_user` |
|
||||||
| Update | ✓ | Nur `status` Feld, kein Row-Filter |
|
| Update | ✓ | Nur `status`, `buyer_user` Felder, kein Row-Filter |
|
||||||
| Delete | - | Nicht erlaubt |
|
| 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)
|
### messages (User-Rolle)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user