import { createDAVClient } from 'tsdav'; export interface CalendarEvent { uid: string; summary: string; description?: string; location?: string; startDate: Date; endDate: Date; status?: string; } export class NextcloudCalendarService { private client: any; private initialized: boolean = false; async initialize() { if (this.initialized) return; const serverUrl = process.env.NEXTCLOUD_URL; const username = process.env.NEXTCLOUD_USERNAME; const password = process.env.NEXTCLOUD_PASSWORD; if (!serverUrl || !username || !password) { throw new Error('Nextcloud credentials not configured'); } try { this.client = await createDAVClient({ serverUrl: `${serverUrl}/remote.php/dav`, credentials: { username, password, }, authMethod: 'Basic', defaultAccountType: 'caldav', }); this.initialized = true; console.log('✅ Nextcloud CalDAV client initialized'); } catch (error) { console.error('❌ Failed to initialize Nextcloud CalDAV client:', error); throw error; } } async getCalendars() { await this.initialize(); try { const calendars = await this.client.fetchCalendars(); return calendars; } catch (error) { console.error('Error fetching calendars:', error); throw error; } } async createEvent(calendarUrl: string, event: CalendarEvent) { await this.initialize(); const icsContent = this.generateICS(event); try { const result = await this.client.createCalendarObject({ calendar: { url: calendarUrl }, filename: `${event.uid}.ics`, iCalString: icsContent, }); console.log('✅ Event created in Nextcloud:', event.summary); return result; } catch (error) { console.error('Error creating event:', error); throw error; } } async updateEvent(calendarUrl: string, event: CalendarEvent) { await this.initialize(); const icsContent = this.generateICS(event); try { const result = await this.client.updateCalendarObject({ calendar: { url: calendarUrl }, filename: `${event.uid}.ics`, iCalString: icsContent, }); console.log('✅ Event updated in Nextcloud:', event.summary); return result; } catch (error) { console.error('Error updating event:', error); throw error; } } async deleteEvent(calendarUrl: string, eventUid: string) { await this.initialize(); try { await this.client.deleteCalendarObject({ calendar: { url: calendarUrl }, filename: `${eventUid}.ics`, }); console.log('✅ Event deleted from Nextcloud:', eventUid); } catch (error) { console.error('Error deleting event:', error); throw error; } } async syncBookingToCalendar(booking: any) { await this.initialize(); const calendars = await this.getCalendars(); if (calendars.length === 0) { throw new Error('No calendars found in Nextcloud'); } const calendar = calendars[0]; const event: CalendarEvent = { uid: `savethemoment-booking-${booking.id}`, summary: `${booking.customerName} - ${booking.location?.name || 'Unbekannt'}`, description: ` Buchung #${booking.id} Kunde: ${booking.customerName} E-Mail: ${booking.customerEmail} Telefon: ${booking.customerPhone || 'N/A'} Event-Typ: ${booking.eventType} Status: ${booking.status} Fotobox: ${booking.photobox?.name || 'Keine Box'} Standort: ${booking.location?.name || 'Unbekannt'} `.trim(), location: booking.location?.address || '', startDate: new Date(booking.eventDate), endDate: new Date(new Date(booking.eventDate).getTime() + 4 * 60 * 60 * 1000), status: booking.status, }; try { await this.createEvent(calendar.url, event); return event; } catch (error) { if (error.message?.includes('already exists') || error.response?.status === 412) { await this.updateEvent(calendar.url, event); return event; } throw error; } } async removeBookingFromCalendar(bookingId: string) { await this.initialize(); const calendars = await this.getCalendars(); if (calendars.length === 0) { throw new Error('No calendars found in Nextcloud'); } const calendar = calendars[0]; const eventUid = `savethemoment-booking-${bookingId}`; try { await this.deleteEvent(calendar.url, eventUid); } catch (error) { console.error('Error removing booking from calendar:', error); } } private generateICS(event: CalendarEvent): string { const formatDate = (date: Date) => { return date.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'; }; const now = new Date(); const dtstamp = formatDate(now); const dtstart = formatDate(event.startDate); const dtend = formatDate(event.endDate); return `BEGIN:VCALENDAR VERSION:2.0 PRODID:-//SaveTheMoment Atlas//DE BEGIN:VEVENT UID:${event.uid} DTSTAMP:${dtstamp} DTSTART:${dtstart} DTEND:${dtend} SUMMARY:${event.summary} DESCRIPTION:${event.description || ''} LOCATION:${event.location || ''} STATUS:${event.status === 'CONFIRMED' ? 'CONFIRMED' : 'TENTATIVE'} END:VEVENT END:VCALENDAR`; } } export const nextcloudCalendar = new NextcloudCalendarService();