Video channel API routes refractor
[oweals/peertube.git] / server / middlewares / validators / video-channels.ts
1 import * as express from 'express'
2 import { body, param } from 'express-validator/check'
3 import { UserRight } from '../../../shared'
4 import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
5 import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
6 import {
7   isVideoChannelDescriptionValid, isVideoChannelExist,
8   isVideoChannelNameValid, isVideoChannelSupportValid
9 } from '../../helpers/custom-validators/video-channels'
10 import { logger } from '../../helpers/logger'
11 import { UserModel } from '../../models/account/user'
12 import { VideoChannelModel } from '../../models/video/video-channel'
13 import { areValidationErrors } from './utils'
14 import { AccountModel } from '../../models/account/account'
15
16 const listVideoAccountChannelsValidator = [
17   param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
18
19   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
20     logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
21
22     if (areValidationErrors(req, res)) return
23     if (!await isAccountIdExist(req.params.accountId, res)) return
24
25     return next()
26   }
27 ]
28
29 const videoChannelsAddValidator = [
30   param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
31   body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
32   body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
33   body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
34
35   (req: express.Request, res: express.Response, next: express.NextFunction) => {
36     logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body })
37
38     if (areValidationErrors(req, res)) return
39
40     return next()
41   }
42 ]
43
44 const videoChannelsUpdateValidator = [
45   param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
46   param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
47   body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
48   body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
49   body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
50
51   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
52     logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
53
54     if (areValidationErrors(req, res)) return
55     if (!await isAccountIdExist(req.params.accountId, res)) return
56     if (!await isVideoChannelExist(req.params.id, res)) return
57     if (!checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
58
59     // We need to make additional checks
60     if (res.locals.videoChannel.Actor.isOwned() === false) {
61       return res.status(403)
62         .json({ error: 'Cannot update video channel of another server' })
63         .end()
64     }
65
66     if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
67       return res.status(403)
68         .json({ error: 'Cannot update video channel of another user' })
69         .end()
70     }
71
72     return next()
73   }
74 ]
75
76 const videoChannelsRemoveValidator = [
77   param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
78   param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
79
80   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
81     logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
82
83     if (areValidationErrors(req, res)) return
84     if (!await isAccountIdExist(req.params.accountId, res)) return
85     if (!await isVideoChannelExist(req.params.id, res)) return
86
87     if (!checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
88     // Check if the user who did the request is able to delete the video
89     if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
90     if (!await checkVideoChannelIsNotTheLastOne(res)) return
91
92     return next()
93   }
94 ]
95
96 const videoChannelsGetValidator = [
97   param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
98   param('accountId').optional().custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
99
100   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
101     logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
102
103     if (areValidationErrors(req, res)) return
104
105     // On some routes, accountId is optional (for example in the ActivityPub route)
106     if (req.params.accountId && !await isAccountIdExist(req.params.accountId, res)) return
107     if (!await isVideoChannelExist(req.params.id, res)) return
108
109     if (res.locals.account && !checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
110
111     return next()
112   }
113 ]
114
115 // ---------------------------------------------------------------------------
116
117 export {
118   listVideoAccountChannelsValidator,
119   videoChannelsAddValidator,
120   videoChannelsUpdateValidator,
121   videoChannelsRemoveValidator,
122   videoChannelsGetValidator
123 }
124
125 // ---------------------------------------------------------------------------
126
127 function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) {
128   if (videoChannel.Actor.isOwned() === false) {
129     res.status(403)
130               .json({ error: 'Cannot remove video channel of another server.' })
131               .end()
132
133     return false
134   }
135
136   // Check if the user can delete the video channel
137   // The user can delete it if s/he is an admin
138   // Or if s/he is the video channel's account
139   if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) {
140     res.status(403)
141               .json({ error: 'Cannot remove video channel of another user' })
142               .end()
143
144     return false
145   }
146
147   return true
148 }
149
150 async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
151   const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id)
152
153   if (count <= 1) {
154     res.status(409)
155       .json({ error: 'Cannot remove the last channel of this user' })
156       .end()
157
158     return false
159   }
160
161   return true
162 }
163
164 function checkAccountOwnsVideoChannel (account: AccountModel, videoChannel: VideoChannelModel, res: express.Response) {
165   if (videoChannel.Account.id !== account.id) {
166     res.status(400)
167               .json({ error: 'This account does not own this video channel' })
168               .end()
169
170     return false
171   }
172
173   return true
174 }