const { PrismaClient } = require('@prisma/client'); const { createDAVClient } = require('tsdav'); const fs = require('fs'); const path = require('path'); const prisma = new PrismaClient(); function loadEnv() { const envPath = path.join(__dirname, '.env'); const envContent = fs.readFileSync(envPath, 'utf-8'); const env = {}; envContent.split('\n').forEach(line => { const match = line.match(/^([^=:#]+)=(.*)$/); if (match) { const key = match[1].trim(); let value = match[2].trim(); if (value.startsWith('"') && value.endsWith('"')) { value = value.slice(1, -1); value = value.replace(/\\"/g, '"'); value = value.replace(/\\\\/g, '\\'); } else if (value.startsWith("'") && value.endsWith("'")) { value = value.slice(1, -1); } env[key] = value; } }); return env; } async function syncBookingToCalendar(client, booking, env) { try { const calendars = await client.fetchCalendars(); if (calendars.length === 0) { throw new Error('No calendars found in Nextcloud'); } // Suche nach "Buchungen" Kalender, sonst verwende ersten let calendar = calendars.find((cal) => cal.displayName?.toLowerCase().includes('buchung') ); if (!calendar) { console.log(` ⚠️ Kein "Buchungen"-Kalender gefunden, verwende: ${calendars[0].displayName}`); calendar = calendars[0]; } const startDate = new Date(booking.eventDate); const endDate = new Date(startDate.getTime() + 4 * 60 * 60 * 1000); const event = { summary: `${booking.customerName} - ${booking.location?.name || 'Unbekannt'}`, description: ` Buchung #${booking.bookingNumber || booking.id} Kunde: ${booking.customerName} E-Mail: ${booking.customerEmail} Telefon: ${booking.customerPhone || 'N/A'} Event-Location: ${booking.eventLocation || booking.eventAddress} Status: ${booking.status} Fotobox: ${booking.photobox?.model || 'Keine Box'} Standort: ${booking.location?.name || 'Unbekannt'} Preis: ${booking.calculatedPrice || 0}€ `.trim(), location: `${booking.eventAddress || ''}, ${booking.eventZip || ''} ${booking.eventCity || ''}`.trim(), start: startDate.toISOString(), end: endDate.toISOString(), uid: `savethemoment-booking-${booking.id}`, }; // Create iCalendar format const icsContent = `BEGIN:VCALENDAR VERSION:2.0 PRODID:-//SaveTheMoment Atlas//EN BEGIN:VEVENT UID:${event.uid} DTSTAMP:${new Date().toISOString().replace(/[-:]/g, '').split('.')[0]}Z DTSTART:${startDate.toISOString().replace(/[-:]/g, '').split('.')[0]}Z DTEND:${endDate.toISOString().replace(/[-:]/g, '').split('.')[0]}Z SUMMARY:${event.summary} DESCRIPTION:${event.description.replace(/\n/g, '\\n')} LOCATION:${event.location} STATUS:CONFIRMED END:VEVENT END:VCALENDAR`; // Check if event already exists const calendarObjects = await client.fetchCalendarObjects({ calendar, }); const existingEvent = calendarObjects.find(obj => obj.data && obj.data.includes(`UID:${event.uid}`) ); if (existingEvent) { // Update existing event await client.updateCalendarObject({ calendarObject: { url: existingEvent.url, data: icsContent, etag: existingEvent.etag, }, }); } else { // Create new event await client.createCalendarObject({ calendar, filename: `${event.uid}.ics`, iCalString: icsContent, }); } return true; } catch (error) { throw error; } } async function syncExistingBookings() { console.log('🔄 Synchronisiere bestehende Buchungen mit Nextcloud...\n'); const env = loadEnv(); const serverUrl = env.NEXTCLOUD_URL; const username = env.NEXTCLOUD_USERNAME; const password = env.NEXTCLOUD_PASSWORD; if (!serverUrl || !username || !password) { console.error('❌ Missing Nextcloud credentials in .env file!'); process.exit(1); } try { console.log('⏳ Verbinde mit Nextcloud...'); const client = await createDAVClient({ serverUrl: `${serverUrl}/remote.php/dav`, credentials: { username, password, }, authMethod: 'Basic', defaultAccountType: 'caldav', }); console.log('✅ Nextcloud-Verbindung hergestellt!\n'); // Hole alle bestätigten Buchungen const bookings = await prisma.booking.findMany({ where: { status: { in: ['RESERVED', 'CONFIRMED'], }, }, include: { location: true, photobox: true, }, orderBy: { eventDate: 'asc', }, }); console.log(`📊 Gefunden: ${bookings.length} Buchungen\n`); if (bookings.length === 0) { console.log('ℹ️ Keine Buchungen zum Synchronisieren gefunden.'); return; } let synced = 0; let failed = 0; for (const booking of bookings) { try { console.log(`📅 Synchronisiere: ${booking.bookingNumber} - ${booking.customerName}`); console.log(` Event: ${new Date(booking.eventDate).toLocaleDateString('de-DE')}`); console.log(` Standort: ${booking.location?.name || 'Unbekannt'}`); await syncBookingToCalendar(client, booking, env); synced++; console.log(` ✅ Erfolgreich!\n`); } catch (error) { failed++; console.error(` ❌ Fehler: ${error.message}\n`); } } console.log('─'.repeat(50)); console.log(`✅ Erfolgreich synchronisiert: ${synced}`); console.log(`❌ Fehlgeschlagen: ${failed}`); console.log(`📊 Gesamt: ${bookings.length}`); console.log('\n🎉 Synchronisation abgeschlossen!'); console.log(' Prüfen Sie Nextcloud → Kalender "Buchungen (Dennis Forte)"'); } catch (error) { console.error('❌ Fehler beim Synchronisieren:', error); throw error; } finally { await prisma.$disconnect(); } } syncExistingBookings() .catch((error) => { console.error('Fatal error:', error); process.exit(1); });