From dc65b49219c56c927b1911d6443fb14ffc709f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= Date: Sun, 7 Sep 2025 18:15:15 +0200 Subject: [PATCH] Add possibility to add birthdays + repeating events to the API --- src/models/calendar/events/credentials.service.ts | 2 ++ src/models/calendar/events/event.interface.ts | 1 + src/models/calendar/events/events.router.ts | 7 ++++++- src/models/calendar/events/events.service.ts | 15 +++++++++------ .../calendar/events/icalgenerator.service.ts | 3 +++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/models/calendar/events/credentials.service.ts b/src/models/calendar/events/credentials.service.ts index 2f5eda9..26e07e6 100644 --- a/src/models/calendar/events/credentials.service.ts +++ b/src/models/calendar/events/credentials.service.ts @@ -65,6 +65,8 @@ export const hasAccess = async (calendarName: string, sessionId: string, session return await checkChoirPrivileges(sessionId, sessionKey, password, ip); case 'management': return await checkManagementPrivileges(sessionId, sessionKey, password, ip); + case 'birthdays': + return await checkChoirPrivileges(sessionId, sessionKey, password, ip); default: return false; } diff --git a/src/models/calendar/events/event.interface.ts b/src/models/calendar/events/event.interface.ts index 1151a4f..0a9abf5 100644 --- a/src/models/calendar/events/event.interface.ts +++ b/src/models/calendar/events/event.interface.ts @@ -108,5 +108,6 @@ export interface Event { lastModifiedById?: number; url: string; wholeDay: boolean; + repeatFrequency: string; status?: string; } diff --git a/src/models/calendar/events/events.router.ts b/src/models/calendar/events/events.router.ts index 523a214..2472276 100644 --- a/src/models/calendar/events/events.router.ts +++ b/src/models/calendar/events/events.router.ts @@ -25,7 +25,8 @@ export const calendarNames = new Map([ ['public', {id: 1, name: 'Nachklang_calendar'}], ['members', {id: 2, name: 'Nachklang_internal_calendar'}], ['choir', {id: 4, name: 'Nachklang_choir_calendar'}], - ['management', {id: 3, name: 'Nachklang_management_calendar'}] + ['management', {id: 3, name: 'Nachklang_management_calendar'}], + ['birthdays', {id: 5, name: 'Nachklang_birthday_calendar'}] ]); @@ -557,6 +558,7 @@ eventsRouter.post('/', async (req: Request, res: Response) => { createdById: user.userId ?? -1, url: req.body.url ?? '', wholeDay: req.body.wholeDay ?? false, + repeatFrequency: req.body.repeatFrequency ?? '', status: req.body.status ?? 'PUBLIC' }; @@ -736,6 +738,7 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => { createdById: user.userId ?? -1, url: req.body.url ?? '', wholeDay: req.body.wholeDay ?? false, + repeatFrequency: req.body.repeatFrequency ?? '', status: req.body.status ?? 'PUBLIC' }; @@ -913,6 +916,7 @@ eventsRouter.put('/move/:eventId', async (req: Request, res: Response) => { createdById: user.userId ?? -1, url: req.body.url ?? '', wholeDay: req.body.wholeDay ?? false, + repeatFrequency: req.body.repeatFrequency ?? '', status: req.body.status ?? 'PUBLIC' }; @@ -1044,6 +1048,7 @@ eventsRouter.delete('/:eventId', async (req: Request, res: Response) => { createdById: user.userId ?? -1, url: '', wholeDay: false, + repeatFrequency: '', status: 'DELETED' }; diff --git a/src/models/calendar/events/events.service.ts b/src/models/calendar/events/events.service.ts index f262b5e..2753b1b 100644 --- a/src/models/calendar/events/events.service.ts +++ b/src/models/calendar/events/events.service.ts @@ -55,7 +55,8 @@ export const getAllEvents = async (calendarId: number): Promise => { lastModifiedBy: row.last_modified_by_name, lastModifiedById: row.version_created_by_id, url: row.url, - wholeDay: row.whole_day + wholeDay: row.whole_day, + repeatFrequency: row.repeat_frequency }); } @@ -111,6 +112,7 @@ export const getAllEventsAdmin = async (calendarId: number): Promise => lastModifiedById: row.version_created_by_id, url: row.url, wholeDay: row.whole_day, + repeatFrequency: row.repeat_frequency, status: row.status }); } @@ -135,8 +137,8 @@ export const createEvent = async (event: Event): Promise => { const eventsQuery = 'INSERT INTO events (calendar_id, uuid, created_by_id) VALUES (?,?,?) RETURNING event_id'; const eventsRes = await conn.execute(eventsQuery, [event.calendarId, eventUUID, event.createdById]); - const versionQuery = 'INSERT INTO event_versions (event_id, name, description, start_datetime, end_datetime, whole_day, location, url, status, version_created_by_id) VALUES (?,?,?,?,?,?,?,?,?,?);' - const versionRes = await conn.execute(versionQuery, [eventsRes[0].event_id, event.name, event.description, event.startDateTime, event.endDateTime, event.wholeDay, event.location, event.url, event.status, event.createdById]); + const versionQuery = 'INSERT INTO event_versions (event_id, name, description, start_datetime, end_datetime, whole_day, repeat_frequency, location, url, status, version_created_by_id) VALUES (?,?,?,?,?,?,?,?,?,?,?);' + const versionRes = await conn.execute(versionQuery, [eventsRes[0].event_id, event.name, event.description, event.startDateTime, event.endDateTime, event.wholeDay, event.repeatFrequency, event.location, event.url, event.status, event.createdById]); await conn.commit(); @@ -158,8 +160,8 @@ export const createEvent = async (event: Event): Promise => { export const updateEvent = async (event: Event): Promise => { let conn = await NachklangCalendarDB.getConnection(); try { - const versionQuery = 'INSERT INTO event_versions (event_id, name, description, start_datetime, end_datetime, whole_day, location, url, status, version_created_by_id) VALUES (?,?,?,?,?,?,?,?,?,?);' - const versionRes = await conn.execute(versionQuery, [event.eventId, event.name, event.description, event.startDateTime, event.endDateTime, event.wholeDay, event.location, event.url, event.status, event.createdById]); + const versionQuery = 'INSERT INTO event_versions (event_id, name, description, start_datetime, end_datetime, whole_day, repeat_frequency, location, url, status, version_created_by_id) VALUES (?,?,?,?,?,?,?,?,?,?,?);' + const versionRes = await conn.execute(versionQuery, [event.eventId, event.name, event.description, event.startDateTime, event.endDateTime, event.wholeDay, event.repeatFrequency, event.location, event.url, event.status, event.createdById]); await conn.commit(); @@ -274,7 +276,8 @@ export const getNextUpcomingEvent = async (calendarId: number): Promise { returnString += 'DTSTART;TZID=Europe/Berlin:' + icalevent.start; returnString += 'DTEND;TZID=Europe/Berlin:' + icalevent.end; } + if(!isNullOrBlank(icalevent.repeatFrequency)) returnString += 'RRULE:FREQ=' + icalevent.repeatFrequency; returnString += 'SUMMARY:' + icalevent.summary; if(!isNullOrBlank(icalevent.description)) returnString += 'DESCRIPTION:' + icalevent.description; if(!isNullOrBlank(icalevent.location)) returnString += 'LOCATION:' + icalevent.location; @@ -126,6 +127,7 @@ const createIcalEvent = (event: Event): iCalEvent => { organizer: event.createdBy + '\n', start: formatDate(event.startDateTime, event.wholeDay) + '\n', end: formatDate(event.endDateTime, event.wholeDay, true) + '\n', + repeatFrequency: event.repeatFrequency ? event.repeatFrequency + '\n' : '', summary: event.name + '\n', description: description, location: location, @@ -178,6 +180,7 @@ export interface iCalEvent { location: string; url: string; wholeDay: boolean; + repeatFrequency: string; footer: string; }