diff options
Diffstat (limited to 'server/controllers')
-rw-r--r-- | server/controllers/api/config.ts | 12 | ||||
-rw-r--r-- | server/controllers/api/jobs.ts | 4 | ||||
-rw-r--r-- | server/controllers/api/plugins.ts | 1 | ||||
-rw-r--r-- | server/controllers/api/search.ts | 43 | ||||
-rw-r--r-- | server/controllers/api/users/index.ts | 8 | ||||
-rw-r--r-- | server/controllers/api/users/me.ts | 10 | ||||
-rw-r--r-- | server/controllers/api/users/my-notifications.ts | 4 | ||||
-rw-r--r-- | server/controllers/api/users/my-subscriptions.ts | 12 | ||||
-rw-r--r-- | server/controllers/api/users/token.ts | 72 | ||||
-rw-r--r-- | server/controllers/api/video-channel.ts | 66 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 11 | ||||
-rw-r--r-- | server/controllers/api/videos/ownership.ts | 2 | ||||
-rw-r--r-- | server/controllers/client.ts | 28 | ||||
-rw-r--r-- | server/controllers/download.ts | 85 | ||||
-rw-r--r-- | server/controllers/feeds.ts | 10 | ||||
-rw-r--r-- | server/controllers/lazy-static.ts | 41 | ||||
-rw-r--r-- | server/controllers/plugins.ts | 14 | ||||
-rw-r--r-- | server/controllers/services.ts | 4 | ||||
-rw-r--r-- | server/controllers/static.ts | 4 |
19 files changed, 322 insertions, 109 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index fb108ca1c..e28f7502d 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -158,9 +158,17 @@ async function getConfig (req: express.Request, res: express.Response) { | |||
158 | avatar: { | 158 | avatar: { |
159 | file: { | 159 | file: { |
160 | size: { | 160 | size: { |
161 | max: CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max | 161 | max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max |
162 | }, | 162 | }, |
163 | extensions: CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME | 163 | extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME |
164 | } | ||
165 | }, | ||
166 | banner: { | ||
167 | file: { | ||
168 | size: { | ||
169 | max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max | ||
170 | }, | ||
171 | extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME | ||
164 | } | 172 | } |
165 | }, | 173 | }, |
166 | video: { | 174 | video: { |
diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts index 861cc22b9..d7cee1605 100644 --- a/server/controllers/api/jobs.ts +++ b/server/controllers/api/jobs.ts | |||
@@ -9,10 +9,10 @@ import { | |||
9 | authenticate, | 9 | authenticate, |
10 | ensureUserHasRight, | 10 | ensureUserHasRight, |
11 | jobsSortValidator, | 11 | jobsSortValidator, |
12 | paginationValidatorBuilder, | ||
12 | setDefaultPagination, | 13 | setDefaultPagination, |
13 | setDefaultSort | 14 | setDefaultSort |
14 | } from '../../middlewares' | 15 | } from '../../middlewares' |
15 | import { paginationValidator } from '../../middlewares/validators' | ||
16 | import { listJobsValidator } from '../../middlewares/validators/jobs' | 16 | import { listJobsValidator } from '../../middlewares/validators/jobs' |
17 | 17 | ||
18 | const jobsRouter = express.Router() | 18 | const jobsRouter = express.Router() |
@@ -20,7 +20,7 @@ const jobsRouter = express.Router() | |||
20 | jobsRouter.get('/:state?', | 20 | jobsRouter.get('/:state?', |
21 | authenticate, | 21 | authenticate, |
22 | ensureUserHasRight(UserRight.MANAGE_JOBS), | 22 | ensureUserHasRight(UserRight.MANAGE_JOBS), |
23 | paginationValidator, | 23 | paginationValidatorBuilder([ 'jobs' ]), |
24 | jobsSortValidator, | 24 | jobsSortValidator, |
25 | setDefaultSort, | 25 | setDefaultSort, |
26 | setDefaultPagination, | 26 | setDefaultPagination, |
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index 1c0b5edb1..bb69f25a1 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -205,7 +205,6 @@ async function listAvailablePlugins (req: express.Request, res: express.Response | |||
205 | if (!resultList) { | 205 | if (!resultList) { |
206 | return res.status(HttpStatusCode.SERVICE_UNAVAILABLE_503) | 206 | return res.status(HttpStatusCode.SERVICE_UNAVAILABLE_503) |
207 | .json({ error: 'Plugin index unavailable. Please retry later' }) | 207 | .json({ error: 'Plugin index unavailable. Please retry later' }) |
208 | .end() | ||
209 | } | 208 | } |
210 | 209 | ||
211 | return res.json(resultList) | 210 | return res.json(resultList) |
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 7e1b7b230..f0cdf3a89 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { sanitizeUrl } from '@server/helpers/core-utils' | 2 | import { sanitizeUrl } from '@server/helpers/core-utils' |
3 | import { doRequest } from '@server/helpers/requests' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
4 | import { CONFIG } from '@server/initializers/config' | 4 | import { CONFIG } from '@server/initializers/config' |
5 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' | 5 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' |
6 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' | 7 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' |
7 | import { getServerActor } from '@server/models/application/application' | 8 | import { getServerActor } from '@server/models/application/application' |
8 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | 9 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' |
@@ -22,8 +23,8 @@ import { | |||
22 | paginationValidator, | 23 | paginationValidator, |
23 | setDefaultPagination, | 24 | setDefaultPagination, |
24 | setDefaultSearchSort, | 25 | setDefaultSearchSort, |
25 | videoChannelsSearchSortValidator, | ||
26 | videoChannelsListSearchValidator, | 26 | videoChannelsListSearchValidator, |
27 | videoChannelsSearchSortValidator, | ||
27 | videosSearchSortValidator, | 28 | videosSearchSortValidator, |
28 | videosSearchValidator | 29 | videosSearchValidator |
29 | } from '../../middlewares' | 30 | } from '../../middlewares' |
@@ -87,16 +88,17 @@ function searchVideoChannels (req: express.Request, res: express.Response) { | |||
87 | async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { | 88 | async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { |
88 | const result = await buildMutedForSearchIndex(res) | 89 | const result = await buildMutedForSearchIndex(res) |
89 | 90 | ||
90 | const body = Object.assign(query, result) | 91 | const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params') |
91 | 92 | ||
92 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/video-channels' | 93 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/video-channels' |
93 | 94 | ||
94 | try { | 95 | try { |
95 | logger.debug('Doing video channels search index request on %s.', url, { body }) | 96 | logger.debug('Doing video channels search index request on %s.', url, { body }) |
96 | 97 | ||
97 | const searchIndexResult = await doRequest<ResultList<VideoChannel>>({ uri: url, body, json: true }) | 98 | const { body: searchIndexResult } = await doJSONRequest<ResultList<VideoChannel>>(url, { method: 'POST', json: body }) |
99 | const jsonResult = await Hooks.wrapObject(searchIndexResult, 'filter:api.search.video-channels.index.list.result') | ||
98 | 100 | ||
99 | return res.json(searchIndexResult.body) | 101 | return res.json(jsonResult) |
100 | } catch (err) { | 102 | } catch (err) { |
101 | logger.warn('Cannot use search index to make video channels search.', { err }) | 103 | logger.warn('Cannot use search index to make video channels search.', { err }) |
102 | 104 | ||
@@ -107,14 +109,19 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e | |||
107 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { | 109 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { |
108 | const serverActor = await getServerActor() | 110 | const serverActor = await getServerActor() |
109 | 111 | ||
110 | const options = { | 112 | const apiOptions = await Hooks.wrapObject({ |
111 | actorId: serverActor.id, | 113 | actorId: serverActor.id, |
112 | search: query.search, | 114 | search: query.search, |
113 | start: query.start, | 115 | start: query.start, |
114 | count: query.count, | 116 | count: query.count, |
115 | sort: query.sort | 117 | sort: query.sort |
116 | } | 118 | }, 'filter:api.search.video-channels.local.list.params') |
117 | const resultList = await VideoChannelModel.searchForApi(options) | 119 | |
120 | const resultList = await Hooks.wrapPromiseFun( | ||
121 | VideoChannelModel.searchForApi, | ||
122 | apiOptions, | ||
123 | 'filter:api.search.video-channels.local.list.result' | ||
124 | ) | ||
118 | 125 | ||
119 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 126 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
120 | } | 127 | } |
@@ -168,7 +175,7 @@ function searchVideos (req: express.Request, res: express.Response) { | |||
168 | async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { | 175 | async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { |
169 | const result = await buildMutedForSearchIndex(res) | 176 | const result = await buildMutedForSearchIndex(res) |
170 | 177 | ||
171 | const body: VideosSearchQuery = Object.assign(query, result) | 178 | let body: VideosSearchQuery = Object.assign(query, result) |
172 | 179 | ||
173 | // Use the default instance NSFW policy if not specified | 180 | // Use the default instance NSFW policy if not specified |
174 | if (!body.nsfw) { | 181 | if (!body.nsfw) { |
@@ -181,14 +188,17 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons | |||
181 | : 'both' | 188 | : 'both' |
182 | } | 189 | } |
183 | 190 | ||
191 | body = await Hooks.wrapObject(body, 'filter:api.search.videos.index.list.params') | ||
192 | |||
184 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/videos' | 193 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/videos' |
185 | 194 | ||
186 | try { | 195 | try { |
187 | logger.debug('Doing videos search index request on %s.', url, { body }) | 196 | logger.debug('Doing videos search index request on %s.', url, { body }) |
188 | 197 | ||
189 | const searchIndexResult = await doRequest<ResultList<Video>>({ uri: url, body, json: true }) | 198 | const { body: searchIndexResult } = await doJSONRequest<ResultList<Video>>(url, { method: 'POST', json: body }) |
199 | const jsonResult = await Hooks.wrapObject(searchIndexResult, 'filter:api.search.videos.index.list.result') | ||
190 | 200 | ||
191 | return res.json(searchIndexResult.body) | 201 | return res.json(jsonResult) |
192 | } catch (err) { | 202 | } catch (err) { |
193 | logger.warn('Cannot use search index to make video search.', { err }) | 203 | logger.warn('Cannot use search index to make video search.', { err }) |
194 | 204 | ||
@@ -197,13 +207,18 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons | |||
197 | } | 207 | } |
198 | 208 | ||
199 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { | 209 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { |
200 | const options = Object.assign(query, { | 210 | const apiOptions = await Hooks.wrapObject(Object.assign(query, { |
201 | includeLocalVideos: true, | 211 | includeLocalVideos: true, |
202 | nsfw: buildNSFWFilter(res, query.nsfw), | 212 | nsfw: buildNSFWFilter(res, query.nsfw), |
203 | filter: query.filter, | 213 | filter: query.filter, |
204 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined | 214 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined |
205 | }) | 215 | }), 'filter:api.search.videos.local.list.params') |
206 | const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) | 216 | |
217 | const resultList = await Hooks.wrapPromiseFun( | ||
218 | VideoModel.searchAndPopulateAccountAndServer, | ||
219 | apiOptions, | ||
220 | 'filter:api.search.videos.local.list.result' | ||
221 | ) | ||
207 | 222 | ||
208 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 223 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
209 | } | 224 | } |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 3be1d55ae..e2b1ea7cd 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -2,8 +2,10 @@ import * as express from 'express' | |||
2 | import * as RateLimit from 'express-rate-limit' | 2 | import * as RateLimit from 'express-rate-limit' |
3 | import { tokensRouter } from '@server/controllers/api/users/token' | 3 | import { tokensRouter } from '@server/controllers/api/users/token' |
4 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
5 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' | ||
5 | import { MUser, MUserAccountDefault } from '@server/types/models' | 6 | import { MUser, MUserAccountDefault } from '@server/types/models' |
6 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' | 7 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' |
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
7 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | 9 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' |
8 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | 10 | import { UserRegister } from '../../../../shared/models/users/user-register.model' |
9 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | 11 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' |
@@ -14,7 +16,6 @@ import { WEBSERVER } from '../../../initializers/constants' | |||
14 | import { sequelizeTypescript } from '../../../initializers/database' | 16 | import { sequelizeTypescript } from '../../../initializers/database' |
15 | import { Emailer } from '../../../lib/emailer' | 17 | import { Emailer } from '../../../lib/emailer' |
16 | import { Notifier } from '../../../lib/notifier' | 18 | import { Notifier } from '../../../lib/notifier' |
17 | import { deleteUserToken } from '../../../lib/oauth-model' | ||
18 | import { Redis } from '../../../lib/redis' | 19 | import { Redis } from '../../../lib/redis' |
19 | import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user' | 20 | import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user' |
20 | import { | 21 | import { |
@@ -52,7 +53,6 @@ import { myVideosHistoryRouter } from './my-history' | |||
52 | import { myNotificationsRouter } from './my-notifications' | 53 | import { myNotificationsRouter } from './my-notifications' |
53 | import { mySubscriptionsRouter } from './my-subscriptions' | 54 | import { mySubscriptionsRouter } from './my-subscriptions' |
54 | import { myVideoPlaylistsRouter } from './my-video-playlists' | 55 | import { myVideoPlaylistsRouter } from './my-video-playlists' |
55 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
56 | 56 | ||
57 | const auditLogger = auditLoggerFactory('users') | 57 | const auditLogger = auditLoggerFactory('users') |
58 | 58 | ||
@@ -335,7 +335,7 @@ async function updateUser (req: express.Request, res: express.Response) { | |||
335 | const user = await userToUpdate.save() | 335 | const user = await userToUpdate.save() |
336 | 336 | ||
337 | // Destroy user token to refresh rights | 337 | // Destroy user token to refresh rights |
338 | if (roleChanged || body.password !== undefined) await deleteUserToken(userToUpdate.id) | 338 | if (roleChanged || body.password !== undefined) await OAuthTokenModel.deleteUserToken(userToUpdate.id) |
339 | 339 | ||
340 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) | 340 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) |
341 | 341 | ||
@@ -395,7 +395,7 @@ async function changeUserBlock (res: express.Response, user: MUserAccountDefault | |||
395 | user.blockedReason = reason || null | 395 | user.blockedReason = reason || null |
396 | 396 | ||
397 | await sequelizeTypescript.transaction(async t => { | 397 | await sequelizeTypescript.transaction(async t => { |
398 | await deleteUserToken(user.id, t) | 398 | await OAuthTokenModel.deleteUserToken(user.id, t) |
399 | 399 | ||
400 | await user.save({ transaction: t }) | 400 | await user.save({ transaction: t }) |
401 | }) | 401 | }) |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 5a3e9e51a..9f9d2d77f 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -2,7 +2,7 @@ import 'multer' | |||
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' | 3 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' |
4 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
5 | import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' | 5 | import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' |
6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
7 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' | 7 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' |
8 | import { createReqFiles } from '../../../helpers/express-utils' | 8 | import { createReqFiles } from '../../../helpers/express-utils' |
@@ -11,7 +11,7 @@ import { CONFIG } from '../../../initializers/config' | |||
11 | import { MIMETYPES } from '../../../initializers/constants' | 11 | import { MIMETYPES } from '../../../initializers/constants' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
13 | import { sendUpdateActor } from '../../../lib/activitypub/send' | 13 | import { sendUpdateActor } from '../../../lib/activitypub/send' |
14 | import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../../lib/avatar' | 14 | import { deleteLocalActorImageFile, updateLocalActorImageFile } from '../../../lib/actor-image' |
15 | import { getOriginalVideoFileTotalDailyFromUser, getOriginalVideoFileTotalFromUser, sendVerifyUserEmail } from '../../../lib/user' | 15 | import { getOriginalVideoFileTotalDailyFromUser, getOriginalVideoFileTotalFromUser, sendVerifyUserEmail } from '../../../lib/user' |
16 | import { | 16 | import { |
17 | asyncMiddleware, | 17 | asyncMiddleware, |
@@ -25,7 +25,7 @@ import { | |||
25 | usersVideoRatingValidator | 25 | usersVideoRatingValidator |
26 | } from '../../../middlewares' | 26 | } from '../../../middlewares' |
27 | import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' | 27 | import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' |
28 | import { updateAvatarValidator } from '../../../middlewares/validators/avatar' | 28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' |
29 | import { AccountModel } from '../../../models/account/account' | 29 | import { AccountModel } from '../../../models/account/account' |
30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
31 | import { UserModel } from '../../../models/account/user' | 31 | import { UserModel } from '../../../models/account/user' |
@@ -238,7 +238,7 @@ async function updateMyAvatar (req: express.Request, res: express.Response) { | |||
238 | 238 | ||
239 | const userAccount = await AccountModel.load(user.Account.id) | 239 | const userAccount = await AccountModel.load(user.Account.id) |
240 | 240 | ||
241 | const avatar = await updateLocalActorAvatarFile(userAccount, avatarPhysicalFile) | 241 | const avatar = await updateLocalActorImageFile(userAccount, avatarPhysicalFile, ActorImageType.AVATAR) |
242 | 242 | ||
243 | return res.json({ avatar: avatar.toFormattedJSON() }) | 243 | return res.json({ avatar: avatar.toFormattedJSON() }) |
244 | } | 244 | } |
@@ -247,7 +247,7 @@ async function deleteMyAvatar (req: express.Request, res: express.Response) { | |||
247 | const user = res.locals.oauth.token.user | 247 | const user = res.locals.oauth.token.user |
248 | 248 | ||
249 | const userAccount = await AccountModel.load(user.Account.id) | 249 | const userAccount = await AccountModel.load(user.Account.id) |
250 | await deleteLocalActorAvatarFile(userAccount) | 250 | await deleteLocalActorImageFile(userAccount, ActorImageType.AVATAR) |
251 | 251 | ||
252 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 252 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) |
253 | } | 253 | } |
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index 5f5e4c5e6..0a9101a46 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -80,7 +80,9 @@ async function updateNotificationSettings (req: express.Request, res: express.Re | |||
80 | newInstanceFollower: body.newInstanceFollower, | 80 | newInstanceFollower: body.newInstanceFollower, |
81 | autoInstanceFollowing: body.autoInstanceFollowing, | 81 | autoInstanceFollowing: body.autoInstanceFollowing, |
82 | abuseNewMessage: body.abuseNewMessage, | 82 | abuseNewMessage: body.abuseNewMessage, |
83 | abuseStateChange: body.abuseStateChange | 83 | abuseStateChange: body.abuseStateChange, |
84 | newPeerTubeVersion: body.newPeerTubeVersion, | ||
85 | newPluginVersion: body.newPluginVersion | ||
84 | } | 86 | } |
85 | 87 | ||
86 | await UserNotificationSettingModel.update(values, query) | 88 | await UserNotificationSettingModel.update(values, query) |
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index ec77ddd7a..e8949ee59 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -1,5 +1,8 @@ | |||
1 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { sendUndoFollow } from '@server/lib/activitypub/send' | ||
4 | import { VideoChannelModel } from '@server/models/video/video-channel' | ||
5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | 6 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' |
4 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | 7 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' |
5 | import { getFormattedObjects } from '../../../helpers/utils' | 8 | import { getFormattedObjects } from '../../../helpers/utils' |
@@ -26,8 +29,6 @@ import { | |||
26 | } from '../../../middlewares/validators' | 29 | } from '../../../middlewares/validators' |
27 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 30 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
28 | import { VideoModel } from '../../../models/video/video' | 31 | import { VideoModel } from '../../../models/video/video' |
29 | import { sendUndoFollow } from '@server/lib/activitypub/send' | ||
30 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
31 | 32 | ||
32 | const mySubscriptionsRouter = express.Router() | 33 | const mySubscriptionsRouter = express.Router() |
33 | 34 | ||
@@ -66,7 +67,7 @@ mySubscriptionsRouter.post('/me/subscriptions', | |||
66 | mySubscriptionsRouter.get('/me/subscriptions/:uri', | 67 | mySubscriptionsRouter.get('/me/subscriptions/:uri', |
67 | authenticate, | 68 | authenticate, |
68 | userSubscriptionGetValidator, | 69 | userSubscriptionGetValidator, |
69 | getUserSubscription | 70 | asyncMiddleware(getUserSubscription) |
70 | ) | 71 | ) |
71 | 72 | ||
72 | mySubscriptionsRouter.delete('/me/subscriptions/:uri', | 73 | mySubscriptionsRouter.delete('/me/subscriptions/:uri', |
@@ -130,10 +131,11 @@ function addUserSubscription (req: express.Request, res: express.Response) { | |||
130 | return res.status(HttpStatusCode.NO_CONTENT_204).end() | 131 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
131 | } | 132 | } |
132 | 133 | ||
133 | function getUserSubscription (req: express.Request, res: express.Response) { | 134 | async function getUserSubscription (req: express.Request, res: express.Response) { |
134 | const subscription = res.locals.subscription | 135 | const subscription = res.locals.subscription |
136 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(subscription.ActorFollowing.VideoChannel.id) | ||
135 | 137 | ||
136 | return res.json(subscription.ActorFollowing.VideoChannel.toFormattedJSON()) | 138 | return res.json(videoChannel.toFormattedJSON()) |
137 | } | 139 | } |
138 | 140 | ||
139 | async function deleteUserSubscription (req: express.Request, res: express.Response) { | 141 | async function deleteUserSubscription (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/users/token.ts b/server/controllers/api/users/token.ts index 821429358..694bb0a92 100644 --- a/server/controllers/api/users/token.ts +++ b/server/controllers/api/users/token.ts | |||
@@ -1,11 +1,14 @@ | |||
1 | import { handleLogin, handleTokenRevocation } from '@server/lib/auth' | 1 | import * as express from 'express' |
2 | import * as RateLimit from 'express-rate-limit' | 2 | import * as RateLimit from 'express-rate-limit' |
3 | import { v4 as uuidv4 } from 'uuid' | ||
4 | import { logger } from '@server/helpers/logger' | ||
3 | import { CONFIG } from '@server/initializers/config' | 5 | import { CONFIG } from '@server/initializers/config' |
4 | import * as express from 'express' | 6 | import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth' |
7 | import { handleOAuthToken } from '@server/lib/auth/oauth' | ||
8 | import { BypassLogin, revokeToken } from '@server/lib/auth/oauth-model' | ||
5 | import { Hooks } from '@server/lib/plugins/hooks' | 9 | import { Hooks } from '@server/lib/plugins/hooks' |
6 | import { asyncMiddleware, authenticate } from '@server/middlewares' | 10 | import { asyncMiddleware, authenticate } from '@server/middlewares' |
7 | import { ScopedToken } from '@shared/models/users/user-scoped-token' | 11 | import { ScopedToken } from '@shared/models/users/user-scoped-token' |
8 | import { v4 as uuidv4 } from 'uuid' | ||
9 | 12 | ||
10 | const tokensRouter = express.Router() | 13 | const tokensRouter = express.Router() |
11 | 14 | ||
@@ -16,8 +19,7 @@ const loginRateLimiter = RateLimit({ | |||
16 | 19 | ||
17 | tokensRouter.post('/token', | 20 | tokensRouter.post('/token', |
18 | loginRateLimiter, | 21 | loginRateLimiter, |
19 | handleLogin, | 22 | asyncMiddleware(handleToken) |
20 | tokenSuccess | ||
21 | ) | 23 | ) |
22 | 24 | ||
23 | tokensRouter.post('/revoke-token', | 25 | tokensRouter.post('/revoke-token', |
@@ -42,10 +44,53 @@ export { | |||
42 | } | 44 | } |
43 | // --------------------------------------------------------------------------- | 45 | // --------------------------------------------------------------------------- |
44 | 46 | ||
45 | function tokenSuccess (req: express.Request) { | 47 | async function handleToken (req: express.Request, res: express.Response, next: express.NextFunction) { |
46 | const username = req.body.username | 48 | const grantType = req.body.grant_type |
49 | |||
50 | try { | ||
51 | const bypassLogin = await buildByPassLogin(req, grantType) | ||
52 | |||
53 | const refreshTokenAuthName = grantType === 'refresh_token' | ||
54 | ? await getAuthNameFromRefreshGrant(req.body.refresh_token) | ||
55 | : undefined | ||
56 | |||
57 | const options = { | ||
58 | refreshTokenAuthName, | ||
59 | bypassLogin | ||
60 | } | ||
61 | |||
62 | const token = await handleOAuthToken(req, options) | ||
63 | |||
64 | res.set('Cache-Control', 'no-store') | ||
65 | res.set('Pragma', 'no-cache') | ||
66 | |||
67 | Hooks.runAction('action:api.user.oauth2-got-token', { username: token.user.username, ip: req.ip }) | ||
68 | |||
69 | return res.json({ | ||
70 | token_type: 'Bearer', | ||
47 | 71 | ||
48 | Hooks.runAction('action:api.user.oauth2-got-token', { username, ip: req.ip }) | 72 | access_token: token.accessToken, |
73 | refresh_token: token.refreshToken, | ||
74 | |||
75 | expires_in: token.accessTokenExpiresIn, | ||
76 | refresh_token_expires_in: token.refreshTokenExpiresIn | ||
77 | }) | ||
78 | } catch (err) { | ||
79 | logger.warn('Login error', { err }) | ||
80 | |||
81 | return res.status(err.code || 400).json({ | ||
82 | code: err.name, | ||
83 | error: err.message | ||
84 | }) | ||
85 | } | ||
86 | } | ||
87 | |||
88 | async function handleTokenRevocation (req: express.Request, res: express.Response) { | ||
89 | const token = res.locals.oauth.token | ||
90 | |||
91 | const result = await revokeToken(token, { req, explicitLogout: true }) | ||
92 | |||
93 | return res.json(result) | ||
49 | } | 94 | } |
50 | 95 | ||
51 | function getScopedTokens (req: express.Request, res: express.Response) { | 96 | function getScopedTokens (req: express.Request, res: express.Response) { |
@@ -66,3 +111,14 @@ async function renewScopedTokens (req: express.Request, res: express.Response) { | |||
66 | feedToken: user.feedToken | 111 | feedToken: user.feedToken |
67 | } as ScopedToken) | 112 | } as ScopedToken) |
68 | } | 113 | } |
114 | |||
115 | async function buildByPassLogin (req: express.Request, grantType: string): Promise<BypassLogin> { | ||
116 | if (grantType !== 'password') return undefined | ||
117 | |||
118 | if (req.body.externalAuthToken) { | ||
119 | // Consistency with the getBypassFromPasswordGrant promise | ||
120 | return getBypassFromExternalAuth(req.body.username, req.body.externalAuthToken) | ||
121 | } | ||
122 | |||
123 | return getBypassFromPasswordGrant(req.body.username, req.body.password) | ||
124 | } | ||
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 03617dc8d..149d6cfb4 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { Hooks } from '@server/lib/plugins/hooks' | 2 | import { Hooks } from '@server/lib/plugins/hooks' |
3 | import { getServerActor } from '@server/models/application/application' | 3 | import { getServerActor } from '@server/models/application/application' |
4 | import { MChannelAccountDefault } from '@server/types/models' | 4 | import { MChannelBannerAccountDefault } from '@server/types/models' |
5 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | 5 | import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared' |
6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
7 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' | 7 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' |
8 | import { resetSequelizeInstance } from '../../helpers/database-utils' | 8 | import { resetSequelizeInstance } from '../../helpers/database-utils' |
@@ -13,7 +13,7 @@ import { CONFIG } from '../../initializers/config' | |||
13 | import { MIMETYPES } from '../../initializers/constants' | 13 | import { MIMETYPES } from '../../initializers/constants' |
14 | import { sequelizeTypescript } from '../../initializers/database' | 14 | import { sequelizeTypescript } from '../../initializers/database' |
15 | import { sendUpdateActor } from '../../lib/activitypub/send' | 15 | import { sendUpdateActor } from '../../lib/activitypub/send' |
16 | import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../lib/avatar' | 16 | import { deleteLocalActorImageFile, updateLocalActorImageFile } from '../../lib/actor-image' |
17 | import { JobQueue } from '../../lib/job-queue' | 17 | import { JobQueue } from '../../lib/job-queue' |
18 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' | 18 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' |
19 | import { | 19 | import { |
@@ -33,7 +33,7 @@ import { | |||
33 | videoPlaylistsSortValidator | 33 | videoPlaylistsSortValidator |
34 | } from '../../middlewares' | 34 | } from '../../middlewares' |
35 | import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators' | 35 | import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators' |
36 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' | 36 | import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/actor-image' |
37 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' | 37 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' |
38 | import { AccountModel } from '../../models/account/account' | 38 | import { AccountModel } from '../../models/account/account' |
39 | import { VideoModel } from '../../models/video/video' | 39 | import { VideoModel } from '../../models/video/video' |
@@ -42,6 +42,7 @@ import { VideoPlaylistModel } from '../../models/video/video-playlist' | |||
42 | 42 | ||
43 | const auditLogger = auditLoggerFactory('channels') | 43 | const auditLogger = auditLoggerFactory('channels') |
44 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 44 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
45 | const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { bannerfile: CONFIG.STORAGE.TMP_DIR }) | ||
45 | 46 | ||
46 | const videoChannelRouter = express.Router() | 47 | const videoChannelRouter = express.Router() |
47 | 48 | ||
@@ -69,6 +70,15 @@ videoChannelRouter.post('/:nameWithHost/avatar/pick', | |||
69 | asyncMiddleware(updateVideoChannelAvatar) | 70 | asyncMiddleware(updateVideoChannelAvatar) |
70 | ) | 71 | ) |
71 | 72 | ||
73 | videoChannelRouter.post('/:nameWithHost/banner/pick', | ||
74 | authenticate, | ||
75 | reqBannerFile, | ||
76 | // Check the rights | ||
77 | asyncMiddleware(videoChannelsUpdateValidator), | ||
78 | updateBannerValidator, | ||
79 | asyncMiddleware(updateVideoChannelBanner) | ||
80 | ) | ||
81 | |||
72 | videoChannelRouter.delete('/:nameWithHost/avatar', | 82 | videoChannelRouter.delete('/:nameWithHost/avatar', |
73 | authenticate, | 83 | authenticate, |
74 | // Check the rights | 84 | // Check the rights |
@@ -76,6 +86,13 @@ videoChannelRouter.delete('/:nameWithHost/avatar', | |||
76 | asyncMiddleware(deleteVideoChannelAvatar) | 86 | asyncMiddleware(deleteVideoChannelAvatar) |
77 | ) | 87 | ) |
78 | 88 | ||
89 | videoChannelRouter.delete('/:nameWithHost/banner', | ||
90 | authenticate, | ||
91 | // Check the rights | ||
92 | asyncMiddleware(videoChannelsUpdateValidator), | ||
93 | asyncMiddleware(deleteVideoChannelBanner) | ||
94 | ) | ||
95 | |||
79 | videoChannelRouter.put('/:nameWithHost', | 96 | videoChannelRouter.put('/:nameWithHost', |
80 | authenticate, | 97 | authenticate, |
81 | asyncMiddleware(videoChannelsUpdateValidator), | 98 | asyncMiddleware(videoChannelsUpdateValidator), |
@@ -134,26 +151,41 @@ async function listVideoChannels (req: express.Request, res: express.Response) { | |||
134 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 151 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
135 | } | 152 | } |
136 | 153 | ||
154 | async function updateVideoChannelBanner (req: express.Request, res: express.Response) { | ||
155 | const bannerPhysicalFile = req.files['bannerfile'][0] | ||
156 | const videoChannel = res.locals.videoChannel | ||
157 | const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) | ||
158 | |||
159 | const banner = await updateLocalActorImageFile(videoChannel, bannerPhysicalFile, ActorImageType.BANNER) | ||
160 | |||
161 | auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys) | ||
162 | |||
163 | return res.json({ banner: banner.toFormattedJSON() }) | ||
164 | } | ||
137 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { | 165 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { |
138 | const avatarPhysicalFile = req.files['avatarfile'][0] | 166 | const avatarPhysicalFile = req.files['avatarfile'][0] |
139 | const videoChannel = res.locals.videoChannel | 167 | const videoChannel = res.locals.videoChannel |
140 | const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) | 168 | const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) |
141 | 169 | ||
142 | const avatar = await updateLocalActorAvatarFile(videoChannel, avatarPhysicalFile) | 170 | const avatar = await updateLocalActorImageFile(videoChannel, avatarPhysicalFile, ActorImageType.AVATAR) |
143 | 171 | ||
144 | auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys) | 172 | auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys) |
145 | 173 | ||
146 | return res | 174 | return res.json({ avatar: avatar.toFormattedJSON() }) |
147 | .json({ | ||
148 | avatar: avatar.toFormattedJSON() | ||
149 | }) | ||
150 | .end() | ||
151 | } | 175 | } |
152 | 176 | ||
153 | async function deleteVideoChannelAvatar (req: express.Request, res: express.Response) { | 177 | async function deleteVideoChannelAvatar (req: express.Request, res: express.Response) { |
154 | const videoChannel = res.locals.videoChannel | 178 | const videoChannel = res.locals.videoChannel |
155 | 179 | ||
156 | await deleteLocalActorAvatarFile(videoChannel) | 180 | await deleteLocalActorImageFile(videoChannel, ActorImageType.AVATAR) |
181 | |||
182 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
183 | } | ||
184 | |||
185 | async function deleteVideoChannelBanner (req: express.Request, res: express.Response) { | ||
186 | const videoChannel = res.locals.videoChannel | ||
187 | |||
188 | await deleteLocalActorImageFile(videoChannel, ActorImageType.BANNER) | ||
157 | 189 | ||
158 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 190 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) |
159 | } | 191 | } |
@@ -177,7 +209,7 @@ async function addVideoChannel (req: express.Request, res: express.Response) { | |||
177 | videoChannel: { | 209 | videoChannel: { |
178 | id: videoChannelCreated.id | 210 | id: videoChannelCreated.id |
179 | } | 211 | } |
180 | }).end() | 212 | }) |
181 | } | 213 | } |
182 | 214 | ||
183 | async function updateVideoChannel (req: express.Request, res: express.Response) { | 215 | async function updateVideoChannel (req: express.Request, res: express.Response) { |
@@ -206,7 +238,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
206 | } | 238 | } |
207 | } | 239 | } |
208 | 240 | ||
209 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelAccountDefault | 241 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelBannerAccountDefault |
210 | await sendUpdateActor(videoChannelInstanceUpdated, t) | 242 | await sendUpdateActor(videoChannelInstanceUpdated, t) |
211 | 243 | ||
212 | auditLogger.update( | 244 | auditLogger.update( |
@@ -252,13 +284,13 @@ async function removeVideoChannel (req: express.Request, res: express.Response) | |||
252 | } | 284 | } |
253 | 285 | ||
254 | async function getVideoChannel (req: express.Request, res: express.Response) { | 286 | async function getVideoChannel (req: express.Request, res: express.Response) { |
255 | const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) | 287 | const videoChannel = res.locals.videoChannel |
256 | 288 | ||
257 | if (videoChannelWithVideos.isOutdated()) { | 289 | if (videoChannel.isOutdated()) { |
258 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) | 290 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } }) |
259 | } | 291 | } |
260 | 292 | ||
261 | return res.json(videoChannelWithVideos.toFormattedJSON()) | 293 | return res.json(videoChannel.toFormattedJSON()) |
262 | } | 294 | } |
263 | 295 | ||
264 | async function listVideoChannelPlaylists (req: express.Request, res: express.Response) { | 296 | async function listVideoChannelPlaylists (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 2447c1288..7fee278f2 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -17,7 +17,7 @@ import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../ | |||
17 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 17 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' |
18 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | 18 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' |
19 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | 19 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' |
20 | import { logger } from '../../../helpers/logger' | 20 | import { logger, loggerTagsFactory } from '../../../helpers/logger' |
21 | import { getFormattedObjects } from '../../../helpers/utils' | 21 | import { getFormattedObjects } from '../../../helpers/utils' |
22 | import { CONFIG } from '../../../initializers/config' | 22 | import { CONFIG } from '../../../initializers/config' |
23 | import { | 23 | import { |
@@ -67,6 +67,7 @@ import { ownershipVideoRouter } from './ownership' | |||
67 | import { rateVideoRouter } from './rate' | 67 | import { rateVideoRouter } from './rate' |
68 | import { watchingRouter } from './watching' | 68 | import { watchingRouter } from './watching' |
69 | 69 | ||
70 | const lTags = loggerTagsFactory('api', 'video') | ||
70 | const auditLogger = auditLoggerFactory('videos') | 71 | const auditLogger = auditLoggerFactory('videos') |
71 | const videosRouter = express.Router() | 72 | const videosRouter = express.Router() |
72 | 73 | ||
@@ -257,14 +258,14 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
257 | }) | 258 | }) |
258 | 259 | ||
259 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | 260 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) |
260 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) | 261 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) |
261 | 262 | ||
262 | return { videoCreated } | 263 | return { videoCreated } |
263 | }) | 264 | }) |
264 | 265 | ||
265 | // Create the torrent file in async way because it could be long | 266 | // Create the torrent file in async way because it could be long |
266 | createTorrentAndSetInfoHashAsync(video, videoFile) | 267 | createTorrentAndSetInfoHashAsync(video, videoFile) |
267 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err })) | 268 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) |
268 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) | 269 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) |
269 | .then(refreshedVideo => { | 270 | .then(refreshedVideo => { |
270 | if (!refreshedVideo) return | 271 | if (!refreshedVideo) return |
@@ -276,7 +277,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
276 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) | 277 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) |
277 | }) | 278 | }) |
278 | }) | 279 | }) |
279 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err })) | 280 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) })) |
280 | 281 | ||
281 | if (video.state === VideoState.TO_TRANSCODE) { | 282 | if (video.state === VideoState.TO_TRANSCODE) { |
282 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, res.locals.oauth.token.User) | 283 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, res.locals.oauth.token.User) |
@@ -389,7 +390,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
389 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | 390 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), |
390 | oldVideoAuditView | 391 | oldVideoAuditView |
391 | ) | 392 | ) |
392 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) | 393 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) |
393 | 394 | ||
394 | return videoInstanceUpdated | 395 | return videoInstanceUpdated |
395 | }) | 396 | }) |
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index 86adb6c69..a85d7c30b 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -107,7 +107,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) { | |||
107 | // We need more attributes for federation | 107 | // We need more attributes for federation |
108 | const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id) | 108 | const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id) |
109 | 109 | ||
110 | const oldVideoChannel = await VideoChannelModel.loadByIdAndPopulateAccount(targetVideo.channelId) | 110 | const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId) |
111 | 111 | ||
112 | targetVideo.channelId = channel.id | 112 | targetVideo.channelId = channel.id |
113 | 113 | ||
diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 557cbfdfb..022a17ff4 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts | |||
@@ -2,7 +2,9 @@ import * as express from 'express' | |||
2 | import { constants, promises as fs } from 'fs' | 2 | import { constants, promises as fs } from 'fs' |
3 | import { readFile } from 'fs-extra' | 3 | import { readFile } from 'fs-extra' |
4 | import { join } from 'path' | 4 | import { join } from 'path' |
5 | import { logger } from '@server/helpers/logger' | ||
5 | import { CONFIG } from '@server/initializers/config' | 6 | import { CONFIG } from '@server/initializers/config' |
7 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { HttpStatusCode } from '@shared/core-utils' | 8 | import { HttpStatusCode } from '@shared/core-utils' |
7 | import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' | 9 | import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' |
8 | import { root } from '../helpers/core-utils' | 10 | import { root } from '../helpers/core-utils' |
@@ -27,6 +29,7 @@ const embedMiddlewares = [ | |||
27 | ? embedCSP | 29 | ? embedCSP |
28 | : (req: express.Request, res: express.Response, next: express.NextFunction) => next(), | 30 | : (req: express.Request, res: express.Response, next: express.NextFunction) => next(), |
29 | 31 | ||
32 | // Set headers | ||
30 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 33 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
31 | res.removeHeader('X-Frame-Options') | 34 | res.removeHeader('X-Frame-Options') |
32 | 35 | ||
@@ -105,6 +108,24 @@ function serveServerTranslations (req: express.Request, res: express.Response) { | |||
105 | } | 108 | } |
106 | 109 | ||
107 | async function generateEmbedHtmlPage (req: express.Request, res: express.Response) { | 110 | async function generateEmbedHtmlPage (req: express.Request, res: express.Response) { |
111 | const hookName = req.originalUrl.startsWith('/video-playlists/') | ||
112 | ? 'filter:html.embed.video-playlist.allowed.result' | ||
113 | : 'filter:html.embed.video.allowed.result' | ||
114 | |||
115 | const allowParameters = { req } | ||
116 | |||
117 | const allowedResult = await Hooks.wrapFun( | ||
118 | isEmbedAllowed, | ||
119 | allowParameters, | ||
120 | hookName | ||
121 | ) | ||
122 | |||
123 | if (!allowedResult || allowedResult.allowed !== true) { | ||
124 | logger.info('Embed is not allowed.', { allowedResult }) | ||
125 | |||
126 | return sendHTML(allowedResult?.html || '', res) | ||
127 | } | ||
128 | |||
108 | const html = await ClientHtml.getEmbedHTML() | 129 | const html = await ClientHtml.getEmbedHTML() |
109 | 130 | ||
110 | return sendHTML(html, res) | 131 | return sendHTML(html, res) |
@@ -158,3 +179,10 @@ function serveClientOverride (path: string) { | |||
158 | } | 179 | } |
159 | } | 180 | } |
160 | } | 181 | } |
182 | |||
183 | type AllowedResult = { allowed: boolean, html?: string } | ||
184 | function isEmbedAllowed (_object: { | ||
185 | req: express.Request | ||
186 | }): AllowedResult { | ||
187 | return { allowed: true } | ||
188 | } | ||
diff --git a/server/controllers/download.ts b/server/controllers/download.ts index 27caa1518..9a8194c5c 100644 --- a/server/controllers/download.ts +++ b/server/controllers/download.ts | |||
@@ -1,8 +1,10 @@ | |||
1 | import * as cors from 'cors' | 1 | import * as cors from 'cors' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { logger } from '@server/helpers/logger' | ||
3 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' | 4 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' |
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
4 | import { getVideoFilePath } from '@server/lib/video-paths' | 6 | import { getVideoFilePath } from '@server/lib/video-paths' |
5 | import { MVideoFile, MVideoFullLight } from '@server/types/models' | 7 | import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
7 | import { VideoStreamingPlaylistType } from '@shared/models' | 9 | import { VideoStreamingPlaylistType } from '@shared/models' |
8 | import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' | 10 | import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' |
@@ -14,19 +16,19 @@ downloadRouter.use(cors()) | |||
14 | 16 | ||
15 | downloadRouter.use( | 17 | downloadRouter.use( |
16 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':filename', | 18 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':filename', |
17 | downloadTorrent | 19 | asyncMiddleware(downloadTorrent) |
18 | ) | 20 | ) |
19 | 21 | ||
20 | downloadRouter.use( | 22 | downloadRouter.use( |
21 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', | 23 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', |
22 | asyncMiddleware(videosDownloadValidator), | 24 | asyncMiddleware(videosDownloadValidator), |
23 | downloadVideoFile | 25 | asyncMiddleware(downloadVideoFile) |
24 | ) | 26 | ) |
25 | 27 | ||
26 | downloadRouter.use( | 28 | downloadRouter.use( |
27 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', | 29 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', |
28 | asyncMiddleware(videosDownloadValidator), | 30 | asyncMiddleware(videosDownloadValidator), |
29 | downloadHLSVideoFile | 31 | asyncMiddleware(downloadHLSVideoFile) |
30 | ) | 32 | ) |
31 | 33 | ||
32 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
@@ -41,28 +43,58 @@ async function downloadTorrent (req: express.Request, res: express.Response) { | |||
41 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) | 43 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) |
42 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 44 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) |
43 | 45 | ||
46 | const allowParameters = { torrentPath: result.path, downloadName: result.downloadName } | ||
47 | |||
48 | const allowedResult = await Hooks.wrapFun( | ||
49 | isTorrentDownloadAllowed, | ||
50 | allowParameters, | ||
51 | 'filter:api.download.torrent.allowed.result' | ||
52 | ) | ||
53 | |||
54 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
55 | |||
44 | return res.download(result.path, result.downloadName) | 56 | return res.download(result.path, result.downloadName) |
45 | } | 57 | } |
46 | 58 | ||
47 | function downloadVideoFile (req: express.Request, res: express.Response) { | 59 | async function downloadVideoFile (req: express.Request, res: express.Response) { |
48 | const video = res.locals.videoAll | 60 | const video = res.locals.videoAll |
49 | 61 | ||
50 | const videoFile = getVideoFile(req, video.VideoFiles) | 62 | const videoFile = getVideoFile(req, video.VideoFiles) |
51 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 63 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
52 | 64 | ||
65 | const allowParameters = { video, videoFile } | ||
66 | |||
67 | const allowedResult = await Hooks.wrapFun( | ||
68 | isVideoDownloadAllowed, | ||
69 | allowParameters, | ||
70 | 'filter:api.download.video.allowed.result' | ||
71 | ) | ||
72 | |||
73 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
74 | |||
53 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) | 75 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) |
54 | } | 76 | } |
55 | 77 | ||
56 | function downloadHLSVideoFile (req: express.Request, res: express.Response) { | 78 | async function downloadHLSVideoFile (req: express.Request, res: express.Response) { |
57 | const video = res.locals.videoAll | 79 | const video = res.locals.videoAll |
58 | const playlist = getHLSPlaylist(video) | 80 | const streamingPlaylist = getHLSPlaylist(video) |
59 | if (!playlist) return res.status(HttpStatusCode.NOT_FOUND_404).end | 81 | if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end |
60 | 82 | ||
61 | const videoFile = getVideoFile(req, playlist.VideoFiles) | 83 | const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles) |
62 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 84 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
63 | 85 | ||
64 | const filename = `${video.name}-${videoFile.resolution}p-${playlist.getStringType()}${videoFile.extname}` | 86 | const allowParameters = { video, streamingPlaylist, videoFile } |
65 | return res.download(getVideoFilePath(playlist, videoFile), filename) | 87 | |
88 | const allowedResult = await Hooks.wrapFun( | ||
89 | isVideoDownloadAllowed, | ||
90 | allowParameters, | ||
91 | 'filter:api.download.video.allowed.result' | ||
92 | ) | ||
93 | |||
94 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
95 | |||
96 | const filename = `${video.name}-${videoFile.resolution}p-${streamingPlaylist.getStringType()}${videoFile.extname}` | ||
97 | return res.download(getVideoFilePath(streamingPlaylist, videoFile), filename) | ||
66 | } | 98 | } |
67 | 99 | ||
68 | function getVideoFile (req: express.Request, files: MVideoFile[]) { | 100 | function getVideoFile (req: express.Request, files: MVideoFile[]) { |
@@ -76,3 +108,34 @@ function getHLSPlaylist (video: MVideoFullLight) { | |||
76 | 108 | ||
77 | return Object.assign(playlist, { Video: video }) | 109 | return Object.assign(playlist, { Video: video }) |
78 | } | 110 | } |
111 | |||
112 | type AllowedResult = { | ||
113 | allowed: boolean | ||
114 | errorMessage?: string | ||
115 | } | ||
116 | |||
117 | function isTorrentDownloadAllowed (_object: { | ||
118 | torrentPath: string | ||
119 | }): AllowedResult { | ||
120 | return { allowed: true } | ||
121 | } | ||
122 | |||
123 | function isVideoDownloadAllowed (_object: { | ||
124 | video: MVideo | ||
125 | videoFile: MVideoFile | ||
126 | streamingPlaylist?: MStreamingPlaylist | ||
127 | }): AllowedResult { | ||
128 | return { allowed: true } | ||
129 | } | ||
130 | |||
131 | function checkAllowResult (res: express.Response, allowParameters: any, result?: AllowedResult) { | ||
132 | if (!result || result.allowed !== true) { | ||
133 | logger.info('Download is not allowed.', { result, allowParameters }) | ||
134 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
135 | .json({ error: result?.errorMessage || 'Refused download' }) | ||
136 | |||
137 | return false | ||
138 | } | ||
139 | |||
140 | return true | ||
141 | } | ||
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index e29a8fe1d..921067e65 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as Feed from 'pfeed' | 2 | import * as Feed from 'pfeed' |
3 | import { VideoFilter } from '../../shared/models/videos/video-query.type' | ||
3 | import { buildNSFWFilter } from '../helpers/express-utils' | 4 | import { buildNSFWFilter } from '../helpers/express-utils' |
4 | import { CONFIG } from '../initializers/config' | 5 | import { CONFIG } from '../initializers/config' |
5 | import { FEEDS, ROUTE_CACHE_LIFETIME, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants' | 6 | import { FEEDS, PREVIEWS_SIZE, ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants' |
6 | import { | 7 | import { |
7 | asyncMiddleware, | 8 | asyncMiddleware, |
8 | commonVideosFiltersValidator, | 9 | commonVideosFiltersValidator, |
@@ -17,7 +18,6 @@ import { | |||
17 | import { cacheRoute } from '../middlewares/cache' | 18 | import { cacheRoute } from '../middlewares/cache' |
18 | import { VideoModel } from '../models/video/video' | 19 | import { VideoModel } from '../models/video/video' |
19 | import { VideoCommentModel } from '../models/video/video-comment' | 20 | import { VideoCommentModel } from '../models/video/video-comment' |
20 | import { VideoFilter } from '../../shared/models/videos/video-query.type' | ||
21 | 21 | ||
22 | const feedsRouter = express.Router() | 22 | const feedsRouter = express.Router() |
23 | 23 | ||
@@ -318,9 +318,9 @@ function addVideosToFeed (feed, videos: VideoModel[]) { | |||
318 | }, | 318 | }, |
319 | thumbnail: [ | 319 | thumbnail: [ |
320 | { | 320 | { |
321 | url: WEBSERVER.URL + video.getMiniatureStaticPath(), | 321 | url: WEBSERVER.URL + video.getPreviewStaticPath(), |
322 | height: THUMBNAILS_SIZE.height, | 322 | height: PREVIEWS_SIZE.height, |
323 | width: THUMBNAILS_SIZE.width | 323 | width: PREVIEWS_SIZE.width |
324 | } | 324 | } |
325 | ] | 325 | ] |
326 | }) | 326 | }) |
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts index 4e553479b..6f71fdb16 100644 --- a/server/controllers/lazy-static.ts +++ b/server/controllers/lazy-static.ts | |||
@@ -4,10 +4,10 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache | |||
4 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 4 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
5 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
6 | import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' | 6 | import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' |
7 | import { avatarPathUnsafeCache, pushAvatarProcessInQueue } from '../lib/avatar' | 7 | import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image' |
8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' | 8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' |
9 | import { asyncMiddleware } from '../middlewares' | 9 | import { asyncMiddleware } from '../middlewares' |
10 | import { AvatarModel } from '../models/avatar/avatar' | 10 | import { ActorImageModel } from '../models/account/actor-image' |
11 | 11 | ||
12 | const lazyStaticRouter = express.Router() | 12 | const lazyStaticRouter = express.Router() |
13 | 13 | ||
@@ -15,7 +15,12 @@ lazyStaticRouter.use(cors()) | |||
15 | 15 | ||
16 | lazyStaticRouter.use( | 16 | lazyStaticRouter.use( |
17 | LAZY_STATIC_PATHS.AVATARS + ':filename', | 17 | LAZY_STATIC_PATHS.AVATARS + ':filename', |
18 | asyncMiddleware(getAvatar) | 18 | asyncMiddleware(getActorImage) |
19 | ) | ||
20 | |||
21 | lazyStaticRouter.use( | ||
22 | LAZY_STATIC_PATHS.BANNERS + ':filename', | ||
23 | asyncMiddleware(getActorImage) | ||
19 | ) | 24 | ) |
20 | 25 | ||
21 | lazyStaticRouter.use( | 26 | lazyStaticRouter.use( |
@@ -43,36 +48,36 @@ export { | |||
43 | 48 | ||
44 | // --------------------------------------------------------------------------- | 49 | // --------------------------------------------------------------------------- |
45 | 50 | ||
46 | async function getAvatar (req: express.Request, res: express.Response) { | 51 | async function getActorImage (req: express.Request, res: express.Response) { |
47 | const filename = req.params.filename | 52 | const filename = req.params.filename |
48 | 53 | ||
49 | if (avatarPathUnsafeCache.has(filename)) { | 54 | if (actorImagePathUnsafeCache.has(filename)) { |
50 | return res.sendFile(avatarPathUnsafeCache.get(filename), { maxAge: STATIC_MAX_AGE.SERVER }) | 55 | return res.sendFile(actorImagePathUnsafeCache.get(filename), { maxAge: STATIC_MAX_AGE.SERVER }) |
51 | } | 56 | } |
52 | 57 | ||
53 | const avatar = await AvatarModel.loadByName(filename) | 58 | const image = await ActorImageModel.loadByName(filename) |
54 | if (!avatar) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 59 | if (!image) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) |
55 | 60 | ||
56 | if (avatar.onDisk === false) { | 61 | if (image.onDisk === false) { |
57 | if (!avatar.fileUrl) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 62 | if (!image.fileUrl) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) |
58 | 63 | ||
59 | logger.info('Lazy serve remote avatar image %s.', avatar.fileUrl) | 64 | logger.info('Lazy serve remote actor image %s.', image.fileUrl) |
60 | 65 | ||
61 | try { | 66 | try { |
62 | await pushAvatarProcessInQueue({ filename: avatar.filename, fileUrl: avatar.fileUrl }) | 67 | await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl, type: image.type }) |
63 | } catch (err) { | 68 | } catch (err) { |
64 | logger.warn('Cannot process remote avatar %s.', avatar.fileUrl, { err }) | 69 | logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err }) |
65 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 70 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) |
66 | } | 71 | } |
67 | 72 | ||
68 | avatar.onDisk = true | 73 | image.onDisk = true |
69 | avatar.save() | 74 | image.save() |
70 | .catch(err => logger.error('Cannot save new avatar disk state.', { err })) | 75 | .catch(err => logger.error('Cannot save new actor image disk state.', { err })) |
71 | } | 76 | } |
72 | 77 | ||
73 | const path = avatar.getPath() | 78 | const path = image.getPath() |
74 | 79 | ||
75 | avatarPathUnsafeCache.set(filename, path) | 80 | actorImagePathUnsafeCache.set(filename, path) |
76 | return res.sendFile(path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) | 81 | return res.sendFile(path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) |
77 | } | 82 | } |
78 | 83 | ||
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts index 6a1ccc0bf..105f51518 100644 --- a/server/controllers/plugins.ts +++ b/server/controllers/plugins.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' | ||
3 | import { join } from 'path' | 2 | import { join } from 'path' |
4 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' | 3 | import { logger } from '@server/helpers/logger' |
5 | import { getPluginValidator, pluginStaticDirectoryValidator, getExternalAuthValidator } from '../middlewares/validators/plugins' | 4 | import { optionalAuthenticate } from '@server/middlewares/auth' |
6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' | ||
7 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
8 | import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n' | 5 | import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n' |
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
9 | import { PluginType } from '../../shared/models/plugins/plugin.type' | 7 | import { PluginType } from '../../shared/models/plugins/plugin.type' |
10 | import { isTestInstance } from '../helpers/core-utils' | 8 | import { isTestInstance } from '../helpers/core-utils' |
11 | import { logger } from '@server/helpers/logger' | 9 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' |
12 | import { optionalAuthenticate } from '@server/middlewares/oauth' | 10 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' |
11 | import { getExternalAuthValidator, getPluginValidator, pluginStaticDirectoryValidator } from '../middlewares/validators/plugins' | ||
12 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' | ||
13 | 13 | ||
14 | const sendFileOptions = { | 14 | const sendFileOptions = { |
15 | maxAge: '30 days', | 15 | maxAge: '30 days', |
diff --git a/server/controllers/services.ts b/server/controllers/services.ts index d0217c30a..189e1651b 100644 --- a/server/controllers/services.ts +++ b/server/controllers/services.ts | |||
@@ -3,6 +3,7 @@ import { EMBED_SIZE, PREVIEWS_SIZE, WEBSERVER, THUMBNAILS_SIZE } from '../initia | |||
3 | import { asyncMiddleware, oembedValidator } from '../middlewares' | 3 | import { asyncMiddleware, oembedValidator } from '../middlewares' |
4 | import { accountNameWithHostGetValidator } from '../middlewares/validators' | 4 | import { accountNameWithHostGetValidator } from '../middlewares/validators' |
5 | import { MChannelSummary } from '@server/types/models' | 5 | import { MChannelSummary } from '@server/types/models' |
6 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
6 | 7 | ||
7 | const servicesRouter = express.Router() | 8 | const servicesRouter = express.Router() |
8 | 9 | ||
@@ -79,6 +80,7 @@ function buildOEmbed (options: { | |||
79 | const embedUrl = webserverUrl + embedPath | 80 | const embedUrl = webserverUrl + embedPath |
80 | let embedWidth = EMBED_SIZE.width | 81 | let embedWidth = EMBED_SIZE.width |
81 | let embedHeight = EMBED_SIZE.height | 82 | let embedHeight = EMBED_SIZE.height |
83 | const embedTitle = escapeHTML(title) | ||
82 | 84 | ||
83 | let thumbnailUrl = previewPath | 85 | let thumbnailUrl = previewPath |
84 | ? webserverUrl + previewPath | 86 | ? webserverUrl + previewPath |
@@ -96,7 +98,7 @@ function buildOEmbed (options: { | |||
96 | } | 98 | } |
97 | 99 | ||
98 | const html = `<iframe width="${embedWidth}" height="${embedHeight}" sandbox="allow-same-origin allow-scripts" ` + | 100 | const html = `<iframe width="${embedWidth}" height="${embedHeight}" sandbox="allow-same-origin allow-scripts" ` + |
99 | `src="${embedUrl}" frameborder="0" allowfullscreen></iframe>` | 101 | `title="${embedTitle}" src="${embedUrl}" frameborder="0" allowfullscreen></iframe>` |
100 | 102 | ||
101 | const json: any = { | 103 | const json: any = { |
102 | type: 'video', | 104 | type: 'video', |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 4baa31117..e6a0628e6 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -252,9 +252,9 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
252 | avatar: { | 252 | avatar: { |
253 | file: { | 253 | file: { |
254 | size: { | 254 | size: { |
255 | max: CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max | 255 | max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max |
256 | }, | 256 | }, |
257 | extensions: CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME | 257 | extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME |
258 | } | 258 | } |
259 | }, | 259 | }, |
260 | video: { | 260 | video: { |