From 45dfc22c605135058f57cf9ab18c56ca86183546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= Date: Fri, 18 Apr 2025 15:05:32 +0200 Subject: [PATCH] Add endpoint for getting upcoming event and adding swagger docs to all endpoints --- app.ts | 1 + src/models/calendar/Calendar.router.ts | 33 + src/models/calendar/events/event.interface.ts | 81 ++ src/models/calendar/events/events.router.ts | 706 ++++++++++++++++++ src/models/calendar/events/events.service.ts | 60 ++ .../calendar/users/session.interface.ts | 44 ++ src/models/calendar/users/user.interface.ts | 35 + src/models/calendar/users/users.router.ts | 280 +++++++ 8 files changed, 1240 insertions(+) diff --git a/app.ts b/app.ts index e6d9b05..9ad8e74 100644 --- a/app.ts +++ b/app.ts @@ -67,6 +67,7 @@ const options = { swaggerDefinition, // Paths to files containing OpenAPI definitions apis: [ + './src/models/**/*.interface.ts', './src/models/**/*.router.ts' ] }; diff --git a/src/models/calendar/Calendar.router.ts b/src/models/calendar/Calendar.router.ts index c7c0e12..36ad5e1 100644 --- a/src/models/calendar/Calendar.router.ts +++ b/src/models/calendar/Calendar.router.ts @@ -16,6 +16,39 @@ calendarRouter.use('/events', eventsRouter); calendarRouter.use('/users', usersRouter); +/** + * @swagger + * /calendar: + * get: + * summary: Calendar API root endpoint + * description: Returns a welcome message for the Nachklang e.V. Calendar API. + * tags: + * - calendar + * responses: + * 200: + * description: Success + * content: + * text/plain: + * schema: + * type: string + * example: Nachklang e.V. Calendar API Endpoint + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ calendarRouter.get('/', async (req: Request, res: Response) => { try { res.status(200).send('Nachklang e.V. Calendar API Endpoint'); diff --git a/src/models/calendar/events/event.interface.ts b/src/models/calendar/events/event.interface.ts index 1f0965d..9135fc9 100644 --- a/src/models/calendar/events/event.interface.ts +++ b/src/models/calendar/events/event.interface.ts @@ -1,3 +1,84 @@ +/** + * @swagger + * components: + * schemas: + * Event: + * type: object + * required: + * - eventId + * - calendarId + * - uuid + * - name + * - description + * - startDateTime + * - endDateTime + * - createdDate + * - location + * - createdById + * - url + * - wholeDay + * properties: + * eventId: + * type: integer + * description: The unique identifier for the event + * example: 123 + * calendarId: + * type: integer + * description: The ID of the calendar this event belongs to + * example: 1 + * uuid: + * type: string + * description: A unique UUID for the event + * example: "550e8400-e29b-41d4-a716-446655440000" + * name: + * type: string + * description: The name/title of the event + * example: "Concert at Musikhochschule" + * description: + * type: string + * description: A detailed description of the event + * example: "Annual concert at the Musikhochschule" + * startDateTime: + * type: string + * format: date-time + * description: The start date and time of the event + * example: "2023-06-15T19:00:00.000Z" + * endDateTime: + * type: string + * format: date-time + * description: The end date and time of the event + * example: "2023-06-15T21:00:00.000Z" + * createdDate: + * type: string + * format: date-time + * description: The date and time when the event was created + * example: "2023-05-01T10:00:00.000Z" + * location: + * type: string + * description: The location of the event + * example: "Musikhochschule, Karlsruhe" + * createdBy: + * type: string + * description: The name of the user who created the event + * example: "John Doe" + * createdById: + * type: integer + * description: The ID of the user who created the event + * example: 456 + * url: + * type: string + * description: A URL with more information about the event + * example: "https://www.nachklang.art/events/concert" + * wholeDay: + * type: boolean + * description: Whether the event lasts the whole day + * example: false + * status: + * type: string + * description: The status of the event + * enum: [PUBLIC, PRIVATE, DRAFT, DELETED] + * example: "PUBLIC" + */ export interface Event { eventId: number; calendarId: number; diff --git a/src/models/calendar/events/events.router.ts b/src/models/calendar/events/events.router.ts index 7768071..523a214 100644 --- a/src/models/calendar/events/events.router.ts +++ b/src/models/calendar/events/events.router.ts @@ -33,6 +33,77 @@ export const calendarNames = new Map([ * Controller Definitions */ +/** + * @swagger + * /calendar/events/{calendar}/json: + * get: + * summary: Get all events from a specific calendar in JSON format + * description: Returns all events from the specified calendar in JSON format. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: calendar + * required: true + * schema: + * type: string + * enum: [public, members, choir, management] + * description: The name of the calendar to get events from + * - in: query + * name: sessionId + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * schema: + * type: string + * description: Session key for authentication + * - in: query + * name: password + * schema: + * type: string + * description: Password for calendar access (if not using session authentication) + * responses: + * 200: + * description: Success + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Event' + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Please state the name of the calendar you want events from. + * 403: + * description: Forbidden - no access to the calendar + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Internal Server Error. Try again later. + */ eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => { try { // Get request params @@ -80,6 +151,214 @@ eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/events/{calendar}/json/next: + * get: + * summary: Get the next upcoming event from a calendar + * description: Returns the next upcoming event from the specified calendar. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: calendar + * required: true + * schema: + * type: string + * enum: [public, members, choir, management] + * description: The name of the calendar to get the next event from + * - in: query + * name: sessionId + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * schema: + * type: string + * description: Session key for authentication + * - in: query + * name: password + * schema: + * type: string + * description: Password for calendar access (if not using session authentication) + * responses: + * 200: + * description: Success + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Event' + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Please state the name of the calendar you want events from. + * 403: + * description: Forbidden - no access to the calendar + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 404: + * description: Not found - no upcoming events + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: No upcoming events found. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ +eventsRouter.get('/:calendar/json/next', async (req: Request, res: Response) => { + try { + // Get request params + let calendarName: string = req.params.calendar as string ?? ''; + let sessionId: string = req.query.sessionId as string ?? ''; + let sessionKey: string = req.query.sessionKey as string ?? ''; + let password: string = req.query.password as string ?? ''; + let ip: string = req.socket.remoteAddress ?? ''; + + if (calendarName.length < 1) { + res.status(400).send({'message': 'Please state the name of the calendar you want events from.'}); + return; + } + + if (!Array.from(calendarNames.keys()).includes(calendarName)) { + res.status(400).send({'message': 'Unknown calendar.'}); + return; + } + + let calendarId: number = calendarNames.get(calendarName)!.id; + + if (! await CredentialService.hasAccess(calendarName, sessionId, sessionKey, password, ip)) { + res.status(403).send({'message': 'You do not have access to the specified calendar.'}); + return; + } + + // Get next upcoming event + let event = await EventService.getNextUpcomingEvent(calendarId); + + if (event === null) { + res.status(404).send({'message': 'No upcoming events found.'}); + return; + } + + // Send the event back + res.status(200).send(event); + } catch (e: any) { + let errorGuid = Guid.create().toString(); + logger.error('Error handling a request: ' + e.message, {reference: errorGuid}); + res.status(500).send({ + 'status': 'PROCESSING_ERROR', + 'message': 'Internal Server Error. Try again later.', + 'reference': errorGuid + }); + } +}); + +/** + * @swagger + * /calendar/events/{calendar}/ical: + * get: + * summary: Get all events from a specific calendar in iCal format + * description: Returns all events from the specified calendar in iCal format for calendar applications. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: calendar + * required: true + * schema: + * type: string + * enum: [public, members, choir, management] + * description: The name of the calendar to get events from + * - in: query + * name: sessionId + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * schema: + * type: string + * description: Session key for authentication + * - in: query + * name: password + * schema: + * type: string + * description: Password for calendar access (if not using session authentication) + * responses: + * 200: + * description: Success - returns iCal file + * content: + * text/calendar: + * schema: + * type: string + * format: binary + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Please state the name of the calendar you want events from. + * 403: + * description: Forbidden - no access to the calendar + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => { try { // Get request params @@ -127,6 +406,120 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/events: + * post: + * summary: Create a new event + * description: Creates a new event in the specified calendar. Authentication required. + * tags: + * - calendar + * parameters: + * - in: query + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * required: true + * schema: + * type: string + * description: Session key for authentication + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - calendarId + * - name + * - startDateTime + * - endDateTime + * properties: + * calendarId: + * type: integer + * example: 1 + * name: + * type: string + * example: "Concert at Musikhochschule" + * description: + * type: string + * example: "Annual concert at the Musikhochschule" + * startDateTime: + * type: string + * format: date-time + * example: "2023-06-15T19:00:00.000Z" + * endDateTime: + * type: string + * format: date-time + * example: "2023-06-15T21:00:00.000Z" + * location: + * type: string + * example: "Musikhochschule, Karlsruhe" + * url: + * type: string + * example: "https://www.nachklang.art/events/concert" + * wholeDay: + * type: boolean + * example: false + * status: + * type: string + * enum: [PUBLIC, PRIVATE, DRAFT, DELETED] + * example: "PUBLIC" + * responses: + * 201: + * description: Event created successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Event with id 123 was created successfully. + * eventId: + * type: integer + * example: 123 + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Required parameters missing + * 403: + * description: Forbidden - no access to create events + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ eventsRouter.post('/', async (req: Request, res: Response) => { try { // Get params @@ -184,6 +577,126 @@ eventsRouter.post('/', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/events/{eventId}: + * put: + * summary: Update an existing event + * description: Updates an existing event with the provided data. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: eventId + * required: true + * schema: + * type: integer + * description: The ID of the event to update + * - in: query + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * required: true + * schema: + * type: string + * description: Session key for authentication + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - calendarId + * - name + * - startDateTime + * - endDateTime + * properties: + * calendarId: + * type: integer + * example: 1 + * name: + * type: string + * example: "Updated Concert at Musikhochschule" + * description: + * type: string + * example: "Updated annual concert at the Musikhochschule" + * startDateTime: + * type: string + * format: date-time + * example: "2023-06-15T19:00:00.000Z" + * endDateTime: + * type: string + * format: date-time + * example: "2023-06-15T21:00:00.000Z" + * location: + * type: string + * example: "Musikhochschule, Karlsruhe" + * createdBy: + * type: string + * example: "John Doe" + * url: + * type: string + * example: "https://www.nachklang.art/events/concert" + * wholeDay: + * type: boolean + * example: false + * status: + * type: string + * enum: [PUBLIC, PRIVATE, DRAFT, DELETED] + * example: "PUBLIC" + * responses: + * 200: + * description: Event updated successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Event was successfully updated + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Required parameters missing + * 403: + * description: Forbidden - no access to update events + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ eventsRouter.put('/:eventId', async (req: Request, res: Response) => { try { // Get params @@ -246,6 +759,124 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/events/move/{eventId}: + * put: + * summary: Move an event to a different calendar + * description: Moves an existing event to a different calendar. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: eventId + * required: true + * schema: + * type: integer + * description: The ID of the event to move + * - in: query + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * required: true + * schema: + * type: string + * description: Session key for authentication + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - calendarId + * properties: + * calendarId: + * type: integer + * example: 2 + * description: The ID of the calendar to move the event to + * name: + * type: string + * example: "Concert at Musikhochschule" + * description: + * type: string + * example: "Annual concert at the Musikhochschule" + * startDateTime: + * type: string + * format: date-time + * example: "2023-06-15T19:00:00.000Z" + * endDateTime: + * type: string + * format: date-time + * example: "2023-06-15T21:00:00.000Z" + * location: + * type: string + * example: "Musikhochschule, Karlsruhe" + * createdBy: + * type: string + * example: "John Doe" + * url: + * type: string + * example: "https://www.nachklang.art/events/concert" + * wholeDay: + * type: boolean + * example: false + * status: + * type: string + * enum: [PUBLIC, PRIVATE, DRAFT, DELETED] + * example: "PUBLIC" + * responses: + * 200: + * description: Event moved successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Event was successfully moved + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Required parameters missing + * 403: + * description: Forbidden - no access to move events + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ eventsRouter.put('/move/:eventId', async (req: Request, res: Response) => { try { // Get params @@ -303,6 +934,81 @@ eventsRouter.put('/move/:eventId', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/events/{eventId}: + * delete: + * summary: Delete an event + * description: Deletes an existing event. Authentication required. + * tags: + * - calendar + * parameters: + * - in: path + * name: eventId + * required: true + * schema: + * type: integer + * description: The ID of the event to delete + * - in: query + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID for authentication + * - in: query + * name: sessionKey + * required: true + * schema: + * type: string + * description: Session key for authentication + * responses: + * 200: + * description: Event deleted successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Event was successfully deleted + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Required parameters missing + * 403: + * description: Forbidden - no access to delete events + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: You do not have access to the specified calendar. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ eventsRouter.delete('/:eventId', async (req: Request, res: Response) => { try { // Get params diff --git a/src/models/calendar/events/events.service.ts b/src/models/calendar/events/events.service.ts index cb6c4ba..57a2889 100644 --- a/src/models/calendar/events/events.service.ts +++ b/src/models/calendar/events/events.service.ts @@ -211,3 +211,63 @@ export const moveEvent = async (event: Event): Promise => { await conn.end(); } } + +/** + * Returns the next upcoming event for the given calendar + * @param calendarId The calendar Id + */ +export const getNextUpcomingEvent = async (calendarId: number): Promise => { + let conn = await NachklangCalendarDB.getConnection(); + try { + const calendarQuery = 'SELECT calendar_id, includes_calendars FROM calendars WHERE calendar_id = ?'; + const calendarRes = await conn.query(calendarQuery, calendarId); + let calendarsToFetch: number[] = [calendarId]; + for(let row of calendarRes) { + let includes: number[] = JSON.parse(row.includes_calendars); + calendarsToFetch = [...calendarsToFetch, ...includes]; + } + + const now = new Date(); + const eventsQuery = ` + SELECT e.calendar_id, e.uuid, e.created_date, e.created_by_id, u.full_name, v.* FROM events e + INNER JOIN ( + SELECT event_id, MAX(event_version_id) AS latest_version + FROM event_versions + GROUP BY event_id + ) latest_versions + ON e.event_id = latest_versions.event_id + INNER JOIN event_versions v + ON v.event_id = latest_versions.event_id AND v.event_version_id = latest_versions.latest_version + LEFT OUTER JOIN users u ON u.user_id = e.created_by_id + WHERE e.calendar_id IN (?) AND v.status = 'PUBLIC' AND v.start_datetime > ? + ORDER BY v.start_datetime ASC + LIMIT 1`; + const eventsRes = await conn.query(eventsQuery, [calendarsToFetch, now]); + + if (eventsRes.length === 0) { + return null; + } + + const row = eventsRes[0]; + return { + eventId: row.event_id, + calendarId: row.calendar_id, + uuid: row.uuid, + name: row.name, + description: row.description, + startDateTime: row.start_datetime, + endDateTime: row.end_datetime, + createdDate: row.created_date, + location: row.location, + createdBy: row.full_name, + createdById: row.created_by_id, + url: row.url, + wholeDay: row.whole_day + } as Event; + } catch (err) { + throw err; + } finally { + // Return connection + await conn.end(); + } +} diff --git a/src/models/calendar/users/session.interface.ts b/src/models/calendar/users/session.interface.ts index eeecf2f..cf90a6a 100644 --- a/src/models/calendar/users/session.interface.ts +++ b/src/models/calendar/users/session.interface.ts @@ -1,3 +1,47 @@ +/** + * @swagger + * components: + * schemas: + * Session: + * type: object + * required: + * - sessionId + * - userId + * - sessionKey + * - sessionKeyHash + * - lastIP + * properties: + * sessionId: + * type: integer + * description: The unique identifier for the session + * example: 789 + * userId: + * type: integer + * description: The ID of the user this session belongs to + * example: 456 + * sessionKey: + * type: string + * description: The session key used for authentication + * example: "abc123def456" + * sessionKeyHash: + * type: string + * description: The hashed session key (not returned in API responses) + * example: "$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG" + * createdDate: + * type: string + * format: date-time + * description: The date and time when the session was created + * example: "2023-05-01T10:00:00.000Z" + * validUntil: + * type: string + * format: date-time + * description: The date and time until when the session is valid + * example: "2023-05-08T10:00:00.000Z" + * lastIP: + * type: string + * description: The last IP address used with this session + * example: "192.168.1.1" + */ export interface Session { sessionId: number; userId: number; diff --git a/src/models/calendar/users/user.interface.ts b/src/models/calendar/users/user.interface.ts index 4216773..a74a20c 100644 --- a/src/models/calendar/users/user.interface.ts +++ b/src/models/calendar/users/user.interface.ts @@ -1,3 +1,38 @@ +/** + * @swagger + * components: + * schemas: + * User: + * type: object + * required: + * - userId + * - fullName + * - passwordHash + * - email + * - isActive + * properties: + * userId: + * type: integer + * description: The unique identifier for the user + * example: 456 + * fullName: + * type: string + * description: The full name of the user + * example: "John Doe" + * passwordHash: + * type: string + * description: The hashed password of the user (not returned in API responses) + * example: "$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG" + * email: + * type: string + * format: email + * description: The email address of the user + * example: "john.doe@nachklang.art" + * isActive: + * type: boolean + * description: Whether the user account is active + * example: true + */ export interface User { userId: number; fullName: string; diff --git a/src/models/calendar/users/users.router.ts b/src/models/calendar/users/users.router.ts index 2edd92d..ff90e96 100644 --- a/src/models/calendar/users/users.router.ts +++ b/src/models/calendar/users/users.router.ts @@ -20,6 +20,78 @@ export const usersRouter = express.Router(); * Controller Definitions */ +/** + * @swagger + * /calendar/users/register: + * post: + * summary: Register a new user + * description: Creates a new user account with the provided email, password, and full name. Only accepts official Nachklang email addresses. + * tags: + * - calendar + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - email + * - password + * - fullName + * properties: + * email: + * type: string + * format: email + * example: john.doe@nachklang.art + * description: Must be an official Nachklang email address + * password: + * type: string + * format: password + * example: securePassword123 + * fullName: + * type: string + * example: John Doe + * responses: + * 201: + * description: User registered successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * sessionId: + * type: integer + * example: 123 + * sessionKey: + * type: string + * example: abc123def456 + * 400: + * description: Bad request - missing or invalid parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Missing parameters + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ // POST users/register usersRouter.post('/register', async (req: Request, res: Response) => { try { @@ -60,6 +132,71 @@ usersRouter.post('/register', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/users/activate: + * get: + * summary: Activate a user account + * description: Activates a user account using the provided user ID and activation token. + * tags: + * - calendar + * parameters: + * - in: query + * name: id + * required: true + * schema: + * type: integer + * description: The ID of the user to activate + * - in: query + * name: token + * required: true + * schema: + * type: string + * description: The activation token sent to the user's email + * responses: + * 200: + * description: User activated successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: OK + * message: + * type: string + * example: User activated + * 400: + * description: Bad request - missing parameters or activation failed + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Error activating user. Please contact your administrator. + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ // GET /users/activate usersRouter.get('/activate', async (req: Request, res: Response) => { try { @@ -95,6 +232,89 @@ usersRouter.get('/activate', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/users/login: + * post: + * summary: Login a user + * description: Authenticates a user with the provided email and password and returns a session. + * tags: + * - calendar + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - email + * - password + * properties: + * email: + * type: string + * format: email + * example: john.doe@nachklang.art + * password: + * type: string + * format: password + * example: securePassword123 + * responses: + * 200: + * description: Login successful + * content: + * application/json: + * schema: + * type: object + * properties: + * sessionId: + * type: integer + * example: 123 + * sessionKey: + * type: string + * example: abc123def456 + * 400: + * description: Bad request - missing parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Missing parameters + * 401: + * description: Unauthorized - invalid credentials + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Wrong username and / or password + * sessionId: + * type: integer + * example: -1 + * sessionKey: + * type: string + * example: "" + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ // POST users/login usersRouter.post('/login', async (req: Request, res: Response) => { try { @@ -133,6 +353,66 @@ usersRouter.post('/login', async (req: Request, res: Response) => { } }); +/** + * @swagger + * /calendar/users/checkSessionValid: + * post: + * summary: Check if a session is valid + * description: Checks if the provided session is valid and returns the user information if it is. + * tags: + * - calendar + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - sessionId + * - sessionKey + * properties: + * sessionId: + * type: integer + * example: 123 + * sessionKey: + * type: string + * example: abc123def456 + * responses: + * 200: + * description: Session is valid + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 401: + * description: Unauthorized - invalid session + * content: + * application/json: + * schema: + * type: object + * properties: + * messages: + * type: array + * items: + * type: string + * example: ["Invalid session"] + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: PROCESSING_ERROR + * message: + * type: string + * example: Internal Server Error. Try again later. + * reference: + * type: string + * example: 6ec1361c-4175-4e81-b2ef-a0792a9a1dc3 + */ // POST users/checkSessionValid usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => { try {