]>
Commit | Line | Data |
---|---|---|
1 | import express from 'express' | |
2 | import { body, param, query } from 'express-validator' | |
3 | import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc' | |
4 | import { CONFIG } from '@server/initializers/config' | |
5 | import { MChannelAccountDefault } from '@server/types/models' | |
6 | import { VideosImportInChannelCreate } from '@shared/models' | |
7 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | |
8 | import { isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | |
9 | import { | |
10 | isVideoChannelDescriptionValid, | |
11 | isVideoChannelDisplayNameValid, | |
12 | isVideoChannelSupportValid, | |
13 | isVideoChannelUsernameValid | |
14 | } from '../../../helpers/custom-validators/video-channels' | |
15 | import { ActorModel } from '../../../models/actor/actor' | |
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | |
17 | import { areValidationErrors, checkUserQuota, doesVideoChannelNameWithHostExist } from '../shared' | |
18 | import { doesVideoChannelSyncIdExist } from '../shared/video-channel-syncs' | |
19 | ||
20 | export const videoChannelsAddValidator = [ | |
21 | body('name') | |
22 | .custom(isVideoChannelUsernameValid), | |
23 | body('displayName') | |
24 | .custom(isVideoChannelDisplayNameValid), | |
25 | body('description') | |
26 | .optional() | |
27 | .custom(isVideoChannelDescriptionValid), | |
28 | body('support') | |
29 | .optional() | |
30 | .custom(isVideoChannelSupportValid), | |
31 | ||
32 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
33 | if (areValidationErrors(req, res)) return | |
34 | ||
35 | const actor = await ActorModel.loadLocalByName(req.body.name) | |
36 | if (actor) { | |
37 | res.fail({ | |
38 | status: HttpStatusCode.CONFLICT_409, | |
39 | message: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' | |
40 | }) | |
41 | return false | |
42 | } | |
43 | ||
44 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) | |
45 | if (count >= CONFIG.VIDEO_CHANNELS.MAX_PER_USER) { | |
46 | res.fail({ message: `You cannot create more than ${CONFIG.VIDEO_CHANNELS.MAX_PER_USER} channels` }) | |
47 | return false | |
48 | } | |
49 | ||
50 | return next() | |
51 | } | |
52 | ] | |
53 | ||
54 | export const videoChannelsUpdateValidator = [ | |
55 | param('nameWithHost') | |
56 | .exists(), | |
57 | ||
58 | body('displayName') | |
59 | .optional() | |
60 | .custom(isVideoChannelDisplayNameValid), | |
61 | body('description') | |
62 | .optional() | |
63 | .custom(isVideoChannelDescriptionValid), | |
64 | body('support') | |
65 | .optional() | |
66 | .custom(isVideoChannelSupportValid), | |
67 | body('bulkVideosSupportUpdate') | |
68 | .optional() | |
69 | .custom(isBooleanValid).withMessage('Should have a valid bulkVideosSupportUpdate boolean field'), | |
70 | ||
71 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
72 | if (areValidationErrors(req, res)) return | |
73 | ||
74 | return next() | |
75 | } | |
76 | ] | |
77 | ||
78 | export const videoChannelsRemoveValidator = [ | |
79 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
80 | if (!await checkVideoChannelIsNotTheLastOne(res.locals.videoChannel, res)) return | |
81 | ||
82 | return next() | |
83 | } | |
84 | ] | |
85 | ||
86 | export const videoChannelsNameWithHostValidator = [ | |
87 | param('nameWithHost') | |
88 | .exists(), | |
89 | ||
90 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
91 | if (areValidationErrors(req, res)) return | |
92 | ||
93 | if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return | |
94 | ||
95 | return next() | |
96 | } | |
97 | ] | |
98 | ||
99 | export const ensureIsLocalChannel = [ | |
100 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
101 | if (res.locals.videoChannel.Actor.isOwned() === false) { | |
102 | return res.fail({ | |
103 | status: HttpStatusCode.FORBIDDEN_403, | |
104 | message: 'This channel is not owned.' | |
105 | }) | |
106 | } | |
107 | ||
108 | return next() | |
109 | } | |
110 | ] | |
111 | ||
112 | export const ensureChannelOwnerCanUpload = [ | |
113 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
114 | const channel = res.locals.videoChannel | |
115 | const user = { id: channel.Account.userId } | |
116 | ||
117 | if (!await checkUserQuota(user, 1, res)) return | |
118 | ||
119 | next() | |
120 | } | |
121 | ] | |
122 | ||
123 | export const videoChannelStatsValidator = [ | |
124 | query('withStats') | |
125 | .optional() | |
126 | .customSanitizer(toBooleanOrNull) | |
127 | .custom(isBooleanValid).withMessage('Should have a valid stats flag boolean'), | |
128 | ||
129 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
130 | if (areValidationErrors(req, res)) return | |
131 | return next() | |
132 | } | |
133 | ] | |
134 | ||
135 | export const videoChannelsListValidator = [ | |
136 | query('search') | |
137 | .optional() | |
138 | .not().isEmpty(), | |
139 | ||
140 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
141 | if (areValidationErrors(req, res)) return | |
142 | ||
143 | return next() | |
144 | } | |
145 | ] | |
146 | ||
147 | export const videoChannelImportVideosValidator = [ | |
148 | body('externalChannelUrl') | |
149 | .custom(isUrlValid), | |
150 | ||
151 | body('videoChannelSyncId') | |
152 | .optional() | |
153 | .custom(isIdValid), | |
154 | ||
155 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | |
156 | if (areValidationErrors(req, res)) return | |
157 | ||
158 | const body: VideosImportInChannelCreate = req.body | |
159 | ||
160 | if (!CONFIG.IMPORT.VIDEOS.HTTP.ENABLED) { | |
161 | return res.fail({ | |
162 | status: HttpStatusCode.FORBIDDEN_403, | |
163 | message: 'Channel import is impossible as video upload via HTTP is not enabled on the server' | |
164 | }) | |
165 | } | |
166 | ||
167 | if (body.videoChannelSyncId && !await doesVideoChannelSyncIdExist(body.videoChannelSyncId, res)) return | |
168 | ||
169 | if (res.locals.videoChannelSync && res.locals.videoChannelSync.videoChannelId !== res.locals.videoChannel.id) { | |
170 | return res.fail({ | |
171 | status: HttpStatusCode.FORBIDDEN_403, | |
172 | message: 'This channel sync is not owned by this channel' | |
173 | }) | |
174 | } | |
175 | ||
176 | return next() | |
177 | } | |
178 | ] | |
179 | ||
180 | // --------------------------------------------------------------------------- | |
181 | ||
182 | async function checkVideoChannelIsNotTheLastOne (videoChannel: MChannelAccountDefault, res: express.Response) { | |
183 | const count = await VideoChannelModel.countByAccount(videoChannel.Account.id) | |
184 | ||
185 | if (count <= 1) { | |
186 | res.fail({ | |
187 | status: HttpStatusCode.CONFLICT_409, | |
188 | message: 'Cannot remove the last channel of this user' | |
189 | }) | |
190 | return false | |
191 | } | |
192 | ||
193 | return true | |
194 | } |