diff options
Diffstat (limited to 'server/controllers')
-rw-r--r-- | server/controllers/api/accounts.ts | 168 | ||||
-rw-r--r-- | server/controllers/api/video-channel.ts | 173 |
2 files changed, 173 insertions, 168 deletions
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 85987c912..ccae0436b 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts | |||
@@ -1,30 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils' | 2 | import { getFormattedObjects } from '../../helpers/utils' |
3 | import { | 3 | import { |
4 | asyncMiddleware, | 4 | asyncMiddleware, |
5 | authenticate, | ||
6 | listVideoAccountChannelsValidator, | 5 | listVideoAccountChannelsValidator, |
7 | optionalAuthenticate, | 6 | optionalAuthenticate, |
8 | paginationValidator, | 7 | paginationValidator, |
9 | setDefaultPagination, | 8 | setDefaultPagination, |
10 | setDefaultSort, | 9 | setDefaultSort |
11 | videoChannelsAddValidator, | ||
12 | videoChannelsGetValidator, | ||
13 | videoChannelsRemoveValidator, | ||
14 | videoChannelsUpdateValidator | ||
15 | } from '../../middlewares' | 10 | } from '../../middlewares' |
16 | import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' | 11 | import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' |
17 | import { AccountModel } from '../../models/account/account' | 12 | import { AccountModel } from '../../models/account/account' |
18 | import { VideoModel } from '../../models/video/video' | 13 | import { VideoModel } from '../../models/video/video' |
19 | import { isNSFWHidden } from '../../helpers/express-utils' | 14 | import { isNSFWHidden } from '../../helpers/express-utils' |
20 | import { VideoChannelModel } from '../../models/video/video-channel' | 15 | import { VideoChannelModel } from '../../models/video/video-channel' |
21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | ||
22 | import { sendUpdateActor } from '../../lib/activitypub/send' | ||
23 | import { createVideoChannel } from '../../lib/video-channel' | ||
24 | import { setAsyncActorKeys } from '../../lib/activitypub' | ||
25 | import { sequelizeTypescript } from '../../initializers' | ||
26 | import { logger } from '../../helpers/logger' | ||
27 | import { retryTransactionWrapper } from '../../helpers/database-utils' | ||
28 | 16 | ||
29 | const accountsRouter = express.Router() | 17 | const accountsRouter = express.Router() |
30 | 18 | ||
@@ -56,39 +44,6 @@ accountsRouter.get('/:accountId/video-channels', | |||
56 | asyncMiddleware(listVideoAccountChannels) | 44 | asyncMiddleware(listVideoAccountChannels) |
57 | ) | 45 | ) |
58 | 46 | ||
59 | accountsRouter.post('/:accountId/video-channels', | ||
60 | authenticate, | ||
61 | videoChannelsAddValidator, | ||
62 | asyncMiddleware(addVideoChannelRetryWrapper) | ||
63 | ) | ||
64 | |||
65 | accountsRouter.put('/:accountId/video-channels/:id', | ||
66 | authenticate, | ||
67 | asyncMiddleware(videoChannelsUpdateValidator), | ||
68 | updateVideoChannelRetryWrapper | ||
69 | ) | ||
70 | |||
71 | accountsRouter.delete('/:accountId/video-channels/:id', | ||
72 | authenticate, | ||
73 | asyncMiddleware(videoChannelsRemoveValidator), | ||
74 | asyncMiddleware(removeVideoChannelRetryWrapper) | ||
75 | ) | ||
76 | |||
77 | accountsRouter.get('/:accountId/video-channels/:id', | ||
78 | asyncMiddleware(videoChannelsGetValidator), | ||
79 | asyncMiddleware(getVideoChannel) | ||
80 | ) | ||
81 | |||
82 | accountsRouter.get('/:accountId/video-channels/:id/videos', | ||
83 | asyncMiddleware(videoChannelsGetValidator), | ||
84 | paginationValidator, | ||
85 | videosSortValidator, | ||
86 | setDefaultSort, | ||
87 | setDefaultPagination, | ||
88 | optionalAuthenticate, | ||
89 | asyncMiddleware(listVideoChannelVideos) | ||
90 | ) | ||
91 | |||
92 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
93 | 48 | ||
94 | export { | 49 | export { |
@@ -115,125 +70,6 @@ async function listVideoAccountChannels (req: express.Request, res: express.Resp | |||
115 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 70 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
116 | } | 71 | } |
117 | 72 | ||
118 | // Wrapper to video channel add that retry the async function if there is a database error | ||
119 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail | ||
120 | async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
121 | const options = { | ||
122 | arguments: [ req, res ], | ||
123 | errorMessage: 'Cannot insert the video video channel with many retries.' | ||
124 | } | ||
125 | |||
126 | const videoChannel = await retryTransactionWrapper(addVideoChannel, options) | ||
127 | return res.json({ | ||
128 | videoChannel: { | ||
129 | id: videoChannel.id, | ||
130 | uuid: videoChannel.Actor.uuid | ||
131 | } | ||
132 | }).end() | ||
133 | } | ||
134 | |||
135 | async function addVideoChannel (req: express.Request, res: express.Response) { | ||
136 | const videoChannelInfo: VideoChannelCreate = req.body | ||
137 | const account: AccountModel = res.locals.oauth.token.User.Account | ||
138 | |||
139 | const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { | ||
140 | return createVideoChannel(videoChannelInfo, account, t) | ||
141 | }) | ||
142 | |||
143 | setAsyncActorKeys(videoChannelCreated.Actor) | ||
144 | .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) | ||
145 | |||
146 | logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) | ||
147 | |||
148 | return videoChannelCreated | ||
149 | } | ||
150 | |||
151 | async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
152 | const options = { | ||
153 | arguments: [ req, res ], | ||
154 | errorMessage: 'Cannot update the video with many retries.' | ||
155 | } | ||
156 | |||
157 | await retryTransactionWrapper(updateVideoChannel, options) | ||
158 | |||
159 | return res.type('json').status(204).end() | ||
160 | } | ||
161 | |||
162 | async function updateVideoChannel (req: express.Request, res: express.Response) { | ||
163 | const videoChannelInstance = res.locals.videoChannel as VideoChannelModel | ||
164 | const videoChannelFieldsSave = videoChannelInstance.toJSON() | ||
165 | const videoChannelInfoToUpdate = req.body as VideoChannelUpdate | ||
166 | |||
167 | try { | ||
168 | await sequelizeTypescript.transaction(async t => { | ||
169 | const sequelizeOptions = { | ||
170 | transaction: t | ||
171 | } | ||
172 | |||
173 | if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) | ||
174 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) | ||
175 | if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) | ||
176 | |||
177 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) | ||
178 | await sendUpdateActor(videoChannelInstanceUpdated, t) | ||
179 | }) | ||
180 | |||
181 | logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) | ||
182 | } catch (err) { | ||
183 | logger.debug('Cannot update the video channel.', { err }) | ||
184 | |||
185 | // Force fields we want to update | ||
186 | // If the transaction is retried, sequelize will think the object has not changed | ||
187 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
188 | resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) | ||
189 | |||
190 | throw err | ||
191 | } | ||
192 | } | ||
193 | |||
194 | async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
195 | const options = { | ||
196 | arguments: [ req, res ], | ||
197 | errorMessage: 'Cannot remove the video channel with many retries.' | ||
198 | } | ||
199 | |||
200 | await retryTransactionWrapper(removeVideoChannel, options) | ||
201 | |||
202 | return res.type('json').status(204).end() | ||
203 | } | ||
204 | |||
205 | async function removeVideoChannel (req: express.Request, res: express.Response) { | ||
206 | const videoChannelInstance: VideoChannelModel = res.locals.videoChannel | ||
207 | |||
208 | return sequelizeTypescript.transaction(async t => { | ||
209 | await videoChannelInstance.destroy({ transaction: t }) | ||
210 | |||
211 | logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) | ||
212 | }) | ||
213 | |||
214 | } | ||
215 | |||
216 | async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
217 | const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) | ||
218 | |||
219 | return res.json(videoChannelWithVideos.toFormattedJSON()) | ||
220 | } | ||
221 | |||
222 | async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
223 | const videoChannelInstance: VideoChannelModel = res.locals.videoChannel | ||
224 | |||
225 | const resultList = await VideoModel.listForApi({ | ||
226 | start: req.query.start, | ||
227 | count: req.query.count, | ||
228 | sort: req.query.sort, | ||
229 | hideNSFW: isNSFWHidden(res), | ||
230 | withFiles: false, | ||
231 | videoChannelId: videoChannelInstance.id | ||
232 | }) | ||
233 | |||
234 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
235 | } | ||
236 | |||
237 | async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | 73 | async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { |
238 | const account: AccountModel = res.locals.account | 74 | const account: AccountModel = res.locals.account |
239 | 75 | ||
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index d57273747..6241aaa5c 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -1,13 +1,30 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects } from '../../helpers/utils' | 2 | import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils' |
3 | import { | 3 | import { |
4 | asyncMiddleware, | 4 | asyncMiddleware, |
5 | authenticate, | ||
6 | optionalAuthenticate, | ||
5 | paginationValidator, | 7 | paginationValidator, |
6 | setDefaultPagination, | 8 | setDefaultPagination, |
7 | setDefaultSort, | 9 | setDefaultSort, |
8 | videoChannelsSortValidator | 10 | videoChannelsAddValidator, |
11 | videoChannelsGetValidator, | ||
12 | videoChannelsRemoveValidator, | ||
13 | videoChannelsSortValidator, | ||
14 | videoChannelsUpdateValidator | ||
9 | } from '../../middlewares' | 15 | } from '../../middlewares' |
10 | import { VideoChannelModel } from '../../models/video/video-channel' | 16 | import { VideoChannelModel } from '../../models/video/video-channel' |
17 | import { videosSortValidator } from '../../middlewares/validators' | ||
18 | import { sendUpdateActor } from '../../lib/activitypub/send' | ||
19 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | ||
20 | import { createVideoChannel } from '../../lib/video-channel' | ||
21 | import { isNSFWHidden } from '../../helpers/express-utils' | ||
22 | import { setAsyncActorKeys } from '../../lib/activitypub' | ||
23 | import { retryTransactionWrapper } from '../../helpers/database-utils' | ||
24 | import { AccountModel } from '../../models/account/account' | ||
25 | import { sequelizeTypescript } from '../../initializers' | ||
26 | import { logger } from '../../helpers/logger' | ||
27 | import { VideoModel } from '../../models/video/video' | ||
11 | 28 | ||
12 | const videoChannelRouter = express.Router() | 29 | const videoChannelRouter = express.Router() |
13 | 30 | ||
@@ -19,6 +36,39 @@ videoChannelRouter.get('/', | |||
19 | asyncMiddleware(listVideoChannels) | 36 | asyncMiddleware(listVideoChannels) |
20 | ) | 37 | ) |
21 | 38 | ||
39 | videoChannelRouter.post('/', | ||
40 | authenticate, | ||
41 | videoChannelsAddValidator, | ||
42 | asyncMiddleware(addVideoChannelRetryWrapper) | ||
43 | ) | ||
44 | |||
45 | videoChannelRouter.put('/:id', | ||
46 | authenticate, | ||
47 | asyncMiddleware(videoChannelsUpdateValidator), | ||
48 | updateVideoChannelRetryWrapper | ||
49 | ) | ||
50 | |||
51 | videoChannelRouter.delete('/:id', | ||
52 | authenticate, | ||
53 | asyncMiddleware(videoChannelsRemoveValidator), | ||
54 | asyncMiddleware(removeVideoChannelRetryWrapper) | ||
55 | ) | ||
56 | |||
57 | videoChannelRouter.get('/:id', | ||
58 | asyncMiddleware(videoChannelsGetValidator), | ||
59 | asyncMiddleware(getVideoChannel) | ||
60 | ) | ||
61 | |||
62 | videoChannelRouter.get('/:id/videos', | ||
63 | asyncMiddleware(videoChannelsGetValidator), | ||
64 | paginationValidator, | ||
65 | videosSortValidator, | ||
66 | setDefaultSort, | ||
67 | setDefaultPagination, | ||
68 | optionalAuthenticate, | ||
69 | asyncMiddleware(listVideoChannelVideos) | ||
70 | ) | ||
71 | |||
22 | // --------------------------------------------------------------------------- | 72 | // --------------------------------------------------------------------------- |
23 | 73 | ||
24 | export { | 74 | export { |
@@ -32,3 +82,122 @@ async function listVideoChannels (req: express.Request, res: express.Response, n | |||
32 | 82 | ||
33 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 83 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
34 | } | 84 | } |
85 | |||
86 | // Wrapper to video channel add that retry the async function if there is a database error | ||
87 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail | ||
88 | async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
89 | const options = { | ||
90 | arguments: [ req, res ], | ||
91 | errorMessage: 'Cannot insert the video video channel with many retries.' | ||
92 | } | ||
93 | |||
94 | const videoChannel = await retryTransactionWrapper(addVideoChannel, options) | ||
95 | return res.json({ | ||
96 | videoChannel: { | ||
97 | id: videoChannel.id, | ||
98 | uuid: videoChannel.Actor.uuid | ||
99 | } | ||
100 | }).end() | ||
101 | } | ||
102 | |||
103 | async function addVideoChannel (req: express.Request, res: express.Response) { | ||
104 | const videoChannelInfo: VideoChannelCreate = req.body | ||
105 | const account: AccountModel = res.locals.oauth.token.User.Account | ||
106 | |||
107 | const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { | ||
108 | return createVideoChannel(videoChannelInfo, account, t) | ||
109 | }) | ||
110 | |||
111 | setAsyncActorKeys(videoChannelCreated.Actor) | ||
112 | .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) | ||
113 | |||
114 | logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) | ||
115 | |||
116 | return videoChannelCreated | ||
117 | } | ||
118 | |||
119 | async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
120 | const options = { | ||
121 | arguments: [ req, res ], | ||
122 | errorMessage: 'Cannot update the video with many retries.' | ||
123 | } | ||
124 | |||
125 | await retryTransactionWrapper(updateVideoChannel, options) | ||
126 | |||
127 | return res.type('json').status(204).end() | ||
128 | } | ||
129 | |||
130 | async function updateVideoChannel (req: express.Request, res: express.Response) { | ||
131 | const videoChannelInstance = res.locals.videoChannel as VideoChannelModel | ||
132 | const videoChannelFieldsSave = videoChannelInstance.toJSON() | ||
133 | const videoChannelInfoToUpdate = req.body as VideoChannelUpdate | ||
134 | |||
135 | try { | ||
136 | await sequelizeTypescript.transaction(async t => { | ||
137 | const sequelizeOptions = { | ||
138 | transaction: t | ||
139 | } | ||
140 | |||
141 | if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) | ||
142 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) | ||
143 | if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) | ||
144 | |||
145 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) | ||
146 | await sendUpdateActor(videoChannelInstanceUpdated, t) | ||
147 | }) | ||
148 | |||
149 | logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) | ||
150 | } catch (err) { | ||
151 | logger.debug('Cannot update the video channel.', { err }) | ||
152 | |||
153 | // Force fields we want to update | ||
154 | // If the transaction is retried, sequelize will think the object has not changed | ||
155 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
156 | resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) | ||
157 | |||
158 | throw err | ||
159 | } | ||
160 | } | ||
161 | |||
162 | async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
163 | const options = { | ||
164 | arguments: [ req, res ], | ||
165 | errorMessage: 'Cannot remove the video channel with many retries.' | ||
166 | } | ||
167 | |||
168 | await retryTransactionWrapper(removeVideoChannel, options) | ||
169 | |||
170 | return res.type('json').status(204).end() | ||
171 | } | ||
172 | |||
173 | async function removeVideoChannel (req: express.Request, res: express.Response) { | ||
174 | const videoChannelInstance: VideoChannelModel = res.locals.videoChannel | ||
175 | |||
176 | return sequelizeTypescript.transaction(async t => { | ||
177 | await videoChannelInstance.destroy({ transaction: t }) | ||
178 | |||
179 | logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) | ||
180 | }) | ||
181 | |||
182 | } | ||
183 | |||
184 | async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
185 | const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) | ||
186 | |||
187 | return res.json(videoChannelWithVideos.toFormattedJSON()) | ||
188 | } | ||
189 | |||
190 | async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
191 | const videoChannelInstance: VideoChannelModel = res.locals.videoChannel | ||
192 | |||
193 | const resultList = await VideoModel.listForApi({ | ||
194 | start: req.query.start, | ||
195 | count: req.query.count, | ||
196 | sort: req.query.sort, | ||
197 | hideNSFW: isNSFWHidden(res), | ||
198 | withFiles: false, | ||
199 | videoChannelId: videoChannelInstance.id | ||
200 | }) | ||
201 | |||
202 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
203 | } | ||