@frkntmbs/strapi-plugin-video-optimizer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/admin/custom.d.ts +8 -0
  4. package/admin/src/buildVersion.ts +3 -0
  5. package/admin/src/components/AssetOptimizationLabel.tsx +61 -0
  6. package/admin/src/components/BridgeProviders.tsx +123 -0
  7. package/admin/src/components/MediaLibraryCacheBridge.tsx +24 -0
  8. package/admin/src/components/MediaLibraryCardActionsBridge.tsx +249 -0
  9. package/admin/src/components/MediaLibraryJobWatcher.tsx +136 -0
  10. package/admin/src/components/MediaLibraryProgressBridge.tsx +97 -0
  11. package/admin/src/components/OptimizationChoicePicker.tsx +65 -0
  12. package/admin/src/components/OptimizationResizeFields.tsx +120 -0
  13. package/admin/src/components/OptimizationVideoFields.tsx +217 -0
  14. package/admin/src/components/UploadEnhancerBridge.tsx +205 -0
  15. package/admin/src/components/upload/PendingAssetStep.tsx +97 -0
  16. package/admin/src/defaultGlobalSettings.ts +32 -0
  17. package/admin/src/hooks/useDefaultOptimizationMode.ts +24 -0
  18. package/admin/src/hooks/useUploadWithOptimizer.ts +45 -0
  19. package/admin/src/index.ts +84 -0
  20. package/admin/src/pages/SettingsPage.tsx +208 -0
  21. package/admin/src/pluginId.ts +79 -0
  22. package/admin/src/translations/en.json +74 -0
  23. package/admin/src/translations/tr.json +74 -0
  24. package/admin/src/utils/adminFetch.ts +57 -0
  25. package/admin/src/utils/captureQueryClient.ts +34 -0
  26. package/admin/src/utils/debugMediaLibraryProgress.ts +70 -0
  27. package/admin/src/utils/extractAssetDimensions.ts +22 -0
  28. package/admin/src/utils/initJobPoller.ts +173 -0
  29. package/admin/src/utils/initMediaLibraryCardActions.ts +308 -0
  30. package/admin/src/utils/initMediaLibraryProgress.ts +219 -0
  31. package/admin/src/utils/initUploadEnhancer.ts +447 -0
  32. package/admin/src/utils/invalidateMediaLibrary.ts +203 -0
  33. package/admin/src/utils/jobProgressStore.ts +113 -0
  34. package/admin/src/utils/mediaLibraryCardMatch.ts +414 -0
  35. package/admin/src/utils/mediaLibraryCardStore.ts +223 -0
  36. package/admin/src/utils/mediaLibraryQueryBridge.ts +113 -0
  37. package/admin/src/utils/mediaLibraryRoute.ts +9 -0
  38. package/admin/src/utils/optimizationFields.ts +17 -0
  39. package/admin/src/utils/probeVideoDimensions.ts +94 -0
  40. package/admin/src/utils/uploadAssetStore.ts +670 -0
  41. package/admin/tsconfig.json +8 -0
  42. package/dist/admin/SettingsPage-CN2fR83m.js +150 -0
  43. package/dist/admin/SettingsPage-D6e536P0.mjs +150 -0
  44. package/dist/admin/en-CqM903j3.js +77 -0
  45. package/dist/admin/en-CsHicGzL.mjs +77 -0
  46. package/dist/admin/index-BjWoS0YU.js +2542 -0
  47. package/dist/admin/index-Cs_uiChW.mjs +2541 -0
  48. package/dist/admin/index-DOuHOS2G.js +8799 -0
  49. package/dist/admin/index-rAmxCQz6.mjs +8781 -0
  50. package/dist/admin/index.js +4 -0
  51. package/dist/admin/index.mjs +4 -0
  52. package/dist/admin/tr-Y0-ANilh.mjs +77 -0
  53. package/dist/admin/tr-muzHkdC4.js +77 -0
  54. package/dist/server/index.js +1538 -0
  55. package/dist/server/index.mjs +1533 -0
  56. package/package.json +100 -0
  57. package/server/index.js +1 -0
  58. package/server/src/bootstrap.ts +377 -0
  59. package/server/src/buildVersion.ts +1 -0
  60. package/server/src/config/defaults.ts +91 -0
  61. package/server/src/config/index.ts +51 -0
  62. package/server/src/constants.ts +83 -0
  63. package/server/src/controllers/index.ts +7 -0
  64. package/server/src/controllers/job.ts +102 -0
  65. package/server/src/controllers/preference.ts +206 -0
  66. package/server/src/index.ts +15 -0
  67. package/server/src/register.ts +19 -0
  68. package/server/src/routes/index.ts +103 -0
  69. package/server/src/services/index.ts +9 -0
  70. package/server/src/services/job-queue.ts +663 -0
  71. package/server/src/services/optimizer.ts +284 -0
  72. package/server/src/services/preference.ts +172 -0
  73. package/server/src/utils/request-context.ts +7 -0
  74. package/server/src/utils/upload-preferences-context.ts +202 -0
  75. package/server/tsconfig.json +8 -0
  76. package/strapi-admin.js +7 -0
  77. package/strapi-server.js +7 -0
@@ -0,0 +1,83 @@
1
+ export const PLUGIN_ID = 'video-optimizer';
2
+
3
+ export type OptimizationChoice = 'original' | 'global' | 'custom';
4
+ export type VideoFormat = 'mp4' | 'webm';
5
+ export type VideoCodec = 'h264' | 'vp9';
6
+ export type AudioMode = 'keep' | 'remove' | 'compress';
7
+ export type FfmpegPreset =
8
+ | 'ultrafast'
9
+ | 'superfast'
10
+ | 'veryfast'
11
+ | 'faster'
12
+ | 'fast'
13
+ | 'medium'
14
+ | 'slow'
15
+ | 'slower'
16
+ | 'veryslow';
17
+
18
+ export type JobStatus = 'queued' | 'processing' | 'completed' | 'failed';
19
+
20
+ export const OPTIMIZATION_CHOICES: OptimizationChoice[] = ['original', 'global', 'custom'];
21
+ export const VIDEO_FORMATS: VideoFormat[] = ['mp4', 'webm'];
22
+ export const VIDEO_CODECS: VideoCodec[] = ['h264', 'vp9'];
23
+ export const AUDIO_MODES: AudioMode[] = ['keep', 'remove', 'compress'];
24
+ export const FFMPEG_PRESETS: FfmpegPreset[] = [
25
+ 'ultrafast',
26
+ 'superfast',
27
+ 'veryfast',
28
+ 'faster',
29
+ 'fast',
30
+ 'medium',
31
+ 'slow',
32
+ 'slower',
33
+ 'veryslow',
34
+ ];
35
+ export const JOB_STATUSES: JobStatus[] = ['queued', 'processing', 'completed', 'failed'];
36
+
37
+ export const MAX_CONCURRENT_JOBS_LIMIT = 32;
38
+ export const MAX_FFMPEG_THREADS_LIMIT = 8;
39
+
40
+ export const clampMaxConcurrentJobs = (value: number) =>
41
+ Math.min(MAX_CONCURRENT_JOBS_LIMIT, Math.max(1, Math.round(value)));
42
+
43
+ export const clampMaxFfmpegThreads = (value: number) =>
44
+ Math.min(MAX_FFMPEG_THREADS_LIMIT, Math.max(1, Math.round(value)));
45
+
46
+ export interface OptimizationSettings {
47
+ defaultFormat: VideoFormat;
48
+ videoCodec: VideoCodec;
49
+ crf: number;
50
+ preset: FfmpegPreset;
51
+ maxWidth: number;
52
+ maxHeight: number;
53
+ audioMode: AudioMode;
54
+ audioBitrate: string;
55
+ }
56
+
57
+ export interface ResolvedOptimization {
58
+ skip: boolean;
59
+ settings?: OptimizationSettings;
60
+ }
61
+
62
+ export interface VideoOptimizerJob {
63
+ id: string;
64
+ fileId: number;
65
+ fileName?: string;
66
+ fileHash?: string;
67
+ status: JobStatus;
68
+ stage: string;
69
+ progress: number;
70
+ settings?: OptimizationSettings;
71
+ error?: string;
72
+ createdAt: string;
73
+ updatedAt: string;
74
+ }
75
+
76
+ export const isVideoMime = (mime: unknown): boolean =>
77
+ typeof mime === 'string' && mime.startsWith('video/');
78
+
79
+ export const codecForFormat = (format: VideoFormat): VideoCodec =>
80
+ format === 'webm' ? 'vp9' : 'h264';
81
+
82
+ export const formatForCodec = (codec: VideoCodec): VideoFormat =>
83
+ codec === 'vp9' ? 'webm' : 'mp4';
@@ -0,0 +1,7 @@
1
+ import preference from './preference';
2
+ import job from './job';
3
+
4
+ export default {
5
+ preference,
6
+ job,
7
+ };
@@ -0,0 +1,102 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import {
3
+ OPTIMIZATION_CHOICES,
4
+ type OptimizationChoice,
5
+ type OptimizationSettings,
6
+ } from '../constants';
7
+
8
+ const isValidChoice = (choice: unknown): choice is OptimizationChoice =>
9
+ typeof choice === 'string' && OPTIMIZATION_CHOICES.includes(choice as OptimizationChoice);
10
+
11
+ export default ({ strapi }: { strapi: Core.Strapi }) => ({
12
+ async getJob(ctx) {
13
+ const { id } = ctx.params;
14
+ const job = await strapi.plugin('video-optimizer').service('job-queue').getJob(id);
15
+
16
+ if (!job) {
17
+ return ctx.notFound('Job not found');
18
+ }
19
+
20
+ ctx.body = job;
21
+ },
22
+
23
+ async getJobsByFiles(ctx) {
24
+ const raw = ctx.query.fileIds;
25
+
26
+ if (!raw || typeof raw !== 'string') {
27
+ return ctx.badRequest('fileIds query parameter is required');
28
+ }
29
+
30
+ const fileIds = raw
31
+ .split(',')
32
+ .map((value) => Number(value.trim()))
33
+ .filter((value) => Number.isFinite(value) && value > 0);
34
+
35
+ const jobs = await strapi.plugin('video-optimizer').service('job-queue').getJobsByFileIds(fileIds);
36
+ ctx.body = { jobs };
37
+ },
38
+
39
+ async listActive(ctx) {
40
+ const jobs = await strapi.plugin('video-optimizer').service('job-queue').listActiveJobs();
41
+ ctx.body = { jobs };
42
+ },
43
+
44
+ async enqueueFile(ctx) {
45
+ const body = ctx.request.body as {
46
+ fileId?: number | string;
47
+ optimizationChoice?: OptimizationChoice;
48
+ optimizationCustom?: Partial<OptimizationSettings>;
49
+ };
50
+
51
+ const fileId = Number(body.fileId);
52
+
53
+ if (!Number.isFinite(fileId) || fileId <= 0) {
54
+ return ctx.badRequest('fileId is required');
55
+ }
56
+
57
+ const file = await strapi.db.query('plugin::upload.file').findOne({
58
+ where: { id: fileId },
59
+ });
60
+
61
+ if (!file) {
62
+ return ctx.notFound('File not found');
63
+ }
64
+
65
+ if (!file.mime || !String(file.mime).startsWith('video/')) {
66
+ return ctx.badRequest('Only video files can be optimized');
67
+ }
68
+
69
+ const preferenceService = strapi.plugin('video-optimizer').service('preference');
70
+ const resolved = await preferenceService.resolveOptimization({
71
+ optimizationChoice: isValidChoice(body.optimizationChoice)
72
+ ? body.optimizationChoice
73
+ : undefined,
74
+ optimizationCustom: body.optimizationCustom,
75
+ mime: String(file.mime),
76
+ });
77
+
78
+ if (resolved.skip || !resolved.settings) {
79
+ return ctx.badRequest('Optimization is disabled for the selected choice');
80
+ }
81
+
82
+ const job = await strapi.plugin('video-optimizer').service('job-queue').enqueue({
83
+ fileId,
84
+ settings: resolved.settings,
85
+ choice: isValidChoice(body.optimizationChoice) ? body.optimizationChoice : undefined,
86
+ });
87
+
88
+ ctx.body = { job };
89
+ },
90
+
91
+ async cancelForFile(ctx) {
92
+ const body = ctx.request.body as { fileId?: number | string };
93
+ const fileId = Number(body.fileId);
94
+
95
+ if (!Number.isFinite(fileId) || fileId <= 0) {
96
+ return ctx.badRequest('fileId is required');
97
+ }
98
+
99
+ await strapi.plugin('video-optimizer').service('job-queue').cancelJobsForFile(fileId);
100
+ ctx.body = { ok: true };
101
+ },
102
+ });
@@ -0,0 +1,206 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import {
3
+ AUDIO_MODES,
4
+ FFMPEG_PRESETS,
5
+ OPTIMIZATION_CHOICES,
6
+ MAX_CONCURRENT_JOBS_LIMIT,
7
+ MAX_FFMPEG_THREADS_LIMIT,
8
+ VIDEO_CODECS,
9
+ VIDEO_FORMATS,
10
+ clampMaxConcurrentJobs,
11
+ clampMaxFfmpegThreads,
12
+ codecForFormat,
13
+ type AudioMode,
14
+ type FfmpegPreset,
15
+ type OptimizationChoice,
16
+ type OptimizationSettings,
17
+ type VideoCodec,
18
+ type VideoFormat,
19
+ } from '../constants';
20
+
21
+ const isValidChoice = (choice: unknown): choice is OptimizationChoice =>
22
+ typeof choice === 'string' && OPTIMIZATION_CHOICES.includes(choice as OptimizationChoice);
23
+
24
+ const isValidFormat = (format: unknown): format is VideoFormat =>
25
+ typeof format === 'string' && VIDEO_FORMATS.includes(format as VideoFormat);
26
+
27
+ const isValidCodec = (codec: unknown): codec is VideoCodec =>
28
+ typeof codec === 'string' && VIDEO_CODECS.includes(codec as VideoCodec);
29
+
30
+ const isValidAudioMode = (mode: unknown): mode is AudioMode =>
31
+ typeof mode === 'string' && AUDIO_MODES.includes(mode as AudioMode);
32
+
33
+ const isValidPreset = (preset: unknown): preset is FfmpegPreset =>
34
+ typeof preset === 'string' && FFMPEG_PRESETS.includes(preset as FfmpegPreset);
35
+
36
+ const isValidCustomSettings = (custom: unknown): custom is OptimizationSettings => {
37
+ if (!custom || typeof custom !== 'object') {
38
+ return false;
39
+ }
40
+
41
+ const value = custom as Partial<OptimizationSettings>;
42
+
43
+ return (
44
+ isValidFormat(value.defaultFormat) &&
45
+ isValidCodec(value.videoCodec) &&
46
+ typeof value.crf === 'number' &&
47
+ value.crf >= 0 &&
48
+ value.crf <= 51 &&
49
+ isValidPreset(value.preset) &&
50
+ typeof value.maxWidth === 'number' &&
51
+ value.maxWidth >= 1 &&
52
+ typeof value.maxHeight === 'number' &&
53
+ value.maxHeight >= 1 &&
54
+ isValidAudioMode(value.audioMode) &&
55
+ typeof value.audioBitrate === 'string'
56
+ );
57
+ };
58
+
59
+ export default ({ strapi }: { strapi: Core.Strapi }) => ({
60
+ async getDefaultMode(ctx) {
61
+ const settings = await strapi.plugin('video-optimizer').service('preference').getGlobalSettings();
62
+ ctx.body = {
63
+ defaultChoice: settings.defaultChoice,
64
+ defaultFormat: settings.defaultFormat,
65
+ videoCodec: settings.videoCodec,
66
+ crf: settings.crf,
67
+ preset: settings.preset,
68
+ maxWidth: settings.maxWidth,
69
+ maxHeight: settings.maxHeight,
70
+ audioMode: settings.audioMode,
71
+ audioBitrate: settings.audioBitrate,
72
+ maxConcurrentJobs: settings.maxConcurrentJobs,
73
+ maxFfmpegThreads: settings.maxFfmpegThreads,
74
+ };
75
+ },
76
+
77
+ async getPreference(ctx) {
78
+ const userId = ctx.state.user?.id;
79
+
80
+ if (!userId) {
81
+ return ctx.unauthorized();
82
+ }
83
+
84
+ const preferenceService = strapi.plugin('video-optimizer').service('preference');
85
+ const defaultFormat = await preferenceService.getUserPreference(userId);
86
+ const settings = await preferenceService.getGlobalSettings();
87
+
88
+ ctx.body = {
89
+ defaultFormat: defaultFormat ?? settings.defaultFormat,
90
+ defaultChoice: settings.defaultChoice,
91
+ };
92
+ },
93
+
94
+ async updatePreference(ctx) {
95
+ const userId = ctx.state.user?.id;
96
+
97
+ if (!userId) {
98
+ return ctx.unauthorized();
99
+ }
100
+
101
+ const { defaultFormat } = ctx.request.body ?? {};
102
+
103
+ if (!isValidFormat(defaultFormat)) {
104
+ return ctx.badRequest('Invalid video format');
105
+ }
106
+
107
+ await strapi.plugin('video-optimizer').service('preference').setUserPreference(userId, defaultFormat);
108
+
109
+ ctx.body = { defaultFormat };
110
+ },
111
+
112
+ async getSettings(ctx) {
113
+ const settings = await strapi.plugin('video-optimizer').service('preference').getGlobalSettings();
114
+ ctx.body = settings;
115
+ },
116
+
117
+ async updateSettings(ctx) {
118
+ const {
119
+ defaultChoice,
120
+ defaultFormat,
121
+ videoCodec,
122
+ crf,
123
+ preset,
124
+ maxWidth,
125
+ maxHeight,
126
+ audioMode,
127
+ audioBitrate,
128
+ maxConcurrentJobs,
129
+ maxFfmpegThreads,
130
+ } = ctx.request.body ?? {};
131
+
132
+ if (defaultChoice !== undefined && !isValidChoice(defaultChoice)) {
133
+ return ctx.badRequest('Invalid default choice');
134
+ }
135
+
136
+ if (defaultFormat !== undefined && !isValidFormat(defaultFormat)) {
137
+ return ctx.badRequest('Invalid default format');
138
+ }
139
+
140
+ if (videoCodec !== undefined && !isValidCodec(videoCodec)) {
141
+ return ctx.badRequest('Invalid video codec');
142
+ }
143
+
144
+ if (crf !== undefined && (crf < 0 || crf > 51)) {
145
+ return ctx.badRequest('Invalid CRF value');
146
+ }
147
+
148
+ if (preset !== undefined && !isValidPreset(preset)) {
149
+ return ctx.badRequest('Invalid preset');
150
+ }
151
+
152
+ if (maxWidth !== undefined && maxWidth < 1) {
153
+ return ctx.badRequest('Invalid max width');
154
+ }
155
+
156
+ if (maxHeight !== undefined && maxHeight < 1) {
157
+ return ctx.badRequest('Invalid max height');
158
+ }
159
+
160
+ if (audioMode !== undefined && !isValidAudioMode(audioMode)) {
161
+ return ctx.badRequest('Invalid audio mode');
162
+ }
163
+
164
+ if (
165
+ maxConcurrentJobs !== undefined &&
166
+ clampMaxConcurrentJobs(Number(maxConcurrentJobs)) !== Number(maxConcurrentJobs)
167
+ ) {
168
+ return ctx.badRequest(`maxConcurrentJobs must be between 1 and ${MAX_CONCURRENT_JOBS_LIMIT}`);
169
+ }
170
+
171
+ if (
172
+ maxFfmpegThreads !== undefined &&
173
+ clampMaxFfmpegThreads(Number(maxFfmpegThreads)) !== Number(maxFfmpegThreads)
174
+ ) {
175
+ return ctx.badRequest(`maxFfmpegThreads must be between 1 and ${MAX_FFMPEG_THREADS_LIMIT}`);
176
+ }
177
+
178
+ const payload: Record<string, unknown> = {};
179
+
180
+ if (defaultChoice !== undefined) payload.defaultChoice = defaultChoice;
181
+ if (defaultFormat !== undefined) {
182
+ payload.defaultFormat = defaultFormat;
183
+ if (videoCodec === undefined) {
184
+ payload.videoCodec = codecForFormat(defaultFormat);
185
+ }
186
+ }
187
+ if (videoCodec !== undefined) payload.videoCodec = videoCodec;
188
+ if (crf !== undefined) payload.crf = Number(crf);
189
+ if (preset !== undefined) payload.preset = preset;
190
+ if (maxWidth !== undefined) payload.maxWidth = Number(maxWidth);
191
+ if (maxHeight !== undefined) payload.maxHeight = Number(maxHeight);
192
+ if (audioMode !== undefined) payload.audioMode = audioMode;
193
+ if (audioBitrate !== undefined) payload.audioBitrate = String(audioBitrate);
194
+ if (maxConcurrentJobs !== undefined) payload.maxConcurrentJobs = Number(maxConcurrentJobs);
195
+ if (maxFfmpegThreads !== undefined) payload.maxFfmpegThreads = Number(maxFfmpegThreads);
196
+
197
+ await strapi.plugin('video-optimizer').service('preference').setGlobalSettings(payload);
198
+
199
+ if (maxConcurrentJobs !== undefined) {
200
+ void strapi.plugin('video-optimizer').service('job-queue').drainQueue();
201
+ }
202
+
203
+ const settings = await strapi.plugin('video-optimizer').service('preference').getGlobalSettings();
204
+ ctx.body = settings;
205
+ },
206
+ });
@@ -0,0 +1,15 @@
1
+ import register from './register';
2
+ import bootstrap from './bootstrap';
3
+ import config from './config';
4
+ import controllers from './controllers';
5
+ import routes from './routes';
6
+ import services from './services';
7
+
8
+ export default {
9
+ register,
10
+ bootstrap,
11
+ config,
12
+ controllers,
13
+ routes,
14
+ services,
15
+ };
@@ -0,0 +1,19 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import { PLUGIN_ID } from './constants';
3
+
4
+ export default async ({ strapi }: { strapi: Core.Strapi }) => {
5
+ await strapi.admin.services.permission.actionProvider.registerMany([
6
+ {
7
+ section: 'plugins',
8
+ displayName: 'Read Video Optimizer settings',
9
+ uid: 'settings.read',
10
+ pluginName: PLUGIN_ID,
11
+ },
12
+ {
13
+ section: 'plugins',
14
+ displayName: 'Update Video Optimizer settings',
15
+ uid: 'settings.update',
16
+ pluginName: PLUGIN_ID,
17
+ },
18
+ ]);
19
+ };
@@ -0,0 +1,103 @@
1
+ export default {
2
+ admin: {
3
+ type: 'admin',
4
+ routes: [
5
+ {
6
+ method: 'GET',
7
+ path: '/default-mode',
8
+ handler: 'preference.getDefaultMode',
9
+ config: {
10
+ policies: ['admin::isAuthenticatedAdmin'],
11
+ },
12
+ },
13
+ {
14
+ method: 'GET',
15
+ path: '/preference',
16
+ handler: 'preference.getPreference',
17
+ config: {
18
+ policies: ['admin::isAuthenticatedAdmin'],
19
+ },
20
+ },
21
+ {
22
+ method: 'PUT',
23
+ path: '/preference',
24
+ handler: 'preference.updatePreference',
25
+ config: {
26
+ policies: ['admin::isAuthenticatedAdmin'],
27
+ },
28
+ },
29
+ {
30
+ method: 'GET',
31
+ path: '/settings',
32
+ handler: 'preference.getSettings',
33
+ config: {
34
+ policies: [
35
+ 'admin::isAuthenticatedAdmin',
36
+ {
37
+ name: 'admin::hasPermissions',
38
+ config: {
39
+ actions: ['plugin::video-optimizer.settings.read'],
40
+ },
41
+ },
42
+ ],
43
+ },
44
+ },
45
+ {
46
+ method: 'PUT',
47
+ path: '/settings',
48
+ handler: 'preference.updateSettings',
49
+ config: {
50
+ policies: [
51
+ 'admin::isAuthenticatedAdmin',
52
+ {
53
+ name: 'admin::hasPermissions',
54
+ config: {
55
+ actions: ['plugin::video-optimizer.settings.update'],
56
+ },
57
+ },
58
+ ],
59
+ },
60
+ },
61
+ {
62
+ method: 'GET',
63
+ path: '/jobs/active',
64
+ handler: 'job.listActive',
65
+ config: {
66
+ policies: ['admin::isAuthenticatedAdmin'],
67
+ },
68
+ },
69
+ {
70
+ method: 'GET',
71
+ path: '/jobs/by-files',
72
+ handler: 'job.getJobsByFiles',
73
+ config: {
74
+ policies: ['admin::isAuthenticatedAdmin'],
75
+ },
76
+ },
77
+ {
78
+ method: 'POST',
79
+ path: '/jobs/enqueue',
80
+ handler: 'job.enqueueFile',
81
+ config: {
82
+ policies: ['admin::isAuthenticatedAdmin'],
83
+ },
84
+ },
85
+ {
86
+ method: 'POST',
87
+ path: '/jobs/cancel',
88
+ handler: 'job.cancelForFile',
89
+ config: {
90
+ policies: ['admin::isAuthenticatedAdmin'],
91
+ },
92
+ },
93
+ {
94
+ method: 'GET',
95
+ path: '/jobs/:id',
96
+ handler: 'job.getJob',
97
+ config: {
98
+ policies: ['admin::isAuthenticatedAdmin'],
99
+ },
100
+ },
101
+ ],
102
+ },
103
+ };
@@ -0,0 +1,9 @@
1
+ import preference from './preference';
2
+ import optimizer from './optimizer';
3
+ import jobQueue from './job-queue';
4
+
5
+ export default {
6
+ preference,
7
+ optimizer,
8
+ 'job-queue': jobQueue,
9
+ };