aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/runners
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/runners')
-rw-r--r--server/controllers/api/runners/index.ts20
-rw-r--r--server/controllers/api/runners/jobs-files.ts112
-rw-r--r--server/controllers/api/runners/jobs.ts416
-rw-r--r--server/controllers/api/runners/manage-runners.ts112
-rw-r--r--server/controllers/api/runners/registration-tokens.ts91
5 files changed, 0 insertions, 751 deletions
diff --git a/server/controllers/api/runners/index.ts b/server/controllers/api/runners/index.ts
deleted file mode 100644
index 9998fe4cc..000000000
--- a/server/controllers/api/runners/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
1import express from 'express'
2import { runnerJobsRouter } from './jobs'
3import { runnerJobFilesRouter } from './jobs-files'
4import { manageRunnersRouter } from './manage-runners'
5import { runnerRegistrationTokensRouter } from './registration-tokens'
6
7const runnersRouter = express.Router()
8
9// No api route limiter here, they are defined in child routers
10
11runnersRouter.use('/', manageRunnersRouter)
12runnersRouter.use('/', runnerJobsRouter)
13runnersRouter.use('/', runnerJobFilesRouter)
14runnersRouter.use('/', runnerRegistrationTokensRouter)
15
16// ---------------------------------------------------------------------------
17
18export {
19 runnersRouter
20}
diff --git a/server/controllers/api/runners/jobs-files.ts b/server/controllers/api/runners/jobs-files.ts
deleted file mode 100644
index d28f43701..000000000
--- a/server/controllers/api/runners/jobs-files.ts
+++ /dev/null
@@ -1,112 +0,0 @@
1import express from 'express'
2import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { proxifyHLS, proxifyWebVideoFile } from '@server/lib/object-storage'
4import { VideoPathManager } from '@server/lib/video-path-manager'
5import { getStudioTaskFilePath } from '@server/lib/video-studio'
6import { apiRateLimiter, asyncMiddleware } from '@server/middlewares'
7import { jobOfRunnerGetValidatorFactory } from '@server/middlewares/validators/runners'
8import {
9 runnerJobGetVideoStudioTaskFileValidator,
10 runnerJobGetVideoTranscodingFileValidator
11} from '@server/middlewares/validators/runners/job-files'
12import { RunnerJobState, VideoStorage } from '@shared/models'
13
14const lTags = loggerTagsFactory('api', 'runner')
15
16const runnerJobFilesRouter = express.Router()
17
18runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
19 apiRateLimiter,
20 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
21 asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
22 asyncMiddleware(getMaxQualityVideoFile)
23)
24
25runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
26 apiRateLimiter,
27 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
28 asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
29 getMaxQualityVideoPreview
30)
31
32runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
33 apiRateLimiter,
34 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
35 asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
36 runnerJobGetVideoStudioTaskFileValidator,
37 getVideoStudioTaskFile
38)
39
40// ---------------------------------------------------------------------------
41
42export {
43 runnerJobFilesRouter
44}
45
46// ---------------------------------------------------------------------------
47
48async function getMaxQualityVideoFile (req: express.Request, res: express.Response) {
49 const runnerJob = res.locals.runnerJob
50 const runner = runnerJob.Runner
51 const video = res.locals.videoAll
52
53 logger.info(
54 'Get max quality file of video %s of job %s for runner %s', video.uuid, runnerJob.uuid, runner.name,
55 lTags(runner.name, runnerJob.id, runnerJob.type)
56 )
57
58 const file = video.getMaxQualityFile()
59
60 if (file.storage === VideoStorage.OBJECT_STORAGE) {
61 if (file.isHLS()) {
62 return proxifyHLS({
63 req,
64 res,
65 filename: file.filename,
66 playlist: video.getHLSPlaylist(),
67 reinjectVideoFileToken: false,
68 video
69 })
70 }
71
72 // Web video
73 return proxifyWebVideoFile({
74 req,
75 res,
76 filename: file.filename
77 })
78 }
79
80 return VideoPathManager.Instance.makeAvailableVideoFile(file, videoPath => {
81 return res.sendFile(videoPath)
82 })
83}
84
85function getMaxQualityVideoPreview (req: express.Request, res: express.Response) {
86 const runnerJob = res.locals.runnerJob
87 const runner = runnerJob.Runner
88 const video = res.locals.videoAll
89
90 logger.info(
91 'Get max quality preview file of video %s of job %s for runner %s', video.uuid, runnerJob.uuid, runner.name,
92 lTags(runner.name, runnerJob.id, runnerJob.type)
93 )
94
95 const file = video.getPreview()
96
97 return res.sendFile(file.getPath())
98}
99
100function getVideoStudioTaskFile (req: express.Request, res: express.Response) {
101 const runnerJob = res.locals.runnerJob
102 const runner = runnerJob.Runner
103 const video = res.locals.videoAll
104 const filename = req.params.filename
105
106 logger.info(
107 'Get video studio task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
108 lTags(runner.name, runnerJob.id, runnerJob.type)
109 )
110
111 return res.sendFile(getStudioTaskFilePath(filename))
112}
diff --git a/server/controllers/api/runners/jobs.ts b/server/controllers/api/runners/jobs.ts
deleted file mode 100644
index e9e2ddf49..000000000
--- a/server/controllers/api/runners/jobs.ts
+++ /dev/null
@@ -1,416 +0,0 @@
1import express, { UploadFiles } from 'express'
2import { retryTransactionWrapper } from '@server/helpers/database-utils'
3import { createReqFiles } from '@server/helpers/express-utils'
4import { logger, loggerTagsFactory } from '@server/helpers/logger'
5import { generateRunnerJobToken } from '@server/helpers/token-generator'
6import { MIMETYPES } from '@server/initializers/constants'
7import { sequelizeTypescript } from '@server/initializers/database'
8import { getRunnerJobHandlerClass, runnerJobCanBeCancelled, updateLastRunnerContact } from '@server/lib/runners'
9import {
10 apiRateLimiter,
11 asyncMiddleware,
12 authenticate,
13 ensureUserHasRight,
14 paginationValidator,
15 runnerJobsSortValidator,
16 setDefaultPagination,
17 setDefaultSort
18} from '@server/middlewares'
19import {
20 abortRunnerJobValidator,
21 acceptRunnerJobValidator,
22 cancelRunnerJobValidator,
23 errorRunnerJobValidator,
24 getRunnerFromTokenValidator,
25 jobOfRunnerGetValidatorFactory,
26 listRunnerJobsValidator,
27 runnerJobGetValidator,
28 successRunnerJobValidator,
29 updateRunnerJobValidator
30} from '@server/middlewares/validators/runners'
31import { RunnerModel } from '@server/models/runner/runner'
32import { RunnerJobModel } from '@server/models/runner/runner-job'
33import {
34 AbortRunnerJobBody,
35 AcceptRunnerJobResult,
36 ErrorRunnerJobBody,
37 HttpStatusCode,
38 ListRunnerJobsQuery,
39 LiveRTMPHLSTranscodingUpdatePayload,
40 RequestRunnerJobResult,
41 RunnerJobState,
42 RunnerJobSuccessBody,
43 RunnerJobSuccessPayload,
44 RunnerJobType,
45 RunnerJobUpdateBody,
46 RunnerJobUpdatePayload,
47 ServerErrorCode,
48 UserRight,
49 VideoStudioTranscodingSuccess,
50 VODAudioMergeTranscodingSuccess,
51 VODHLSTranscodingSuccess,
52 VODWebVideoTranscodingSuccess
53} from '@shared/models'
54
55const postRunnerJobSuccessVideoFiles = createReqFiles(
56 [ 'payload[videoFile]', 'payload[resolutionPlaylistFile]' ],
57 { ...MIMETYPES.VIDEO.MIMETYPE_EXT, ...MIMETYPES.M3U8.MIMETYPE_EXT }
58)
59
60const runnerJobUpdateVideoFiles = createReqFiles(
61 [ 'payload[videoChunkFile]', 'payload[resolutionPlaylistFile]', 'payload[masterPlaylistFile]' ],
62 { ...MIMETYPES.VIDEO.MIMETYPE_EXT, ...MIMETYPES.M3U8.MIMETYPE_EXT }
63)
64
65const lTags = loggerTagsFactory('api', 'runner')
66
67const runnerJobsRouter = express.Router()
68
69// ---------------------------------------------------------------------------
70// Controllers for runners
71// ---------------------------------------------------------------------------
72
73runnerJobsRouter.post('/jobs/request',
74 apiRateLimiter,
75 asyncMiddleware(getRunnerFromTokenValidator),
76 asyncMiddleware(requestRunnerJob)
77)
78
79runnerJobsRouter.post('/jobs/:jobUUID/accept',
80 apiRateLimiter,
81 asyncMiddleware(runnerJobGetValidator),
82 acceptRunnerJobValidator,
83 asyncMiddleware(getRunnerFromTokenValidator),
84 asyncMiddleware(acceptRunnerJob)
85)
86
87runnerJobsRouter.post('/jobs/:jobUUID/abort',
88 apiRateLimiter,
89 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
90 abortRunnerJobValidator,
91 asyncMiddleware(abortRunnerJob)
92)
93
94runnerJobsRouter.post('/jobs/:jobUUID/update',
95 runnerJobUpdateVideoFiles,
96 apiRateLimiter, // Has to be after multer middleware to parse runner token
97 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING, RunnerJobState.COMPLETING, RunnerJobState.COMPLETED ])),
98 updateRunnerJobValidator,
99 asyncMiddleware(updateRunnerJobController)
100)
101
102runnerJobsRouter.post('/jobs/:jobUUID/error',
103 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
104 errorRunnerJobValidator,
105 asyncMiddleware(errorRunnerJob)
106)
107
108runnerJobsRouter.post('/jobs/:jobUUID/success',
109 postRunnerJobSuccessVideoFiles,
110 apiRateLimiter, // Has to be after multer middleware to parse runner token
111 asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
112 successRunnerJobValidator,
113 asyncMiddleware(postRunnerJobSuccess)
114)
115
116// ---------------------------------------------------------------------------
117// Controllers for admins
118// ---------------------------------------------------------------------------
119
120runnerJobsRouter.post('/jobs/:jobUUID/cancel',
121 authenticate,
122 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
123 asyncMiddleware(runnerJobGetValidator),
124 cancelRunnerJobValidator,
125 asyncMiddleware(cancelRunnerJob)
126)
127
128runnerJobsRouter.get('/jobs',
129 authenticate,
130 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
131 paginationValidator,
132 runnerJobsSortValidator,
133 setDefaultSort,
134 setDefaultPagination,
135 listRunnerJobsValidator,
136 asyncMiddleware(listRunnerJobs)
137)
138
139runnerJobsRouter.delete('/jobs/:jobUUID',
140 authenticate,
141 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
142 asyncMiddleware(runnerJobGetValidator),
143 asyncMiddleware(deleteRunnerJob)
144)
145
146// ---------------------------------------------------------------------------
147
148export {
149 runnerJobsRouter
150}
151
152// ---------------------------------------------------------------------------
153
154// ---------------------------------------------------------------------------
155// Controllers for runners
156// ---------------------------------------------------------------------------
157
158async function requestRunnerJob (req: express.Request, res: express.Response) {
159 const runner = res.locals.runner
160 const availableJobs = await RunnerJobModel.listAvailableJobs()
161
162 logger.debug('Runner %s requests for a job.', runner.name, { availableJobs, ...lTags(runner.name) })
163
164 const result: RequestRunnerJobResult = {
165 availableJobs: availableJobs.map(j => ({
166 uuid: j.uuid,
167 type: j.type,
168 payload: j.payload
169 }))
170 }
171
172 updateLastRunnerContact(req, runner)
173
174 return res.json(result)
175}
176
177async function acceptRunnerJob (req: express.Request, res: express.Response) {
178 const runner = res.locals.runner
179 const runnerJob = res.locals.runnerJob
180
181 const newRunnerJob = await retryTransactionWrapper(() => {
182 return sequelizeTypescript.transaction(async transaction => {
183 await runnerJob.reload({ transaction })
184
185 if (runnerJob.state !== RunnerJobState.PENDING) {
186 res.fail({
187 type: ServerErrorCode.RUNNER_JOB_NOT_IN_PENDING_STATE,
188 message: 'This job is not in pending state anymore',
189 status: HttpStatusCode.CONFLICT_409
190 })
191
192 return undefined
193 }
194
195 runnerJob.state = RunnerJobState.PROCESSING
196 runnerJob.processingJobToken = generateRunnerJobToken()
197 runnerJob.startedAt = new Date()
198 runnerJob.runnerId = runner.id
199
200 return runnerJob.save({ transaction })
201 })
202 })
203 if (!newRunnerJob) return
204
205 newRunnerJob.Runner = runner as RunnerModel
206
207 const result: AcceptRunnerJobResult = {
208 job: {
209 ...newRunnerJob.toFormattedJSON(),
210
211 jobToken: newRunnerJob.processingJobToken
212 }
213 }
214
215 updateLastRunnerContact(req, runner)
216
217 logger.info(
218 'Remote runner %s has accepted job %s (%s)', runner.name, runnerJob.uuid, runnerJob.type,
219 lTags(runner.name, runnerJob.uuid, runnerJob.type)
220 )
221
222 return res.json(result)
223}
224
225async function abortRunnerJob (req: express.Request, res: express.Response) {
226 const runnerJob = res.locals.runnerJob
227 const runner = runnerJob.Runner
228 const body: AbortRunnerJobBody = req.body
229
230 logger.info(
231 'Remote runner %s is aborting job %s (%s)', runner.name, runnerJob.uuid, runnerJob.type,
232 { reason: body.reason, ...lTags(runner.name, runnerJob.uuid, runnerJob.type) }
233 )
234
235 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
236 await new RunnerJobHandler().abort({ runnerJob })
237
238 updateLastRunnerContact(req, runnerJob.Runner)
239
240 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
241}
242
243async function errorRunnerJob (req: express.Request, res: express.Response) {
244 const runnerJob = res.locals.runnerJob
245 const runner = runnerJob.Runner
246 const body: ErrorRunnerJobBody = req.body
247
248 runnerJob.failures += 1
249
250 logger.error(
251 'Remote runner %s had an error with job %s (%s)', runner.name, runnerJob.uuid, runnerJob.type,
252 { errorMessage: body.message, totalFailures: runnerJob.failures, ...lTags(runner.name, runnerJob.uuid, runnerJob.type) }
253 )
254
255 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
256 await new RunnerJobHandler().error({ runnerJob, message: body.message })
257
258 updateLastRunnerContact(req, runnerJob.Runner)
259
260 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
261}
262
263// ---------------------------------------------------------------------------
264
265const jobUpdateBuilders: {
266 [id in RunnerJobType]?: (payload: RunnerJobUpdatePayload, files?: UploadFiles) => RunnerJobUpdatePayload
267} = {
268 'live-rtmp-hls-transcoding': (payload: LiveRTMPHLSTranscodingUpdatePayload, files) => {
269 return {
270 ...payload,
271
272 masterPlaylistFile: files['payload[masterPlaylistFile]']?.[0].path,
273 resolutionPlaylistFile: files['payload[resolutionPlaylistFile]']?.[0].path,
274 videoChunkFile: files['payload[videoChunkFile]']?.[0].path
275 }
276 }
277}
278
279async function updateRunnerJobController (req: express.Request, res: express.Response) {
280 const runnerJob = res.locals.runnerJob
281 const runner = runnerJob.Runner
282 const body: RunnerJobUpdateBody = req.body
283
284 if (runnerJob.state === RunnerJobState.COMPLETING || runnerJob.state === RunnerJobState.COMPLETED) {
285 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
286 }
287
288 const payloadBuilder = jobUpdateBuilders[runnerJob.type]
289 const updatePayload = payloadBuilder
290 ? payloadBuilder(body.payload, req.files as UploadFiles)
291 : undefined
292
293 logger.debug(
294 'Remote runner %s is updating job %s (%s)', runnerJob.Runner.name, runnerJob.uuid, runnerJob.type,
295 { body, updatePayload, ...lTags(runner.name, runnerJob.uuid, runnerJob.type) }
296 )
297
298 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
299 await new RunnerJobHandler().update({
300 runnerJob,
301 progress: req.body.progress,
302 updatePayload
303 })
304
305 updateLastRunnerContact(req, runnerJob.Runner)
306
307 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
308}
309
310// ---------------------------------------------------------------------------
311
312const jobSuccessPayloadBuilders: {
313 [id in RunnerJobType]: (payload: RunnerJobSuccessPayload, files?: UploadFiles) => RunnerJobSuccessPayload
314} = {
315 'vod-web-video-transcoding': (payload: VODWebVideoTranscodingSuccess, files) => {
316 return {
317 ...payload,
318
319 videoFile: files['payload[videoFile]'][0].path
320 }
321 },
322
323 'vod-hls-transcoding': (payload: VODHLSTranscodingSuccess, files) => {
324 return {
325 ...payload,
326
327 videoFile: files['payload[videoFile]'][0].path,
328 resolutionPlaylistFile: files['payload[resolutionPlaylistFile]'][0].path
329 }
330 },
331
332 'vod-audio-merge-transcoding': (payload: VODAudioMergeTranscodingSuccess, files) => {
333 return {
334 ...payload,
335
336 videoFile: files['payload[videoFile]'][0].path
337 }
338 },
339
340 'video-studio-transcoding': (payload: VideoStudioTranscodingSuccess, files) => {
341 return {
342 ...payload,
343
344 videoFile: files['payload[videoFile]'][0].path
345 }
346 },
347
348 'live-rtmp-hls-transcoding': () => ({})
349}
350
351async function postRunnerJobSuccess (req: express.Request, res: express.Response) {
352 const runnerJob = res.locals.runnerJob
353 const runner = runnerJob.Runner
354 const body: RunnerJobSuccessBody = req.body
355
356 const resultPayload = jobSuccessPayloadBuilders[runnerJob.type](body.payload, req.files as UploadFiles)
357
358 logger.info(
359 'Remote runner %s is sending success result for job %s (%s)', runnerJob.Runner.name, runnerJob.uuid, runnerJob.type,
360 { resultPayload, ...lTags(runner.name, runnerJob.uuid, runnerJob.type) }
361 )
362
363 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
364 await new RunnerJobHandler().complete({ runnerJob, resultPayload })
365
366 updateLastRunnerContact(req, runnerJob.Runner)
367
368 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
369}
370
371// ---------------------------------------------------------------------------
372// Controllers for admins
373// ---------------------------------------------------------------------------
374
375async function cancelRunnerJob (req: express.Request, res: express.Response) {
376 const runnerJob = res.locals.runnerJob
377
378 logger.info('Cancelling job %s (%s)', runnerJob.uuid, runnerJob.type, lTags(runnerJob.uuid, runnerJob.type))
379
380 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
381 await new RunnerJobHandler().cancel({ runnerJob })
382
383 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
384}
385
386async function deleteRunnerJob (req: express.Request, res: express.Response) {
387 const runnerJob = res.locals.runnerJob
388
389 logger.info('Deleting job %s (%s)', runnerJob.uuid, runnerJob.type, lTags(runnerJob.uuid, runnerJob.type))
390
391 if (runnerJobCanBeCancelled(runnerJob)) {
392 const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob)
393 await new RunnerJobHandler().cancel({ runnerJob })
394 }
395
396 await runnerJob.destroy()
397
398 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
399}
400
401async function listRunnerJobs (req: express.Request, res: express.Response) {
402 const query: ListRunnerJobsQuery = req.query
403
404 const resultList = await RunnerJobModel.listForApi({
405 start: query.start,
406 count: query.count,
407 sort: query.sort,
408 search: query.search,
409 stateOneOf: query.stateOneOf
410 })
411
412 return res.json({
413 total: resultList.total,
414 data: resultList.data.map(d => d.toFormattedAdminJSON())
415 })
416}
diff --git a/server/controllers/api/runners/manage-runners.ts b/server/controllers/api/runners/manage-runners.ts
deleted file mode 100644
index be7ebc0b3..000000000
--- a/server/controllers/api/runners/manage-runners.ts
+++ /dev/null
@@ -1,112 +0,0 @@
1import express from 'express'
2import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { generateRunnerToken } from '@server/helpers/token-generator'
4import {
5 apiRateLimiter,
6 asyncMiddleware,
7 authenticate,
8 ensureUserHasRight,
9 paginationValidator,
10 runnersSortValidator,
11 setDefaultPagination,
12 setDefaultSort
13} from '@server/middlewares'
14import { deleteRunnerValidator, getRunnerFromTokenValidator, registerRunnerValidator } from '@server/middlewares/validators/runners'
15import { RunnerModel } from '@server/models/runner/runner'
16import { HttpStatusCode, ListRunnersQuery, RegisterRunnerBody, UserRight } from '@shared/models'
17
18const lTags = loggerTagsFactory('api', 'runner')
19
20const manageRunnersRouter = express.Router()
21
22manageRunnersRouter.post('/register',
23 apiRateLimiter,
24 asyncMiddleware(registerRunnerValidator),
25 asyncMiddleware(registerRunner)
26)
27manageRunnersRouter.post('/unregister',
28 apiRateLimiter,
29 asyncMiddleware(getRunnerFromTokenValidator),
30 asyncMiddleware(unregisterRunner)
31)
32
33manageRunnersRouter.delete('/:runnerId',
34 apiRateLimiter,
35 authenticate,
36 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
37 asyncMiddleware(deleteRunnerValidator),
38 asyncMiddleware(deleteRunner)
39)
40
41manageRunnersRouter.get('/',
42 apiRateLimiter,
43 authenticate,
44 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
45 paginationValidator,
46 runnersSortValidator,
47 setDefaultSort,
48 setDefaultPagination,
49 asyncMiddleware(listRunners)
50)
51
52// ---------------------------------------------------------------------------
53
54export {
55 manageRunnersRouter
56}
57
58// ---------------------------------------------------------------------------
59
60async function registerRunner (req: express.Request, res: express.Response) {
61 const body: RegisterRunnerBody = req.body
62
63 const runnerToken = generateRunnerToken()
64
65 const runner = new RunnerModel({
66 runnerToken,
67 name: body.name,
68 description: body.description,
69 lastContact: new Date(),
70 ip: req.ip,
71 runnerRegistrationTokenId: res.locals.runnerRegistrationToken.id
72 })
73
74 await runner.save()
75
76 logger.info('Registered new runner %s', runner.name, { ...lTags(runner.name) })
77
78 return res.json({ id: runner.id, runnerToken })
79}
80async function unregisterRunner (req: express.Request, res: express.Response) {
81 const runner = res.locals.runner
82 await runner.destroy()
83
84 logger.info('Unregistered runner %s', runner.name, { ...lTags(runner.name) })
85
86 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
87}
88
89async function deleteRunner (req: express.Request, res: express.Response) {
90 const runner = res.locals.runner
91
92 await runner.destroy()
93
94 logger.info('Deleted runner %s', runner.name, { ...lTags(runner.name) })
95
96 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
97}
98
99async function listRunners (req: express.Request, res: express.Response) {
100 const query: ListRunnersQuery = req.query
101
102 const resultList = await RunnerModel.listForApi({
103 start: query.start,
104 count: query.count,
105 sort: query.sort
106 })
107
108 return res.json({
109 total: resultList.total,
110 data: resultList.data.map(d => d.toFormattedJSON())
111 })
112}
diff --git a/server/controllers/api/runners/registration-tokens.ts b/server/controllers/api/runners/registration-tokens.ts
deleted file mode 100644
index 117ff271b..000000000
--- a/server/controllers/api/runners/registration-tokens.ts
+++ /dev/null
@@ -1,91 +0,0 @@
1import express from 'express'
2import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { generateRunnerRegistrationToken } from '@server/helpers/token-generator'
4import {
5 apiRateLimiter,
6 asyncMiddleware,
7 authenticate,
8 ensureUserHasRight,
9 paginationValidator,
10 runnerRegistrationTokensSortValidator,
11 setDefaultPagination,
12 setDefaultSort
13} from '@server/middlewares'
14import { deleteRegistrationTokenValidator } from '@server/middlewares/validators/runners'
15import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token'
16import { HttpStatusCode, ListRunnerRegistrationTokensQuery, UserRight } from '@shared/models'
17
18const lTags = loggerTagsFactory('api', 'runner')
19
20const runnerRegistrationTokensRouter = express.Router()
21
22runnerRegistrationTokensRouter.post('/registration-tokens/generate',
23 apiRateLimiter,
24 authenticate,
25 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
26 asyncMiddleware(generateRegistrationToken)
27)
28
29runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
30 apiRateLimiter,
31 authenticate,
32 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
33 asyncMiddleware(deleteRegistrationTokenValidator),
34 asyncMiddleware(deleteRegistrationToken)
35)
36
37runnerRegistrationTokensRouter.get('/registration-tokens',
38 apiRateLimiter,
39 authenticate,
40 ensureUserHasRight(UserRight.MANAGE_RUNNERS),
41 paginationValidator,
42 runnerRegistrationTokensSortValidator,
43 setDefaultSort,
44 setDefaultPagination,
45 asyncMiddleware(listRegistrationTokens)
46)
47
48// ---------------------------------------------------------------------------
49
50export {
51 runnerRegistrationTokensRouter
52}
53
54// ---------------------------------------------------------------------------
55
56async function generateRegistrationToken (req: express.Request, res: express.Response) {
57 logger.info('Generating new runner registration token.', lTags())
58
59 const registrationToken = new RunnerRegistrationTokenModel({
60 registrationToken: generateRunnerRegistrationToken()
61 })
62
63 await registrationToken.save()
64
65 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
66}
67
68async function deleteRegistrationToken (req: express.Request, res: express.Response) {
69 logger.info('Removing runner registration token.', lTags())
70
71 const runnerRegistrationToken = res.locals.runnerRegistrationToken
72
73 await runnerRegistrationToken.destroy()
74
75 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
76}
77
78async function listRegistrationTokens (req: express.Request, res: express.Response) {
79 const query: ListRunnerRegistrationTokensQuery = req.query
80
81 const resultList = await RunnerRegistrationTokenModel.listForApi({
82 start: query.start,
83 count: query.count,
84 sort: query.sort
85 })
86
87 return res.json({
88 total: resultList.total,
89 data: resultList.data.map(d => d.toFormattedJSON())
90 })
91}