aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares
diff options
context:
space:
mode:
authorFlorent <florent.git@zeteo.me>2022-08-10 09:53:39 +0200
committerGitHub <noreply@github.com>2022-08-10 09:53:39 +0200
commit2a491182e483b97afb1b65c908b23cb48d591807 (patch)
treeec13503216ad72a3ea8f1ce3b659899f8167fb47 /server/middlewares
parent06ac128958c489efe1008eeca1df683819bd2f18 (diff)
downloadPeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.gz
PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.zst
PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.zip
Channel sync (#5135)
* Add external channel URL for channel update / creation (#754) * Disallow synchronisation if user has no video quota (#754) * More constraints serverside (#754) * Disable sync if server configuration does not allow HTTP import (#754) * Working version synchronizing videos with a job (#754) TODO: refactoring, too much code duplication * More logs and try/catch (#754) * Fix eslint error (#754) * WIP: support synchronization time change (#754) * New frontend #754 * WIP: Create sync front (#754) * Enhance UI, sync creation form (#754) * Warning message when HTTP upload is disallowed * More consistent names (#754) * Binding Front with API (#754) * Add a /me API (#754) * Improve list UI (#754) * Implement creation and deletion routes (#754) * Lint (#754) * Lint again (#754) * WIP: UI for triggering import existing videos (#754) * Implement jobs for syncing and importing channels * Don't sync videos before sync creation + avoid concurrency issue (#754) * Cleanup (#754) * Cleanup: OpenAPI + API rework (#754) * Remove dead code (#754) * Eslint (#754) * Revert the mess with whitespaces in constants.ts (#754) * Some fixes after rebase (#754) * Several fixes after PR remarks (#754) * Front + API: Rename video-channels-sync to video-channel-syncs (#754) * Allow enabling channel sync through UI (#754) * getChannelInfo (#754) * Minor fixes: openapi + model + sql (#754) * Simplified API validators (#754) * Rename MChannelSync to MChannelSyncChannel (#754) * Add command for VideoChannelSync (#754) * Use synchronization.enabled config (#754) * Check parameters test + some fixes (#754) * Fix conflict mistake (#754) * Restrict access to video channel sync list API (#754) * Start adding unit test for synchronization (#754) * Continue testing (#754) * Tests finished + convertion of job to scheduler (#754) * Add lastSyncAt field (#754) * Fix externalRemoteUrl sort + creation date not well formatted (#754) * Small fix (#754) * Factorize addYoutubeDLImport and buildVideo (#754) * Check duplicates on channel not on users (#754) * factorize thumbnail generation (#754) * Fetch error should return status 400 (#754) * Separate video-channel-import and video-channel-sync-latest (#754) * Bump DB migration version after rebase (#754) * Prettier states in UI table (#754) * Add DefaultScope in VideoChannelSyncModel (#754) * Fix audit logs (#754) * Ensure user can upload when importing channel + minor fixes (#754) * Mark synchronization as failed on exception + typos (#754) * Change REST API for importing videos into channel (#754) * Add option for fully synchronize a chnanel (#754) * Return a whole sync object on creation to avoid tricks in Front (#754) * Various remarks (#754) * Single quotes by default (#754) * Rename synchronization to video_channel_synchronization * Add check.latest_videos_count and max_per_user options (#754) * Better channel rendering in list #754 * Allow sorting with channel name and state (#754) * Add missing tests for channel imports (#754) * Prefer using a parent job for channel sync * Styling * Client styling Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server/middlewares')
-rw-r--r--server/middlewares/validators/config.ts11
-rw-r--r--server/middlewares/validators/sort.ts2
-rw-r--r--server/middlewares/validators/videos/index.ts1
-rw-r--r--server/middlewares/validators/videos/video-channel-sync.ts66
-rw-r--r--server/middlewares/validators/videos/video-channels.ts55
5 files changed, 117 insertions, 18 deletions
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index 9ce47c5aa..f60103f48 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -66,6 +66,8 @@ const customConfigUpdateValidator = [
66 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'), 66 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'),
67 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'), 67 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'),
68 68
69 body('import.videoChannelSynchronization.enabled').isBoolean().withMessage('Should have a valid synchronization enabled boolean'),
70
69 body('trending.videos.algorithms.default').exists().withMessage('Should have a valid default trending algorithm'), 71 body('trending.videos.algorithms.default').exists().withMessage('Should have a valid default trending algorithm'),
70 body('trending.videos.algorithms.enabled').exists().withMessage('Should have a valid array of enabled trending algorithms'), 72 body('trending.videos.algorithms.enabled').exists().withMessage('Should have a valid array of enabled trending algorithms'),
71 73
@@ -110,6 +112,7 @@ const customConfigUpdateValidator = [
110 if (areValidationErrors(req, res)) return 112 if (areValidationErrors(req, res)) return
111 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return 113 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
112 if (!checkInvalidTranscodingConfig(req.body, res)) return 114 if (!checkInvalidTranscodingConfig(req.body, res)) return
115 if (!checkInvalidSynchronizationConfig(req.body, res)) return
113 if (!checkInvalidLiveConfig(req.body, res)) return 116 if (!checkInvalidLiveConfig(req.body, res)) return
114 if (!checkInvalidVideoStudioConfig(req.body, res)) return 117 if (!checkInvalidVideoStudioConfig(req.body, res)) return
115 118
@@ -157,6 +160,14 @@ function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express
157 return true 160 return true
158} 161}
159 162
163function checkInvalidSynchronizationConfig (customConfig: CustomConfig, res: express.Response) {
164 if (customConfig.import.videoChannelSynchronization.enabled && !customConfig.import.videos.http.enabled) {
165 res.fail({ message: 'You need to enable HTTP video import in order to enable channel synchronization' })
166 return false
167 }
168 return true
169}
170
160function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) { 171function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) {
161 if (customConfig.live.enabled === false) return true 172 if (customConfig.live.enabled === false) return true
162 173
diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts
index c9978e3b4..0354e3fc6 100644
--- a/server/middlewares/validators/sort.ts
+++ b/server/middlewares/validators/sort.ts
@@ -52,6 +52,7 @@ const videoPlaylistsSortValidator = checkSortFactory(SORTABLE_COLUMNS.VIDEO_PLAY
52const pluginsSortValidator = checkSortFactory(SORTABLE_COLUMNS.PLUGINS) 52const pluginsSortValidator = checkSortFactory(SORTABLE_COLUMNS.PLUGINS)
53const availablePluginsSortValidator = checkSortFactory(SORTABLE_COLUMNS.AVAILABLE_PLUGINS) 53const availablePluginsSortValidator = checkSortFactory(SORTABLE_COLUMNS.AVAILABLE_PLUGINS)
54const videoRedundanciesSortValidator = checkSortFactory(SORTABLE_COLUMNS.VIDEO_REDUNDANCIES) 54const videoRedundanciesSortValidator = checkSortFactory(SORTABLE_COLUMNS.VIDEO_REDUNDANCIES)
55const videoChannelSyncsSortValidator = checkSortFactory(SORTABLE_COLUMNS.VIDEO_CHANNEL_SYNCS)
55 56
56const accountsFollowersSortValidator = checkSortFactory(SORTABLE_COLUMNS.ACCOUNT_FOLLOWERS) 57const accountsFollowersSortValidator = checkSortFactory(SORTABLE_COLUMNS.ACCOUNT_FOLLOWERS)
57const videoChannelsFollowersSortValidator = checkSortFactory(SORTABLE_COLUMNS.CHANNEL_FOLLOWERS) 58const videoChannelsFollowersSortValidator = checkSortFactory(SORTABLE_COLUMNS.CHANNEL_FOLLOWERS)
@@ -84,5 +85,6 @@ export {
84 videoPlaylistsSearchSortValidator, 85 videoPlaylistsSearchSortValidator,
85 accountsFollowersSortValidator, 86 accountsFollowersSortValidator,
86 videoChannelsFollowersSortValidator, 87 videoChannelsFollowersSortValidator,
88 videoChannelSyncsSortValidator,
87 pluginsSortValidator 89 pluginsSortValidator
88} 90}
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts
index 1dd7b5d2e..d225dfe45 100644
--- a/server/middlewares/validators/videos/index.ts
+++ b/server/middlewares/validators/videos/index.ts
@@ -14,3 +14,4 @@ export * from './video-stats'
14export * from './video-studio' 14export * from './video-studio'
15export * from './video-transcoding' 15export * from './video-transcoding'
16export * from './videos' 16export * from './videos'
17export * from './video-channel-sync'
diff --git a/server/middlewares/validators/videos/video-channel-sync.ts b/server/middlewares/validators/videos/video-channel-sync.ts
new file mode 100644
index 000000000..b18498243
--- /dev/null
+++ b/server/middlewares/validators/videos/video-channel-sync.ts
@@ -0,0 +1,66 @@
1import * as express from 'express'
2import { body, param } from 'express-validator'
3import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
4import { logger } from '@server/helpers/logger'
5import { CONFIG } from '@server/initializers/config'
6import { VideoChannelModel } from '@server/models/video/video-channel'
7import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
8import { HttpStatusCode, VideoChannelSyncCreate } from '@shared/models'
9import { areValidationErrors, doesVideoChannelIdExist } from '../shared'
10
11export const ensureSyncIsEnabled = (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 if (!CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.ENABLED) {
13 return res.fail({
14 status: HttpStatusCode.FORBIDDEN_403,
15 message: 'Synchronization is impossible as video channel synchronization is not enabled on the server'
16 })
17 }
18
19 return next()
20}
21
22export const videoChannelSyncValidator = [
23 body('externalChannelUrl').custom(isUrlValid).withMessage('Should have a valid channel url'),
24 body('videoChannelId').isInt().withMessage('Should have a valid video channel id'),
25
26 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
27 logger.debug('Checking videoChannelSync parameters', { parameters: req.body })
28
29 if (areValidationErrors(req, res)) return
30
31 const body: VideoChannelSyncCreate = req.body
32 if (!await doesVideoChannelIdExist(body.videoChannelId, res)) return
33
34 const count = await VideoChannelSyncModel.countByAccount(res.locals.videoChannel.accountId)
35 if (count >= CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.MAX_PER_USER) {
36 return res.fail({
37 message: `You cannot create more than ${CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.MAX_PER_USER} channel synchronizations`
38 })
39 }
40
41 return next()
42 }
43]
44
45export const ensureSyncExists = [
46 param('id').exists().isInt().withMessage('Should have an sync id'),
47
48 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
49 if (areValidationErrors(req, res)) return
50
51 const syncId = parseInt(req.params.id, 10)
52 const sync = await VideoChannelSyncModel.loadWithChannel(syncId)
53
54 if (!sync) {
55 return res.fail({
56 status: HttpStatusCode.NOT_FOUND_404,
57 message: 'Synchronization not found'
58 })
59 }
60
61 res.locals.videoChannelSync = sync
62 res.locals.videoChannel = await VideoChannelModel.loadAndPopulateAccount(sync.videoChannelId)
63
64 return next()
65 }
66]
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index 3bfdebbb1..88f8b814d 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -1,5 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
3import { CONFIG } from '@server/initializers/config' 4import { CONFIG } from '@server/initializers/config'
4import { MChannelAccountDefault } from '@server/types/models' 5import { MChannelAccountDefault } from '@server/types/models'
5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
@@ -13,9 +14,9 @@ import {
13import { logger } from '../../../helpers/logger' 14import { logger } from '../../../helpers/logger'
14import { ActorModel } from '../../../models/actor/actor' 15import { ActorModel } from '../../../models/actor/actor'
15import { VideoChannelModel } from '../../../models/video/video-channel' 16import { VideoChannelModel } from '../../../models/video/video-channel'
16import { areValidationErrors, doesVideoChannelNameWithHostExist } from '../shared' 17import { areValidationErrors, checkUserQuota, doesVideoChannelNameWithHostExist } from '../shared'
17 18
18const videoChannelsAddValidator = [ 19export const videoChannelsAddValidator = [
19 body('name').custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'), 20 body('name').custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'),
20 body('displayName').custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid display name'), 21 body('displayName').custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid display name'),
21 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 22 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
@@ -45,7 +46,7 @@ const videoChannelsAddValidator = [
45 } 46 }
46] 47]
47 48
48const videoChannelsUpdateValidator = [ 49export const videoChannelsUpdateValidator = [
49 param('nameWithHost').exists().withMessage('Should have an video channel name with host'), 50 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
50 body('displayName') 51 body('displayName')
51 .optional() 52 .optional()
@@ -69,7 +70,7 @@ const videoChannelsUpdateValidator = [
69 } 70 }
70] 71]
71 72
72const videoChannelsRemoveValidator = [ 73export const videoChannelsRemoveValidator = [
73 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 74 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
74 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 75 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
75 76
@@ -79,7 +80,7 @@ const videoChannelsRemoveValidator = [
79 } 80 }
80] 81]
81 82
82const videoChannelsNameWithHostValidator = [ 83export const videoChannelsNameWithHostValidator = [
83 param('nameWithHost').exists().withMessage('Should have an video channel name with host'), 84 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
84 85
85 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 86 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
@@ -93,7 +94,7 @@ const videoChannelsNameWithHostValidator = [
93 } 94 }
94] 95]
95 96
96const ensureIsLocalChannel = [ 97export const ensureIsLocalChannel = [
97 (req: express.Request, res: express.Response, next: express.NextFunction) => { 98 (req: express.Request, res: express.Response, next: express.NextFunction) => {
98 if (res.locals.videoChannel.Actor.isOwned() === false) { 99 if (res.locals.videoChannel.Actor.isOwned() === false) {
99 return res.fail({ 100 return res.fail({
@@ -106,7 +107,18 @@ const ensureIsLocalChannel = [
106 } 107 }
107] 108]
108 109
109const videoChannelStatsValidator = [ 110export const ensureChannelOwnerCanUpload = [
111 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
112 const channel = res.locals.videoChannel
113 const user = { id: channel.Account.userId }
114
115 if (!await checkUserQuota(user, 1, res)) return
116
117 next()
118 }
119]
120
121export const videoChannelStatsValidator = [
110 query('withStats') 122 query('withStats')
111 .optional() 123 .optional()
112 .customSanitizer(toBooleanOrNull) 124 .customSanitizer(toBooleanOrNull)
@@ -118,7 +130,7 @@ const videoChannelStatsValidator = [
118 } 130 }
119] 131]
120 132
121const videoChannelsListValidator = [ 133export const videoChannelsListValidator = [
122 query('search').optional().not().isEmpty().withMessage('Should have a valid search'), 134 query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
123 135
124 (req: express.Request, res: express.Response, next: express.NextFunction) => { 136 (req: express.Request, res: express.Response, next: express.NextFunction) => {
@@ -130,17 +142,24 @@ const videoChannelsListValidator = [
130 } 142 }
131] 143]
132 144
133// --------------------------------------------------------------------------- 145export const videoChannelImportVideosValidator = [
146 body('externalChannelUrl').custom(isUrlValid).withMessage('Should have a valid channel url'),
134 147
135export { 148 (req: express.Request, res: express.Response, next: express.NextFunction) => {
136 videoChannelsAddValidator, 149 logger.debug('Checking videoChannelImport parameters', { parameters: req.body })
137 videoChannelsUpdateValidator, 150
138 videoChannelsRemoveValidator, 151 if (areValidationErrors(req, res)) return
139 videoChannelsNameWithHostValidator, 152
140 ensureIsLocalChannel, 153 if (!CONFIG.IMPORT.VIDEOS.HTTP.ENABLED) {
141 videoChannelsListValidator, 154 return res.fail({
142 videoChannelStatsValidator 155 status: HttpStatusCode.FORBIDDEN_403,
143} 156 message: 'Channel import is impossible as video upload via HTTP is not enabled on the server'
157 })
158 }
159
160 return next()
161 }
162]
144 163
145// --------------------------------------------------------------------------- 164// ---------------------------------------------------------------------------
146 165