Add endpoint for getting upcoming event and adding swagger docs to all endpoints
All checks were successful
Jenkins Production Deployment
All checks were successful
Jenkins Production Deployment
This commit is contained in:
parent
a38fb20e5a
commit
45dfc22c60
1
app.ts
1
app.ts
|
@ -67,6 +67,7 @@ const options = {
|
|||
swaggerDefinition,
|
||||
// Paths to files containing OpenAPI definitions
|
||||
apis: [
|
||||
'./src/models/**/*.interface.ts',
|
||||
'./src/models/**/*.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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -33,6 +33,77 @@ export const calendarNames = new Map<string, any>([
|
|||
* 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
|
||||
|
|
|
@ -211,3 +211,63 @@ export const moveEvent = async (event: Event): Promise<boolean> => {
|
|||
await conn.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next upcoming event for the given calendar
|
||||
* @param calendarId The calendar Id
|
||||
*/
|
||||
export const getNextUpcomingEvent = async (calendarId: number): Promise<Event | null> => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user