import { HttpStatusCode } from '@peertube/peertube-models' import { isIdValid } from '@server/helpers/custom-validators/misc.js' import { EventRegistrationModel } from '@server/models/video/event-registration.js' import { EventModel } from '@server/models/video/event.js' import { VideoChannelModel } from '@server/models/video/video-channel.js' import express from 'express' import { body, param } from 'express-validator' import { areValidationErrors } from './shared/utils.js' const phoneRegexp = /^[0-9+()\-\s]{7,20}$/ const requiredRegistrationFields = [ body('firstName') .trim() .notEmpty() .withMessage('First name is required'), body('lastName') .trim() .notEmpty() .withMessage('Last name is required'), body('email') .trim() .isEmail() .withMessage('Email must be valid') .bail() .normalizeEmail(), body('phone') .trim() .matches(phoneRegexp) .withMessage('Phone must be valid'), body('companyName') .trim() .notEmpty() .withMessage('Company name is required'), body('jobTitle') .trim() .notEmpty() .withMessage('Job title is required'), body('country') .trim() .isLength({ min: 2, max: 100 }) .withMessage('Country must be between 2 and 100 characters') ] const optionalRegistrationFields = [ body('firstName') .optional() .trim() .notEmpty() .withMessage('First name cannot be empty'), body('lastName') .optional() .trim() .notEmpty() .withMessage('Last name cannot be empty'), body('email') .optional() .trim() .isEmail() .withMessage('Email must be valid') .bail() .normalizeEmail(), body('phone') .optional() .trim() .matches(phoneRegexp) .withMessage('Phone must be valid'), body('companyName') .optional() .trim() .notEmpty() .withMessage('Company name cannot be empty'), body('jobTitle') .optional() .trim() .notEmpty() .withMessage('Job title cannot be empty'), body('country') .optional() .trim() .isLength({ min: 2, max: 100 }) .withMessage('Country must be between 2 and 100 characters') ] async function loadEventWithChannel (eventId: number) { return EventModel.findByPk(eventId, { include: [ { model: VideoChannelModel.scope([ 'WITH_ACCOUNT' ]), required: true } ] }) } export const createEventRegistrationValidator = [ param('eventId') .custom(isIdValid) .withMessage('Invalid event id'), ...requiredRegistrationFields, async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return const eventId = parseInt(req.params.eventId) const event = await loadEventWithChannel(eventId) if (!event) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Event not found' }) } const resLocals = res.locals as any resLocals.event = event return next() } ] export const listEventRegistrationsValidator = [ param('eventId') .custom(isIdValid) .withMessage('Invalid event id'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return const event = await loadEventWithChannel(parseInt(req.params.eventId)) if (!event) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Event not found' }) } const resLocals = res.locals as any resLocals.event = event return next() } ] export const getEventRegistrationValidator = [ param('eventId') .custom(isIdValid) .withMessage('Invalid event id'), param('registrationId') .custom(isIdValid) .withMessage('Invalid registration id'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return const eventId = parseInt(req.params.eventId) const registrationId = parseInt(req.params.registrationId) const [ event, registration ] = await Promise.all([ loadEventWithChannel(eventId), EventRegistrationModel.loadByIdAndEventId(registrationId, eventId) ]) if (!event) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Event not found' }) } if (!registration) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Registration not found' }) } const resLocals = res.locals as any resLocals.event = event resLocals.eventRegistration = registration return next() } ] export const updateEventRegistrationValidator = [ param('eventId') .custom(isIdValid) .withMessage('Invalid event id'), param('registrationId') .custom(isIdValid) .withMessage('Invalid registration id'), ...optionalRegistrationFields, async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return const eventId = parseInt(req.params.eventId) const registrationId = parseInt(req.params.registrationId) const [ event, registration ] = await Promise.all([ loadEventWithChannel(eventId), EventRegistrationModel.loadByIdAndEventId(registrationId, eventId) ]) if (!event) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Event not found' }) } if (!registration) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Registration not found' }) } const resLocals = res.locals as any resLocals.event = event resLocals.eventRegistration = registration return next() } ] export const deleteEventRegistrationValidator = [ param('eventId') .custom(isIdValid) .withMessage('Invalid event id'), param('registrationId') .custom(isIdValid) .withMessage('Invalid registration id'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return const eventId = parseInt(req.params.eventId) const registrationId = parseInt(req.params.registrationId) const [ event, registration ] = await Promise.all([ loadEventWithChannel(eventId), EventRegistrationModel.loadByIdAndEventId(registrationId, eventId) ]) if (!event) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Event not found' }) } if (!registration) { return res.fail({ status: HttpStatusCode.NOT_FOUND_404, message: 'Registration not found' }) } const resLocals = res.locals as any resLocals.event = event resLocals.eventRegistration = registration return next() } ]