]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/feeds.ts
Use largest avatar in RSS feeds, unique guid for liveItems (#5817)
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / feeds.ts
CommitLineData
41fb13c3 1import express from 'express'
c8861d5d 2import { param, query } from 'express-validator'
1ed1994f 3import { HttpStatusCode } from '@shared/models'
244e76a5 4import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
d4a8e7a6 5import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
cb0eda56 6import { buildPodcastGroupsCache } from '../cache'
3e753302 7import {
10363c74 8 areValidationErrors,
ff9d43f6 9 checkCanSeeVideo,
3e753302
C
10 doesAccountIdExist,
11 doesAccountNameWithHostExist,
18490b07 12 doesUserFeedTokenCorrespond,
3e753302 13 doesVideoChannelIdExist,
10363c74
C
14 doesVideoChannelNameWithHostExist,
15 doesVideoExist
16} from './shared'
244e76a5 17
f2f0eda5 18const feedsFormatValidator = [
396f6f01
C
19 param('format')
20 .optional()
21 .custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'),
22 query('format')
23 .optional()
24 .custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'),
25
26 (req: express.Request, res: express.Response, next: express.NextFunction) => {
396f6f01
C
27 if (areValidationErrors(req, res)) return
28
29 return next()
30 }
f2f0eda5
RK
31]
32
33function setFeedFormatContentType (req: express.Request, res: express.Response, next: express.NextFunction) {
34 const format = req.query.format || req.params.format || 'rss'
35
36 let acceptableContentTypes: string[]
37 if (format === 'atom' || format === 'atom1') {
a1587156 38 acceptableContentTypes = [ 'application/atom+xml', 'application/xml', 'text/xml' ]
f2f0eda5 39 } else if (format === 'json' || format === 'json1') {
a1587156 40 acceptableContentTypes = [ 'application/json' ]
f2f0eda5 41 } else if (format === 'rss' || format === 'rss2') {
a1587156 42 acceptableContentTypes = [ 'application/rss+xml', 'application/xml', 'text/xml' ]
f2f0eda5 43 } else {
a1587156 44 acceptableContentTypes = [ 'application/xml', 'text/xml' ]
f2f0eda5
RK
45 }
46
cb0eda56
AG
47 return feedContentTypeResponse(req, res, next, acceptableContentTypes)
48}
49
50function setFeedPodcastContentType (req: express.Request, res: express.Response, next: express.NextFunction) {
51 const acceptableContentTypes = [ 'application/rss+xml', 'application/xml', 'text/xml' ]
52
53 return feedContentTypeResponse(req, res, next, acceptableContentTypes)
54}
55
56function feedContentTypeResponse (
57 req: express.Request,
58 res: express.Response,
59 next: express.NextFunction,
60 acceptableContentTypes: string[]
61) {
f2f0eda5
RK
62 if (req.accepts(acceptableContentTypes)) {
63 res.set('Content-Type', req.accepts(acceptableContentTypes) as string)
64 } else {
76148b27
RK
65 return res.fail({
66 status: HttpStatusCode.NOT_ACCEPTABLE_406,
67 message: `You should accept at least one of the following content-types: ${acceptableContentTypes.join(', ')}`
68 })
f2f0eda5
RK
69 }
70
71 return next()
72}
73
cb0eda56
AG
74// ---------------------------------------------------------------------------
75
f2f0eda5 76const videoFeedsValidator = [
18490b07
C
77 query('accountId')
78 .optional()
396f6f01 79 .custom(isIdValid),
18490b07
C
80
81 query('accountName')
82 .optional(),
83
84 query('videoChannelId')
85 .optional()
396f6f01 86 .custom(isIdValid),
18490b07
C
87
88 query('videoChannelName')
89 .optional(),
244e76a5
RK
90
91 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
244e76a5
RK
92 if (areValidationErrors(req, res)) return
93
0f6acda1
C
94 if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return
95 if (req.query.videoChannelId && !await doesVideoChannelIdExist(req.query.videoChannelId, res)) return
96 if (req.query.accountName && !await doesAccountNameWithHostExist(req.query.accountName, res)) return
97 if (req.query.videoChannelName && !await doesVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return
244e76a5
RK
98
99 return next()
100 }
101]
102
cb0eda56
AG
103// ---------------------------------------------------------------------------
104
105const videoFeedsPodcastValidator = [
106 query('videoChannelId')
107 .custom(isIdValid),
108
109 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
110 if (areValidationErrors(req, res)) return
111 if (!await doesVideoChannelIdExist(req.query.videoChannelId, res)) return
112
113 return next()
114 }
115]
116
117const videoFeedsPodcastSetCacheKey = [
118 (req: express.Request, res: express.Response, next: express.NextFunction) => {
119 if (req.query.videoChannelId) {
120 res.locals.apicacheGroups = [ buildPodcastGroupsCache({ channelId: req.query.videoChannelId }) ]
121 }
122
123 return next()
124 }
125]
126// ---------------------------------------------------------------------------
127
18490b07
C
128const videoSubscriptionFeedsValidator = [
129 query('accountId')
396f6f01 130 .custom(isIdValid),
18490b07
C
131
132 query('token')
396f6f01 133 .custom(exists),
afff310e
RK
134
135 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
afff310e
RK
136 if (areValidationErrors(req, res)) return
137
18490b07
C
138 if (!await doesAccountIdExist(req.query.accountId, res)) return
139 if (!await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return
afff310e
RK
140
141 return next()
142 }
143]
144
fe3a55b0 145const videoCommentsFeedsValidator = [
d4a8e7a6 146 query('videoId')
d4a8e7a6 147 .optional()
396f6f01 148 .customSanitizer(toCompleteUUID)
d4a8e7a6 149 .custom(isIdOrUUIDValid),
fe3a55b0
C
150
151 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
fe3a55b0
C
152 if (areValidationErrors(req, res)) return
153
00494d6e 154 if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) {
76148b27 155 return res.fail({ message: 'videoId cannot be mixed with a channel filter' })
00494d6e
RK
156 }
157
ff9d43f6
C
158 if (req.query.videoId) {
159 if (!await doesVideoExist(req.query.videoId, res)) return
160 if (!await checkCanSeeVideo({ req, res, paramId: req.query.videoId, video: res.locals.videoAll })) return
161 }
fe3a55b0
C
162
163 return next()
164 }
165]
166
244e76a5
RK
167// ---------------------------------------------------------------------------
168
169export {
f2f0eda5
RK
170 feedsFormatValidator,
171 setFeedFormatContentType,
cb0eda56 172 setFeedPodcastContentType,
fe3a55b0 173 videoFeedsValidator,
cb0eda56 174 videoFeedsPodcastValidator,
18490b07 175 videoSubscriptionFeedsValidator,
cb0eda56 176 videoFeedsPodcastSetCacheKey,
fe3a55b0 177 videoCommentsFeedsValidator
244e76a5 178}