/** * Sync All Comments Job * * Synchronisiert Kommentare für alle aktiven YouTube-Accounts. * Kann als Cron-Job oder manuell ausgeführt werden. */ import { getPayload, Payload } from 'payload' import config from '@payload-config' import { refreshAccessToken, isTokenExpired } from '@/lib/integrations/youtube/oauth' interface SocialAccount { id: number displayName?: string isActive?: boolean externalId?: string credentials?: { accessToken?: string refreshToken?: string tokenExpiresAt?: string } syncSettings?: { autoSyncEnabled?: boolean syncIntervalMinutes?: number syncComments?: boolean } stats?: { lastSyncedAt?: string } } interface SyncResult { accountId: number displayName: string success: boolean created: number updated: number errors: string[] } /** * Führt den Comment-Sync für alle aktiven Accounts durch */ export async function runSyncAllCommentsJob(payload: Payload): Promise<{ totalAccounts: number successfulSyncs: number totalNew: number totalUpdated: number errors: string[] }> { console.log('🔄 Starting scheduled comment sync...') console.log(` Time: ${new Date().toISOString()}`) // Aktive Accounts mit Auto-Sync laden const accounts = await payload.find({ collection: 'social-accounts', where: { and: [ { isActive: { equals: true } }, { 'syncSettings.autoSyncEnabled': { equals: true } }, { 'credentials.accessToken': { exists: true } }, ], }, limit: 100, depth: 2, }) console.log(`📊 Found ${accounts.docs.length} accounts to sync`) let totalNew = 0 let totalUpdated = 0 let successfulSyncs = 0 const errors: string[] = [] const results: SyncResult[] = [] for (const doc of accounts.docs) { const account = doc as unknown as SocialAccount try { console.log(`\n📺 Syncing: ${account.displayName || account.id}`) // Token-Ablauf prüfen und ggf. erneuern if (isTokenExpired(account.credentials?.tokenExpiresAt)) { console.log(' ⏰ Token expired, refreshing...') if (!account.credentials?.refreshToken) { console.log(' ❌ No refresh token available') errors.push(`${account.displayName}: No refresh token available`) continue } try { const newCredentials = await refreshAccessToken(account.credentials.refreshToken) await payload.update({ collection: 'social-accounts', id: account.id, data: { credentials: { accessToken: newCredentials.access_token || undefined, refreshToken: newCredentials.refresh_token || account.credentials.refreshToken, tokenExpiresAt: newCredentials.expiry_date ? new Date(newCredentials.expiry_date).toISOString() : undefined, }, }, }) console.log(' ✅ Token refreshed') } catch (refreshError) { const msg = refreshError instanceof Error ? refreshError.message : 'Unknown error' console.error(' ❌ Token refresh failed:', msg) errors.push(`${account.displayName}: Token refresh failed - ${msg}`) continue } } // CommentsSyncService dynamisch importieren const { CommentsSyncService } = await import( '@/lib/integrations/youtube/CommentsSyncService' ) const syncService = new CommentsSyncService(payload) const result = await syncService.syncCommentsForAccount(account.id, { maxComments: 100, analyzeWithAI: true, }) const created = result.created || 0 const updated = result.updated || 0 totalNew += created totalUpdated += updated successfulSyncs++ results.push({ accountId: account.id, displayName: account.displayName || `Account ${account.id}`, success: true, created, updated, errors: result.errors || [], }) console.log(` ✅ New: ${created}, Updated: ${updated}`) if (result.errors?.length > 0) { errors.push(...result.errors.map((e: string) => `${account.displayName}: ${e}`)) } } catch (accountError: unknown) { const msg = accountError instanceof Error ? accountError.message : 'Unknown error' console.error(` ❌ Failed: ${msg}`) errors.push(`${account.displayName}: ${msg}`) results.push({ accountId: account.id, displayName: account.displayName || `Account ${account.id}`, success: false, created: 0, updated: 0, errors: [msg], }) } } // Zusammenfassung console.log('\n' + '='.repeat(50)) console.log('📊 SYNC SUMMARY') console.log('='.repeat(50)) console.log(` Accounts processed: ${accounts.docs.length}`) console.log(` Successful syncs: ${successfulSyncs}`) console.log(` New comments: ${totalNew}`) console.log(` Updated: ${totalUpdated}`) console.log(` Errors: ${errors.length}`) if (errors.length > 0) { console.log('\n⚠️ Errors:') errors.forEach((e) => console.log(` - ${e}`)) } return { totalAccounts: accounts.docs.length, successfulSyncs, totalNew, totalUpdated, errors, } } /** * Standalone-Ausführung für Cron-Job */ async function main() { try { const payload = await getPayload({ config }) await runSyncAllCommentsJob(payload) process.exit(0) } catch (error) { console.error('❌ Sync job failed:', error) process.exit(1) } } // Wenn direkt ausgeführt (nicht importiert) if (require.main === module) { main() }