fix(community): use UnifiedSyncService for manual sync + document Meta integration

The manual sync button in Community Inbox was only syncing YouTube comments
via the legacy syncAllComments service. Now uses UnifiedSyncService to sync
all platforms (YouTube, Facebook, Instagram). Also adds comprehensive
Community Management documentation to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Porwoll 2026-02-13 23:40:26 +00:00
parent e49d32fa01
commit c33372cc70
2 changed files with 186 additions and 8 deletions

158
CLAUDE.md
View file

@ -443,6 +443,11 @@ Das System unterstützt Deutsch (default) und Englisch:
- **Workflows API:** https://pl.porwoll.tech/api/workflows (GET, öffentlich, tenant required) - **Workflows API:** https://pl.porwoll.tech/api/workflows (GET, öffentlich, tenant required)
- **Data Retention API:** https://pl.porwoll.tech/api/retention (GET/POST, Super-Admin erforderlich) - **Data Retention API:** https://pl.porwoll.tech/api/retention (GET/POST, Super-Admin erforderlich)
- **YouTube Analytics:** https://pl.porwoll.tech/admin/youtube-analytics (Admin View, Auth erforderlich) - **YouTube Analytics:** https://pl.porwoll.tech/admin/youtube-analytics (Admin View, Auth erforderlich)
- **Community Inbox:** https://pl.porwoll.tech/admin/community/inbox (Admin View, Auth erforderlich)
- **Community Analytics:** https://pl.porwoll.tech/admin/community/analytics (Admin View, Auth erforderlich)
- **Community Sync:** https://pl.porwoll.tech/api/community/sync (POST: Trigger, GET: Status)
- **Community Stats:** https://pl.porwoll.tech/api/community/stats (GET, Auth erforderlich)
- **Meta OAuth:** https://pl.porwoll.tech/api/auth/meta (GET, startet OAuth-Flow)
## Security-Features ## Security-Features
@ -1156,6 +1161,157 @@ Die FormSubmissions Collection wurde zu einem leichtgewichtigen CRM erweitert:
- `src/collections/FormSubmissionsOverrides.ts` - Feld-Definitionen - `src/collections/FormSubmissionsOverrides.ts` - Feld-Definitionen
- `src/hooks/formSubmissionHooks.ts` - Automatisierungen - `src/hooks/formSubmissionHooks.ts` - Automatisierungen
## Community Management System
Plattformübergreifendes Community Management für YouTube, Facebook und Instagram mit einer einheitlichen Inbox, AI-Analyse und automatischer Moderation.
### Architektur
```
┌─────────────────────────────────────────┐
│ Admin UI │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Community │ │ Community │ │
│ │ Inbox │ │ Analytics │ │
│ └──────┬──────┘ └────────┬─────────┘ │
└─────────┼──────────────────┼────────────┘
│ │
┌─────────▼──────────────────▼────────────┐
│ /api/community/* │
│ sync, sync-status, stats, reply, │
│ generate-reply, export, stream │
└─────────────────┬───────────────────────┘
┌─────────────────▼───────────────────────┐
│ UnifiedSyncService │
│ ┌──────────┬───────────┬────────────┐ │
│ │ YouTube │ Facebook │ Instagram │ │
│ │ Sync │ Sync │ Sync │ │
│ └────┬─────┴─────┬─────┴──────┬─────┘ │
└───────┼───────────┼────────────┼────────┘
│ │ │
┌───────▼───────────▼────────────▼────────┐
│ CommunityInteractions │
│ (einheitliche Collection für alle │
│ Plattform-Kommentare) │
└─────────────────────────────────────────┘
```
### Admin UI
**Community Inbox** (`/admin/community/inbox`):
- Einheitliche Ansicht aller Kommentare (YouTube + Facebook + Instagram)
- Filter nach Plattform, Status, Priorität, Sentiment, Flags
- AI-generierte Antwort-Vorschläge (Claude)
- Direkte Antwort-Funktion
- Echtzeit-Updates via SSE (`/api/community/stream`)
- Export als CSV/JSON
- Sync-Button (triggert alle Plattformen)
**Community Analytics** (`/admin/community/analytics`):
- KPI-Cards (Gesamt, Heute, Unbeantwortet)
- Sentiment-Trend-Chart
- Topic-Cloud
- Response-Metriken
- Kanal-Vergleich
- Top-Content nach Engagement
### Meta (Facebook + Instagram) Integration
**OAuth-Flow:**
```
1. Admin öffnet /api/auth/meta?socialAccountId=X&accountType=both
2. Redirect zu Facebook OAuth (12 Scopes)
3. Callback: Short-Lived → Long-Lived Token (60 Tage)
4. Facebook Pages + Instagram Business Accounts werden geladen
5. Token + Account-Daten in SocialAccounts gespeichert
```
**Unterstützte Scopes:**
- Facebook: `pages_show_list`, `pages_read_engagement`, `pages_manage_posts`, `pages_read_user_content`, `pages_manage_engagement`, `pages_messaging`
- Instagram: `instagram_basic`, `instagram_manage_comments`, `instagram_manage_messages`, `instagram_content_publish`
**API-Clients:**
| Client | Datei | Funktionen |
|--------|-------|------------|
| MetaBaseClient | `src/lib/integrations/meta/MetaBaseClient.ts` | HTTP-Basis, Pagination, Retry, Token-Management |
| FacebookClient | `src/lib/integrations/meta/FacebookClient.ts` | Posts, Kommentare, Messenger, Insights |
| InstagramClient | `src/lib/integrations/meta/InstagramClient.ts` | Media, Kommentare, Mentions, DMs, Insights |
**Sync-Services:**
| Service | Datei | Beschreibung |
|---------|-------|--------------|
| FacebookSyncService | `src/lib/integrations/meta/FacebookSyncService.ts` | Synct Page-Kommentare + Replies |
| InstagramSyncService | `src/lib/integrations/meta/InstagramSyncService.ts` | Synct Media-Kommentare + Mentions |
| UnifiedSyncService | `src/lib/jobs/UnifiedSyncService.ts` | Orchestriert YouTube + Facebook + Instagram |
**Sync-Ablauf (pro Account):**
1. SocialAccount laden + Platform verifizieren
2. Token validieren
3. Posts/Media abrufen (mit Datum-Filter)
4. Kommentare pro Post synchronisieren
5. AI-Analyse via Claude (Sentiment, Topics, Flags)
6. In CommunityInteractions speichern (dedupliziert via `externalId`)
7. Account-Stats aktualisieren
### Sync-Trigger
| Trigger | Endpoint | Plattformen |
|---------|----------|-------------|
| Cron (alle 15 Min) | `/api/cron/community-sync` | Alle aktiven Accounts |
| Manuell (Inbox-Button) | `/api/community/sync` (POST) | Alle (optional filterbar) |
| YouTube-spezifisch | `/api/cron/youtube-sync` | Nur YouTube |
### Community API-Endpoints
| Endpoint | Methode | Beschreibung |
|----------|---------|--------------|
| `/api/community/sync` | POST | Manueller Sync (alle Plattformen) |
| `/api/community/sync` | GET | Sync-Status |
| `/api/community/sync-status` | GET | Detaillierter Status mit Account-Stats |
| `/api/community/stats` | GET | Interaktions-Statistiken |
| `/api/community/reply` | POST | Antwort senden |
| `/api/community/generate-reply` | POST | AI-Antwort generieren (Claude) |
| `/api/community/export` | GET | Daten-Export (CSV/JSON) |
| `/api/community/stream` | GET | SSE-Stream für Echtzeit-Updates |
| `/api/community/sync-comments` | POST | Kommentar-Sync (Legacy) |
| `/api/community/analytics/*` | GET | Analytics-Daten (6 Endpoints) |
| `/api/auth/meta` | GET | Meta OAuth starten |
| `/api/auth/meta/callback` | GET | Meta OAuth Callback |
### CommunityRules (Automatisierung)
Regel-basierte Automatisierung für eingehende Kommentare:
**Trigger-Typen:** `keyword`, `sentiment`, `question_detected`, `medical_detected`, `influencer` (>10k Follower), `all_new`, `contains_link`, `contains_email`
**Aktionen:** `set_priority`, `assign_to`, `set_flag`, `suggest_template`, `send_notification`, `flag_medical`, `escalate`, `mark_spam`, `set_deadline`
### Wichtige Dateien
```
src/lib/integrations/meta/
├── MetaBaseClient.ts # HTTP-Basis mit Retry + Pagination
├── FacebookClient.ts # Facebook Graph API Client
├── InstagramClient.ts # Instagram Graph API Client
├── FacebookSyncService.ts # Facebook Kommentar-Sync
├── InstagramSyncService.ts # Instagram Kommentar-Sync
├── oauth.ts # Meta OAuth 2.0 Flow
└── index.ts # Re-Exports
src/lib/jobs/
├── UnifiedSyncService.ts # Orchestriert alle Plattform-Syncs
├── syncAllComments.ts # YouTube-spezifischer Sync (Legacy)
└── JobLogger.ts # Strukturiertes Logging
src/app/(payload)/api/
├── auth/meta/ # OAuth Start + Callback
├── community/ # 14 Community-Endpoints
└── cron/community-sync/ # Cron-Trigger für Unified Sync
```
## Globals ## Globals
> **Hinweis:** SiteSettings, Navigations und PrivacyPolicySettings wurden zu tenant-spezifischen Collections umgewandelt (siehe Collections Übersicht). > **Hinweis:** SiteSettings, Navigations und PrivacyPolicySettings wurden zu tenant-spezifischen Collections umgewandelt (siehe Collections Übersicht).
@ -1333,4 +1489,4 @@ ssh payload@162.55.85.18
### Scripts & Backup ### Scripts & Backup
- `scripts/backup/README.md` - Backup-System Dokumentation - `scripts/backup/README.md` - Backup-System Dokumentation
*Letzte Aktualisierung: 13.02.2026 (CI Pipeline: ESLint + Typecheck grün, Payload 3.76.1, Next.js 16.2.0-canary.41)* *Letzte Aktualisierung: 13.02.2026 (Community Management Doku, Sync-Endpoint Fix: UnifiedSyncService statt YouTube-only)*

View file

@ -3,11 +3,17 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { getPayload } from 'payload' import { getPayload } from 'payload'
import config from '@payload-config' import config from '@payload-config'
import { runSync, getSyncStatus } from '@/lib/jobs/syncAllComments' import {
runUnifiedSync,
getUnifiedSyncStatus,
type SupportedPlatform,
} from '@/lib/jobs/UnifiedSyncService'
/** /**
* POST /api/community/sync * POST /api/community/sync
* Manueller Sync-Trigger für alle YouTube-Accounts * Manueller Sync-Trigger für alle Plattformen (YouTube, Facebook, Instagram)
*
* Optional: ?platforms=youtube,facebook,instagram (komma-separiert)
*/ */
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
@ -35,7 +41,7 @@ export async function POST(request: NextRequest) {
} }
// Prüfe ob bereits ein Sync läuft // Prüfe ob bereits ein Sync läuft
const status = getSyncStatus() const status = getUnifiedSyncStatus()
if (status.isRunning) { if (status.isRunning) {
return NextResponse.json( return NextResponse.json(
{ {
@ -46,14 +52,30 @@ export async function POST(request: NextRequest) {
) )
} }
console.log(`[Sync] Manual sync triggered by user ${user.id}`) // Optionaler Plattform-Filter aus Body oder Query
let platforms: SupportedPlatform[] | undefined
try {
const body = await request.json()
if (body.platforms && Array.isArray(body.platforms)) {
platforms = body.platforms as SupportedPlatform[]
}
} catch {
// Kein JSON Body — alle Plattformen syncen
}
const result = await runSync() console.log(
`[Sync] Manual unified sync triggered by user ${user.id}`,
platforms ? `(platforms: ${platforms.join(', ')})` : '(all platforms)'
)
const result = await runUnifiedSync({ platforms })
return NextResponse.json({ return NextResponse.json({
success: result.success, success: result.success,
duration: result.duration,
platforms: result.platforms,
results: result.results, results: result.results,
error: result.error, errors: result.errors.length > 0 ? result.errors : undefined,
}) })
} catch (error: unknown) { } catch (error: unknown) {
console.error('[Sync] Error:', error) console.error('[Sync] Error:', error)
@ -76,7 +98,7 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
} }
const status = getSyncStatus() const status = getUnifiedSyncStatus()
return NextResponse.json({ return NextResponse.json({
...status, ...status,