Init commit
This commit is contained in:
3
server/core/middlewares/validators/runners/index.ts
Normal file
3
server/core/middlewares/validators/runners/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './jobs.js'
|
||||
export * from './registration-token.js'
|
||||
export * from './runners.js'
|
||||
60
server/core/middlewares/validators/runners/job-files.ts
Normal file
60
server/core/middlewares/validators/runners/job-files.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import express from 'express'
|
||||
import { param } from 'express-validator'
|
||||
import { basename } from 'path'
|
||||
import { isSafeFilename } from '@server/helpers/custom-validators/misc.js'
|
||||
import { hasVideoStudioTaskFile, HttpStatusCode, RunnerJobStudioTranscodingPayload } from '@peertube/peertube-models'
|
||||
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared/index.js'
|
||||
|
||||
const tags = [ 'runner' ]
|
||||
|
||||
export const runnerJobGetVideoTranscodingFileValidator = [
|
||||
isValidVideoIdParam('videoId'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
if (!await doesVideoExist(req.params.videoId, res, 'all')) return
|
||||
|
||||
const runnerJob = res.locals.runnerJob
|
||||
|
||||
if (runnerJob.privatePayload.videoUUID !== res.locals.videoAll.uuid) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.FORBIDDEN_403,
|
||||
message: 'Job is not associated to this video',
|
||||
tags: [ ...tags, res.locals.videoAll.uuid ]
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const runnerJobGetVideoStudioTaskFileValidator = [
|
||||
param('filename').custom(v => isSafeFilename(v)),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
const filename = req.params.filename
|
||||
|
||||
const payload = res.locals.runnerJob.payload as RunnerJobStudioTranscodingPayload
|
||||
|
||||
const found = Array.isArray(payload?.tasks) && payload.tasks.some(t => {
|
||||
if (hasVideoStudioTaskFile(t)) {
|
||||
return basename(t.options.file) === filename
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!found) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'File is not associated to this edition task',
|
||||
tags: [ ...tags, res.locals.videoAll.uuid ]
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
200
server/core/middlewares/validators/runners/jobs.ts
Normal file
200
server/core/middlewares/validators/runners/jobs.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { arrayify } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
RunnerJobState,
|
||||
RunnerJobStateType,
|
||||
RunnerJobSuccessBody,
|
||||
RunnerJobUpdateBody,
|
||||
ServerErrorCode
|
||||
} from '@peertube/peertube-models'
|
||||
import { exists, isUUIDValid } from '@server/helpers/custom-validators/misc.js'
|
||||
import {
|
||||
isRunnerJobAbortReasonValid,
|
||||
isRunnerJobArrayOfStateValid,
|
||||
isRunnerJobErrorMessageValid,
|
||||
isRunnerJobProgressValid,
|
||||
isRunnerJobSuccessPayloadValid,
|
||||
isRunnerJobTokenValid,
|
||||
isRunnerJobUpdatePayloadValid
|
||||
} from '@server/helpers/custom-validators/runners/jobs.js'
|
||||
import { isRunnerTokenValid } from '@server/helpers/custom-validators/runners/runners.js'
|
||||
import { cleanUpReqFiles } from '@server/helpers/express-utils.js'
|
||||
import { runnerJobCanBeCancelled } from '@server/lib/runners/index.js'
|
||||
import { RunnerJobModel } from '@server/models/runner/runner-job.js'
|
||||
import express from 'express'
|
||||
import { body, param, query } from 'express-validator'
|
||||
import { areValidationErrors } from '../shared/index.js'
|
||||
|
||||
const tags = [ 'runner' ]
|
||||
|
||||
export const acceptRunnerJobValidator = [
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (res.locals.runnerJob.state !== RunnerJobState.PENDING) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'This runner job is not in pending state',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const abortRunnerJobValidator = [
|
||||
body('reason').custom(isRunnerJobAbortReasonValid),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const updateRunnerJobValidator = [
|
||||
body('progress').optional().custom(isRunnerJobProgressValid),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return cleanUpReqFiles(req)
|
||||
|
||||
const body = req.body as RunnerJobUpdateBody
|
||||
const job = res.locals.runnerJob
|
||||
|
||||
if (isRunnerJobUpdatePayloadValid(body.payload, job.type, req.files) !== true) {
|
||||
cleanUpReqFiles(req)
|
||||
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'Payload is invalid',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const errorRunnerJobValidator = [
|
||||
body('message').custom(isRunnerJobErrorMessageValid),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const successRunnerJobValidator = [
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
const body = req.body as RunnerJobSuccessBody
|
||||
|
||||
if (isRunnerJobSuccessPayloadValid(body.payload, res.locals.runnerJob.type, req.files) !== true) {
|
||||
cleanUpReqFiles(req)
|
||||
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'Payload is invalid',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const cancelRunnerJobValidator = [
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
const runnerJob = res.locals.runnerJob
|
||||
|
||||
if (runnerJobCanBeCancelled(runnerJob) !== true) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'Cannot cancel this job that is not in "pending", "processing" or "waiting for parent job" state',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const listRunnerJobsValidator = [
|
||||
query('search')
|
||||
.optional()
|
||||
.custom(exists),
|
||||
|
||||
query('stateOneOf')
|
||||
.optional()
|
||||
.customSanitizer(arrayify)
|
||||
.custom(isRunnerJobArrayOfStateValid),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const runnerJobGetValidator = [
|
||||
param('jobUUID').custom(isUUIDValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
const runnerJob = await RunnerJobModel.loadWithRunner(req.params.jobUUID)
|
||||
|
||||
if (!runnerJob) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Unknown runner job',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runnerJob = runnerJob
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export function jobOfRunnerGetValidatorFactory (allowedStates: RunnerJobStateType[]) {
|
||||
return [
|
||||
param('jobUUID').custom(isUUIDValid),
|
||||
|
||||
body('runnerToken').custom(isRunnerTokenValid),
|
||||
body('jobToken').custom(isRunnerJobTokenValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return cleanUpReqFiles(req)
|
||||
|
||||
const runnerJob = await RunnerJobModel.loadByRunnerAndJobTokensWithRunner({
|
||||
uuid: req.params.jobUUID,
|
||||
runnerToken: req.body.runnerToken,
|
||||
jobToken: req.body.jobToken
|
||||
})
|
||||
|
||||
if (!runnerJob) {
|
||||
cleanUpReqFiles(req)
|
||||
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Unknown runner job',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
if (!allowedStates.includes(runnerJob.state)) {
|
||||
cleanUpReqFiles(req)
|
||||
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
type: ServerErrorCode.RUNNER_JOB_NOT_IN_PROCESSING_STATE,
|
||||
message: 'Job is not in "processing" state',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runnerJob = runnerJob
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import express from 'express'
|
||||
import { param } from 'express-validator'
|
||||
import { isIdValid } from '@server/helpers/custom-validators/misc.js'
|
||||
import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token.js'
|
||||
import { forceNumber } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { areValidationErrors } from '../shared/utils.js'
|
||||
|
||||
const tags = [ 'runner' ]
|
||||
|
||||
const deleteRegistrationTokenValidator = [
|
||||
param('id').custom(isIdValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
const registrationToken = await RunnerRegistrationTokenModel.load(forceNumber(req.params.id))
|
||||
|
||||
if (!registrationToken) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Registration token not found',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runnerRegistrationToken = registrationToken
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
deleteRegistrationTokenValidator
|
||||
}
|
||||
108
server/core/middlewares/validators/runners/runners.ts
Normal file
108
server/core/middlewares/validators/runners/runners.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { forceNumber } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode, RegisterRunnerBody, ServerErrorCode } from '@peertube/peertube-models'
|
||||
import { isIdValid, isStableOrUnstableVersionValid } from '@server/helpers/custom-validators/misc.js'
|
||||
import {
|
||||
isRunnerDescriptionValid,
|
||||
isRunnerNameValid,
|
||||
isRunnerRegistrationTokenValid,
|
||||
isRunnerTokenValid
|
||||
} from '@server/helpers/custom-validators/runners/runners.js'
|
||||
import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token.js'
|
||||
import { RunnerModel } from '@server/models/runner/runner.js'
|
||||
import express from 'express'
|
||||
import { body, param } from 'express-validator'
|
||||
import { areValidationErrors } from '../shared/utils.js'
|
||||
|
||||
const tags = [ 'runner' ]
|
||||
|
||||
export const registerRunnerValidator = [
|
||||
body('registrationToken').custom(isRunnerRegistrationTokenValid),
|
||||
body('name').custom(isRunnerNameValid),
|
||||
body('description').optional().custom(isRunnerDescriptionValid),
|
||||
body('version').optional().custom(isStableOrUnstableVersionValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
const body: RegisterRunnerBody = req.body
|
||||
|
||||
const runnerRegistrationToken = await RunnerRegistrationTokenModel.loadByRegistrationToken(body.registrationToken)
|
||||
|
||||
if (!runnerRegistrationToken) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Registration token is invalid',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
const existing = await RunnerModel.loadByName(body.name)
|
||||
if (existing) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.BAD_REQUEST_400,
|
||||
message: 'This runner name already exists on this instance',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runnerRegistrationToken = runnerRegistrationToken
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const deleteRunnerValidator = [
|
||||
param('runnerId').custom(isIdValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
const runner = await RunnerModel.load(forceNumber(req.params.runnerId))
|
||||
|
||||
if (!runner) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Runner not found',
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runner = runner
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const getRunnerFromTokenValidator = [
|
||||
body('runnerToken').custom(isRunnerTokenValid),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
const runner = await RunnerModel.loadByToken(req.body.runnerToken)
|
||||
|
||||
if (!runner) {
|
||||
return res.fail({
|
||||
status: HttpStatusCode.NOT_FOUND_404,
|
||||
message: 'Unknown runner token',
|
||||
type: ServerErrorCode.UNKNOWN_RUNNER_TOKEN,
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
res.locals.runner = runner
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export const requestRunnerJobValidator = [
|
||||
body('version').optional().custom(isStableOrUnstableVersionValid),
|
||||
body('jobTypes').optional().isArray(),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res, { tags })) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user