Init commit
This commit is contained in:
247
server/core/middlewares/validators/live-videos.ts
Normal file
247
server/core/middlewares/validators/live-videos.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { isIdValid } from '@server/helpers/custom-validators/misc.js'
|
||||
import { isVideoFileMimeTypeValid, isVideoImageValid } from '@server/helpers/custom-validators/videos.js'
|
||||
import { LiveVideoModel, LiveVideoStatus } from '@server/models/video/live-video.js'
|
||||
import express from 'express'
|
||||
import { body, param, query } from 'express-validator'
|
||||
import { areValidationErrors } from './shared/utils.js'
|
||||
|
||||
function ensureAdmin (res: express.Response) {
|
||||
const user = res.locals.oauth?.token?.User
|
||||
return user?.role === 0
|
||||
}
|
||||
|
||||
async function checkScheduleConflict (req: express.Request, res: express.Response, next: express.NextFunction, excludeId?: number) {
|
||||
const startTime = new Date(req.body.startTime)
|
||||
const endTime = new Date(req.body.endTime)
|
||||
|
||||
const conflicts = await LiveVideoModel.listConflicts({
|
||||
targetCamera: req.body.targetCamera,
|
||||
startTime,
|
||||
endTime,
|
||||
excludeId
|
||||
})
|
||||
|
||||
if (conflicts.length !== 0) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.CONFLICT_409,
|
||||
message: 'Target camera is already assigned to another live video'
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
|
||||
export const listLiveVideosValidator = [
|
||||
query('status')
|
||||
.optional()
|
||||
.isIn(Object.values(LiveVideoStatus))
|
||||
.withMessage('Invalid live video status'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const getLiveVideoValidator = [
|
||||
param('id')
|
||||
.custom(isIdValid)
|
||||
.withMessage('Invalid live video id'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
|
||||
if (!liveVideo) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Live video not found'
|
||||
})
|
||||
}
|
||||
|
||||
;(res.locals as any).liveVideo = liveVideo
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const createLiveVideoValidator = [
|
||||
body('title')
|
||||
.trim()
|
||||
.isLength({ min: 1, max: 255 })
|
||||
.withMessage('Live video title must be between 1 and 255 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('targetCamera')
|
||||
.trim()
|
||||
.isLength({ min: 1, max: 255 })
|
||||
.withMessage('Target camera is required'),
|
||||
|
||||
body('thumbnailfile')
|
||||
.custom((_, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
|
||||
.withMessage('Should have a valid thumbnail file'),
|
||||
|
||||
body('videofile')
|
||||
.custom((_, { req }) => isVideoFileMimeTypeValid(req.files))
|
||||
.withMessage('Should have a valid video file'),
|
||||
|
||||
body('subtitlefile')
|
||||
.optional()
|
||||
.custom((_, { req }) => {
|
||||
if (!req.files || !req.files['subtitlefile']) return true
|
||||
const file = req.files['subtitlefile'][0]
|
||||
const validMimes = [ 'text/vtt', 'application/x-subrip', 'text/plain' ]
|
||||
return validMimes.includes(file.mimetype)
|
||||
})
|
||||
.withMessage('Should have a valid subtitle file (vtt or srt)'),
|
||||
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!ensureAdmin(res)) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.FORBIDDEN_403,
|
||||
message: 'Only admins can manage live videos'
|
||||
})
|
||||
}
|
||||
|
||||
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'
|
||||
})
|
||||
}
|
||||
|
||||
return checkScheduleConflict(req, res, next)
|
||||
}
|
||||
]
|
||||
|
||||
export const updateLiveVideoValidator = [
|
||||
param('id')
|
||||
.custom(isIdValid)
|
||||
.withMessage('Invalid live video id'),
|
||||
|
||||
body('title')
|
||||
.optional()
|
||||
.trim()
|
||||
.isLength({ min: 1, max: 255 })
|
||||
.withMessage('Live video title must be between 1 and 255 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('targetCamera')
|
||||
.optional()
|
||||
.trim()
|
||||
.isLength({ min: 1, max: 255 })
|
||||
.withMessage('Target camera must be between 1 and 255 characters'),
|
||||
|
||||
body('status')
|
||||
.optional()
|
||||
.isIn(Object.values(LiveVideoStatus))
|
||||
.withMessage('Invalid live video status'),
|
||||
|
||||
body('thumbnailfile')
|
||||
.optional()
|
||||
.custom((_, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
|
||||
.withMessage('Should have a valid thumbnail file'),
|
||||
|
||||
body('videofile')
|
||||
.optional()
|
||||
.custom((_, { req }) => isVideoFileMimeTypeValid(req.files))
|
||||
.withMessage('Should have a valid video file'),
|
||||
body('subtitlefile')
|
||||
.optional()
|
||||
.custom((_, { req }) => {
|
||||
if (!req.files || !req.files['subtitlefile']) return true
|
||||
const file = req.files['subtitlefile'][0]
|
||||
const validMimes = [ 'text/vtt', 'application/x-subrip', 'text/plain' ]
|
||||
return validMimes.includes(file.mimetype)
|
||||
})
|
||||
.withMessage('Should have a valid subtitle file (vtt or srt)'),
|
||||
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!ensureAdmin(res)) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.FORBIDDEN_403,
|
||||
message: 'Only admins can manage live videos'
|
||||
})
|
||||
}
|
||||
|
||||
const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
|
||||
if (!liveVideo) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Live video not found'
|
||||
})
|
||||
}
|
||||
|
||||
const startTime = req.body.startTime ? new Date(req.body.startTime) : liveVideo.startTime
|
||||
const endTime = req.body.endTime ? new Date(req.body.endTime) : liveVideo.endTime
|
||||
|
||||
if (endTime <= startTime) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
|
||||
message: 'End time must be after start time'
|
||||
})
|
||||
}
|
||||
|
||||
;(res.locals as any).liveVideo = liveVideo
|
||||
|
||||
if (req.body.startTime || req.body.endTime || req.body.targetCamera) {
|
||||
req.body.startTime = startTime.toISOString()
|
||||
req.body.endTime = endTime.toISOString()
|
||||
req.body.targetCamera = req.body.targetCamera ?? liveVideo.targetCamera
|
||||
|
||||
return checkScheduleConflict(req, res, next, liveVideo.id)
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const deleteLiveVideoValidator = [
|
||||
param('id')
|
||||
.custom(isIdValid)
|
||||
.withMessage('Invalid live video id'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!ensureAdmin(res)) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.FORBIDDEN_403,
|
||||
message: 'Only admins can manage live videos'
|
||||
})
|
||||
}
|
||||
|
||||
const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
|
||||
if (!liveVideo) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Live video not found'
|
||||
})
|
||||
}
|
||||
|
||||
;(res.locals as any).liveVideo = liveVideo
|
||||
return next()
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user