255 lines
6.6 KiB
TypeScript
255 lines
6.6 KiB
TypeScript
import { HttpStatusCode } from '@peertube/peertube-models'
|
|
import { isIdValid } from '@server/helpers/custom-validators/misc.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'
|
|
|
|
export const createEventValidator = [
|
|
body('title')
|
|
.trim()
|
|
.isLength({ min: 1, max: 255 })
|
|
.withMessage('Event title must be between 1 and 255 characters'),
|
|
|
|
body('description')
|
|
.optional()
|
|
.trim()
|
|
.isLength({ max: 5000 })
|
|
.withMessage('Event description must not exceed 5000 characters'),
|
|
|
|
body('startTime')
|
|
.isISO8601()
|
|
.withMessage('Start time must be a valid ISO 8601 date'),
|
|
|
|
body('endTime')
|
|
.isISO8601()
|
|
.withMessage('End time must be a valid ISO 8601 date'),
|
|
|
|
body('state')
|
|
.optional()
|
|
.isIn([ 'active', 'disabled' ])
|
|
.withMessage('Invalid event state'),
|
|
|
|
body('liveUrl')
|
|
.optional({ checkFalsy: true})
|
|
.isLength({ max: 2048 })
|
|
.withMessage('Live URL must not exceed 2048 characters')
|
|
.bail()
|
|
.isURL()
|
|
.withMessage('Live URL must be a valid URL'),
|
|
|
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
if (areValidationErrors(req, res)) return
|
|
|
|
// Validate end time is after start time
|
|
const startTime = new Date(req.body.startTime)
|
|
const endTime = new Date(req.body.endTime)
|
|
|
|
if (endTime <= startTime) {
|
|
return res.fail({
|
|
status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
|
|
message: 'End time must be after start time'
|
|
})
|
|
}
|
|
|
|
// Validate channel exists
|
|
const { channelId } = req.params
|
|
const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)
|
|
|
|
if (!channel) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Channel not found'
|
|
})
|
|
}
|
|
|
|
const resLocals = res.locals as any
|
|
resLocals.channel = channel
|
|
|
|
return next()
|
|
}
|
|
]
|
|
|
|
export const updateEventValidator = [
|
|
param('eventId')
|
|
.custom(isIdValid)
|
|
.withMessage('Invalid event id'),
|
|
|
|
body('title')
|
|
.optional()
|
|
.trim()
|
|
.isLength({ min: 1, max: 255 })
|
|
.withMessage('Event title must be between 1 and 255 characters'),
|
|
|
|
body('description')
|
|
.optional()
|
|
.trim()
|
|
.isLength({ max: 5000 })
|
|
.withMessage('Event description must not exceed 5000 characters'),
|
|
|
|
body('startTime')
|
|
.optional()
|
|
.isISO8601()
|
|
.withMessage('Start time must be a valid ISO 8601 date'),
|
|
|
|
body('endTime')
|
|
.optional()
|
|
.isISO8601()
|
|
.withMessage('End time must be a valid ISO 8601 date'),
|
|
|
|
body('state')
|
|
.optional()
|
|
.isIn([ 'active', 'disabled' ])
|
|
.withMessage('Invalid event state'),
|
|
|
|
body('liveUrl')
|
|
.optional({ checkFalsy: true})
|
|
.isLength({ max: 2048 })
|
|
.withMessage('Live URL must not exceed 2048 characters')
|
|
.bail()
|
|
.isURL()
|
|
.withMessage('Live URL must be a valid URL'),
|
|
|
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
if (areValidationErrors(req, res)) return
|
|
|
|
// Validate time range if both provided
|
|
if (req.body.startTime && req.body.endTime) {
|
|
const startTime = new Date(req.body.startTime)
|
|
const endTime = new Date(req.body.endTime)
|
|
|
|
if (endTime <= startTime) {
|
|
return res.fail({
|
|
status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
|
|
message: 'End time must be after start time'
|
|
})
|
|
}
|
|
}
|
|
|
|
// Validate channel exists
|
|
const { channelId, eventId } = req.params
|
|
const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)
|
|
|
|
if (!channel) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Channel not found'
|
|
})
|
|
}
|
|
|
|
// Validate event exists and belongs to channel
|
|
const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))
|
|
|
|
if (!event) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Event not found'
|
|
})
|
|
}
|
|
|
|
const resLocals = res.locals as any
|
|
resLocals.channel = channel
|
|
resLocals.event = event
|
|
|
|
return next()
|
|
}
|
|
]
|
|
|
|
export const deleteEventValidator = [
|
|
param('eventId')
|
|
.custom(isIdValid)
|
|
.withMessage('Invalid event id'),
|
|
|
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
if (areValidationErrors(req, res)) return
|
|
|
|
const { channelId, eventId } = req.params
|
|
const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)
|
|
|
|
if (!channel) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Channel not found'
|
|
})
|
|
}
|
|
|
|
const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))
|
|
|
|
if (!event) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Event not found'
|
|
})
|
|
}
|
|
|
|
const resLocals = res.locals as any
|
|
resLocals.channel = channel
|
|
resLocals.event = event
|
|
|
|
return next()
|
|
}
|
|
]
|
|
|
|
export const updateEventStateValidator = [
|
|
param('eventId')
|
|
.custom(isIdValid)
|
|
.withMessage('Invalid event id'),
|
|
|
|
body('state')
|
|
.isIn([ 'active', 'disabled' ])
|
|
.withMessage('Invalid event state'),
|
|
|
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
if (areValidationErrors(req, res)) return
|
|
|
|
const { channelId, eventId } = req.params
|
|
const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)
|
|
|
|
if (!channel) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Channel not found'
|
|
})
|
|
}
|
|
|
|
const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))
|
|
|
|
if (!event) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Event not found'
|
|
})
|
|
}
|
|
|
|
const resLocals = res.locals as any
|
|
resLocals.channel = channel
|
|
resLocals.event = event
|
|
|
|
return next()
|
|
}
|
|
]
|
|
|
|
export const getEventValidator = [
|
|
param('id')
|
|
.custom(isIdValid)
|
|
.withMessage('Invalid event id'),
|
|
|
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
if (areValidationErrors(req, res)) return
|
|
|
|
const event = await EventModel.findByPk(req.params.id)
|
|
|
|
if (!event) {
|
|
return res.fail({
|
|
status: HttpStatusCode.NOT_FOUND_404,
|
|
message: 'Event not found'
|
|
})
|
|
}
|
|
|
|
(res.locals as any).event = event
|
|
|
|
return next()
|
|
}
|
|
]
|