mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
Implements a unified sync service that orchestrates comment synchronization across all social media platforms. UnifiedSyncService: - Platform-agnostic sync orchestration - Support for YouTube, Facebook, and Instagram - Parallel platform detection and account grouping - Progress tracking with live status updates - Aggregated results per platform - Error handling with partial results support New API Endpoints: - GET/POST /api/cron/community-sync - Cron endpoint for scheduled multi-platform sync - Query params: platforms, accountIds, analyzeWithAI, maxItems - HEAD for monitoring status - GET /api/community/sync-status - Live sync status for dashboard - Platform overview with account details - Interaction statistics (total, today, unanswered) - Last sync result summary Configuration: - vercel.json updated to use community-sync cron - 15-minute sync interval maintained Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
203 lines
5.8 KiB
TypeScript
203 lines
5.8 KiB
TypeScript
// src/app/(payload)/api/cron/community-sync/route.ts
|
|
// Unified Community Sync Cron Endpoint
|
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
import {
|
|
runUnifiedSync,
|
|
getUnifiedSyncStatus,
|
|
type SupportedPlatform,
|
|
type UnifiedSyncOptions,
|
|
} from '@/lib/jobs/UnifiedSyncService'
|
|
|
|
// Geheimer Token für Cron-Authentifizierung
|
|
const CRON_SECRET = process.env.CRON_SECRET
|
|
|
|
/**
|
|
* GET /api/cron/community-sync
|
|
* Wird von externem Cron-Job aufgerufen (z.B. Vercel Cron, cron-job.org)
|
|
*
|
|
* Query Parameters:
|
|
* - platforms: Komma-separierte Liste (youtube,facebook,instagram)
|
|
* - accountIds: Komma-separierte Account-IDs
|
|
* - analyzeWithAI: true/false (default: true)
|
|
* - maxItems: Maximale Items pro Account (default: 100)
|
|
*/
|
|
export async function GET(request: NextRequest) {
|
|
// Auth prüfen wenn CRON_SECRET gesetzt
|
|
if (CRON_SECRET) {
|
|
const authHeader = request.headers.get('authorization')
|
|
|
|
if (authHeader !== `Bearer ${CRON_SECRET}`) {
|
|
console.warn('[Cron] Unauthorized request to community-sync')
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
}
|
|
|
|
// Query-Parameter parsen
|
|
const searchParams = request.nextUrl.searchParams
|
|
const options: UnifiedSyncOptions = {}
|
|
|
|
// Plattform-Filter
|
|
const platformsParam = searchParams.get('platforms')
|
|
if (platformsParam) {
|
|
const platforms = platformsParam.split(',').map((p) => p.trim().toLowerCase())
|
|
const validPlatforms = platforms.filter((p): p is SupportedPlatform =>
|
|
['youtube', 'facebook', 'instagram'].includes(p)
|
|
)
|
|
if (validPlatforms.length > 0) {
|
|
options.platforms = validPlatforms
|
|
}
|
|
}
|
|
|
|
// Account-ID Filter
|
|
const accountIdsParam = searchParams.get('accountIds')
|
|
if (accountIdsParam) {
|
|
const ids = accountIdsParam
|
|
.split(',')
|
|
.map((id) => parseInt(id.trim(), 10))
|
|
.filter((id) => !isNaN(id))
|
|
if (ids.length > 0) {
|
|
options.accountIds = ids
|
|
}
|
|
}
|
|
|
|
// AI-Analyse
|
|
const analyzeParam = searchParams.get('analyzeWithAI')
|
|
if (analyzeParam !== null) {
|
|
options.analyzeWithAI = analyzeParam !== 'false'
|
|
}
|
|
|
|
// Max Items
|
|
const maxItemsParam = searchParams.get('maxItems')
|
|
if (maxItemsParam) {
|
|
const maxItems = parseInt(maxItemsParam, 10)
|
|
if (!isNaN(maxItems) && maxItems > 0) {
|
|
options.maxItemsPerAccount = maxItems
|
|
}
|
|
}
|
|
|
|
console.log('[Cron] Starting community sync', { options })
|
|
|
|
const result = await runUnifiedSync(options)
|
|
|
|
if (result.success) {
|
|
// Gesamtstatistiken berechnen
|
|
let totalNew = 0
|
|
let totalUpdated = 0
|
|
let totalErrors = 0
|
|
|
|
for (const platform of Object.values(result.platforms)) {
|
|
if (platform) {
|
|
totalNew += platform.totalNewComments
|
|
totalUpdated += platform.totalUpdatedComments
|
|
totalErrors += platform.totalErrors
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `Sync completed: ${totalNew} new, ${totalUpdated} updated comments across ${result.results.length} accounts`,
|
|
duration: result.duration,
|
|
platforms: result.platforms,
|
|
results: result.results,
|
|
})
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
errors: result.errors,
|
|
partialResults: result.results,
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* POST /api/cron/community-sync
|
|
* Manueller Sync-Trigger mit erweiterten Optionen
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
// Auth prüfen
|
|
if (CRON_SECRET) {
|
|
const authHeader = request.headers.get('authorization')
|
|
|
|
if (authHeader !== `Bearer ${CRON_SECRET}`) {
|
|
console.warn('[Cron] Unauthorized POST to community-sync')
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
}
|
|
|
|
try {
|
|
const body = await request.json()
|
|
const options: UnifiedSyncOptions = {}
|
|
|
|
// Optionen aus Body übernehmen
|
|
if (body.platforms && Array.isArray(body.platforms)) {
|
|
options.platforms = body.platforms.filter((p: string): p is SupportedPlatform =>
|
|
['youtube', 'facebook', 'instagram'].includes(p)
|
|
)
|
|
}
|
|
|
|
if (body.accountIds && Array.isArray(body.accountIds)) {
|
|
options.accountIds = body.accountIds.filter(
|
|
(id: unknown) => typeof id === 'number' && id > 0
|
|
)
|
|
}
|
|
|
|
if (typeof body.analyzeWithAI === 'boolean') {
|
|
options.analyzeWithAI = body.analyzeWithAI
|
|
}
|
|
|
|
if (typeof body.maxItemsPerAccount === 'number' && body.maxItemsPerAccount > 0) {
|
|
options.maxItemsPerAccount = body.maxItemsPerAccount
|
|
}
|
|
|
|
if (body.sinceDate) {
|
|
const date = new Date(body.sinceDate)
|
|
if (!isNaN(date.getTime())) {
|
|
options.sinceDate = date
|
|
}
|
|
}
|
|
|
|
console.log('[Cron] Manual community sync triggered', { options })
|
|
|
|
const result = await runUnifiedSync(options)
|
|
|
|
return NextResponse.json({
|
|
success: result.success,
|
|
startedAt: result.startedAt,
|
|
completedAt: result.completedAt,
|
|
duration: result.duration,
|
|
platforms: result.platforms,
|
|
results: result.results,
|
|
errors: result.errors,
|
|
})
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
console.error('[Cron] POST error:', error)
|
|
return NextResponse.json({ error: message }, { status: 500 })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HEAD /api/cron/community-sync
|
|
* Status-Check für Monitoring
|
|
*/
|
|
export async function HEAD() {
|
|
const status = getUnifiedSyncStatus()
|
|
|
|
return new NextResponse(null, {
|
|
status: status.isRunning ? 423 : 200, // 423 = Locked (running)
|
|
headers: {
|
|
'X-Sync-Running': status.isRunning.toString(),
|
|
'X-Current-Platform': status.currentPlatform || 'none',
|
|
'X-Current-Account': status.currentAccount || 'none',
|
|
'X-Progress': `${status.progress.percentage}%`,
|
|
'X-Last-Run': status.lastRunAt || 'never',
|
|
},
|
|
})
|
|
}
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
export const maxDuration = 300 // 5 Minuten max (Vercel)
|