Init commit
This commit is contained in:
254
server/core/middlewares/validators/events.ts
Normal file
254
server/core/middlewares/validators/events.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
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()
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user