3c892d02ed
Jenkins Production Deployment
Reviewed-on: #13 Co-authored-by: Patrick Müller <mail@pmueller.me> Co-committed-by: Patrick Müller <mail@pmueller.me>
233 lines
7.8 KiB
TypeScript
233 lines
7.8 KiB
TypeScript
/**
|
|
* Required External Modules and Interfaces
|
|
*/
|
|
import express, {Request, Response} from 'express';
|
|
import * as ReportsAdminService from './reports.admin.service.js';
|
|
import * as CsvService from './csv.service.js';
|
|
import * as EventsAdminService from './events.admin.service.js';
|
|
import {sendServerError} from '../feedback.errors.js';
|
|
|
|
/**
|
|
* Router Definition
|
|
*/
|
|
export const reportsAdminRouter = express.Router();
|
|
|
|
/**
|
|
* @swagger
|
|
* /feedback/admin/events/{eventId}/report:
|
|
* get:
|
|
* summary: Aggregated feedback report for one event
|
|
* description: Song-pick vote counts, song-rating averages, capped free-text list, guest book count, and newsletter sync counts.
|
|
* tags: [feedback-admin]
|
|
* security:
|
|
* - AdminSessionCookie: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: eventId
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* responses:
|
|
* 200:
|
|
* description: Success
|
|
* 404:
|
|
* description: Unknown event
|
|
* 401:
|
|
* description: Unauthorized
|
|
* 403:
|
|
* description: Signed in without the permission for this app, or account disabled
|
|
*/
|
|
reportsAdminRouter.get('/:eventId/report', async (req: Request, res: Response) => {
|
|
try {
|
|
const report = await ReportsAdminService.getReport(Number(req.params.eventId));
|
|
if (!report) {
|
|
res.status(404).send({status: 'NOT_FOUND'});
|
|
return;
|
|
}
|
|
res.status(200).send(report);
|
|
} catch (e: any) {
|
|
sendServerError(res, e);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /feedback/admin/events/{eventId}/guestbook:
|
|
* get:
|
|
* summary: Guest Book entries for one event
|
|
* description: Newest first, paginated.
|
|
* tags: [feedback-admin]
|
|
* security:
|
|
* - AdminSessionCookie: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: eventId
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: page
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: pageSize
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: search
|
|
* description: Filters entries whose name or message contains this text (case-insensitive).
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: Success
|
|
* 401:
|
|
* description: Unauthorized
|
|
* 403:
|
|
* description: Signed in without the permission for this app, or account disabled
|
|
*/
|
|
reportsAdminRouter.get('/:eventId/guestbook', async (req: Request, res: Response) => {
|
|
try {
|
|
const page = Math.max(1, Number(req.query.page) || 1);
|
|
const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 50));
|
|
const search = typeof req.query.search === 'string' ? req.query.search : undefined;
|
|
const result = await ReportsAdminService.getGuestBookEntries(Number(req.params.eventId), page, pageSize, search);
|
|
res.status(200).send(result);
|
|
} catch (e: any) {
|
|
sendServerError(res, e);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /feedback/admin/events/{eventId}/newsletter:
|
|
* get:
|
|
* summary: Newsletter signups for one event
|
|
* description: Includes sync_status, so failures can be handled manually. Newest first, paginated.
|
|
* tags: [feedback-admin]
|
|
* security:
|
|
* - AdminSessionCookie: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: eventId
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: page
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: pageSize
|
|
* schema:
|
|
* type: integer
|
|
* - in: query
|
|
* name: search
|
|
* description: Filters entries whose first name, last name, or email contains this text (case-insensitive).
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: Success
|
|
* 401:
|
|
* description: Unauthorized
|
|
* 403:
|
|
* description: Signed in without the permission for this app, or account disabled
|
|
*/
|
|
reportsAdminRouter.get('/:eventId/newsletter', async (req: Request, res: Response) => {
|
|
try {
|
|
const page = Math.max(1, Number(req.query.page) || 1);
|
|
const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 50));
|
|
const search = typeof req.query.search === 'string' ? req.query.search : undefined;
|
|
const result = await ReportsAdminService.getNewsletterSignups(Number(req.params.eventId), page, pageSize, search);
|
|
res.status(200).send(result);
|
|
} catch (e: any) {
|
|
sendServerError(res, e);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /feedback/admin/events/{eventId}/export/responses.csv:
|
|
* get:
|
|
* summary: CSV export of all answers for one event
|
|
* description: Long format, one row per answer. UTF-8 BOM, `;` separator, RFC 4180 escaping, formula-injection guard.
|
|
* tags: [feedback-admin]
|
|
* security:
|
|
* - AdminSessionCookie: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: eventId
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* responses:
|
|
* 200:
|
|
* description: CSV file
|
|
* content:
|
|
* text/csv: {}
|
|
* 401:
|
|
* description: Unauthorized
|
|
* 403:
|
|
* description: Signed in without the permission for this app, or account disabled
|
|
*/
|
|
reportsAdminRouter.get('/:eventId/export/responses.csv', async (req: Request, res: Response) => {
|
|
try {
|
|
const eventId = Number(req.params.eventId);
|
|
const event = await EventsAdminService.getEventAdmin(eventId);
|
|
if (!event) {
|
|
res.status(404).send({status: 'NOT_FOUND'});
|
|
return;
|
|
}
|
|
const csv = await CsvService.buildResponsesCsv(eventId);
|
|
res.status(200)
|
|
.set('Content-Type', 'text/csv; charset=utf-8')
|
|
.set('Content-Disposition', `attachment; filename="nachklang-feedback-${event.slug}.csv"`)
|
|
.send(csv);
|
|
} catch (e: any) {
|
|
sendServerError(res, e);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /feedback/admin/events/{eventId}/export/guestbook.csv:
|
|
* get:
|
|
* summary: CSV export of Guest Book entries for one event
|
|
* tags: [feedback-admin]
|
|
* security:
|
|
* - AdminSessionCookie: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: eventId
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* responses:
|
|
* 200:
|
|
* description: CSV file
|
|
* content:
|
|
* text/csv: {}
|
|
* 401:
|
|
* description: Unauthorized
|
|
* 403:
|
|
* description: Signed in without the permission for this app, or account disabled
|
|
*/
|
|
reportsAdminRouter.get('/:eventId/export/guestbook.csv', async (req: Request, res: Response) => {
|
|
try {
|
|
const eventId = Number(req.params.eventId);
|
|
const event = await EventsAdminService.getEventAdmin(eventId);
|
|
if (!event) {
|
|
res.status(404).send({status: 'NOT_FOUND'});
|
|
return;
|
|
}
|
|
const csv = await CsvService.buildGuestBookCsv(eventId);
|
|
res.status(200)
|
|
.set('Content-Type', 'text/csv; charset=utf-8')
|
|
.set('Content-Disposition', `attachment; filename="nachklang-feedback-guestbook-${event.slug}.csv"`)
|
|
.send(csv);
|
|
} catch (e: any) {
|
|
sendServerError(res, e);
|
|
}
|
|
});
|