From 48dce1c90dff4e90a4bcffefaecf157336cf904b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 24 Apr 2018 17:05:32 +0200 Subject: Update video channel routes --- server/controllers/api/accounts.ts | 209 ++++++++++++++++++++++++++++--- server/controllers/api/index.ts | 2 + server/controllers/api/video-channel.ts | 34 +++++ server/controllers/api/videos/channel.ts | 177 -------------------------- server/controllers/api/videos/index.ts | 17 ++- server/controllers/feeds.ts | 14 +-- 6 files changed, 246 insertions(+), 207 deletions(-) create mode 100644 server/controllers/api/video-channel.ts delete mode 100644 server/controllers/api/videos/channel.ts (limited to 'server/controllers') diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 06ab04033..04c5897c5 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -1,11 +1,30 @@ import * as express from 'express' -import { getFormattedObjects } from '../../helpers/utils' -import { asyncMiddleware, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort } from '../../middlewares' +import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils' +import { + asyncMiddleware, + authenticate, + listVideoAccountChannelsValidator, + optionalAuthenticate, + paginationValidator, + setDefaultPagination, + setDefaultSort, + videoChannelsAddValidator, + videoChannelsGetValidator, + videoChannelsRemoveValidator, + videoChannelsUpdateValidator +} from '../../middlewares' import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' import { VideoModel } from '../../models/video/video' -import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type' import { isNSFWHidden } from '../../helpers/express-utils' +import { VideoChannelModel } from '../../models/video/video-channel' +import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' +import { sendUpdateActor } from '../../lib/activitypub/send' +import { createVideoChannel } from '../../lib/video-channel' +import { setAsyncActorKeys } from '../../lib/activitypub' +import { sequelizeTypescript } from '../../initializers' +import { logger } from '../../helpers/logger' +import { retryTransactionWrapper } from '../../helpers/database-utils' const accountsRouter = express.Router() @@ -29,7 +48,45 @@ accountsRouter.get('/:id/videos', setDefaultSort, setDefaultPagination, optionalAuthenticate, - asyncMiddleware(getAccountVideos) + asyncMiddleware(listAccountVideos) +) + +accountsRouter.get('/:accountId/video-channels', + asyncMiddleware(listVideoAccountChannelsValidator), + asyncMiddleware(listVideoAccountChannels) +) + +accountsRouter.post('/:accountId/video-channels', + authenticate, + videoChannelsAddValidator, + asyncMiddleware(addVideoChannelRetryWrapper) +) + +accountsRouter.put('/:accountId/video-channels/:id', + authenticate, + asyncMiddleware(videoChannelsUpdateValidator), + updateVideoChannelRetryWrapper +) + +accountsRouter.delete('/:accountId/video-channels/:id', + authenticate, + asyncMiddleware(videoChannelsRemoveValidator), + asyncMiddleware(removeVideoChannelRetryWrapper) +) + +accountsRouter.get('/:accountId/video-channels/:id', + asyncMiddleware(videoChannelsGetValidator), + asyncMiddleware(getVideoChannel) +) + +accountsRouter.get('/:accountId/video-channels/:id/videos', + asyncMiddleware(videoChannelsGetValidator), + paginationValidator, + videosSortValidator, + setDefaultSort, + setDefaultPagination, + optionalAuthenticate, + asyncMiddleware(listVideoChannelVideos) ) // --------------------------------------------------------------------------- @@ -52,18 +109,142 @@ async function listAccounts (req: express.Request, res: express.Response, next: return res.json(getFormattedObjects(resultList.data, resultList.total)) } -async function getAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { +async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) { + const resultList = await VideoChannelModel.listByAccount(res.locals.account.id) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +// Wrapper to video channel add that retry the async function if there is a database error +// We need this because we run the transaction in SERIALIZABLE isolation that can fail +async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot insert the video video channel with many retries.' + } + + const videoChannel = await retryTransactionWrapper(addVideoChannel, options) + return res.json({ + videoChannel: { + id: videoChannel.id + } + }).end() +} + +async function addVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInfo: VideoChannelCreate = req.body + const account: AccountModel = res.locals.oauth.token.User.Account + + const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { + return createVideoChannel(videoChannelInfo, account, t) + }) + + setAsyncActorKeys(videoChannelCreated.Actor) + .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) + + logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) + + return videoChannelCreated +} + +async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot update the video with many retries.' + } + + await retryTransactionWrapper(updateVideoChannel, options) + + return res.type('json').status(204).end() +} + +async function updateVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInstance = res.locals.videoChannel as VideoChannelModel + const videoChannelFieldsSave = videoChannelInstance.toJSON() + const videoChannelInfoToUpdate = req.body as VideoChannelUpdate + + try { + await sequelizeTypescript.transaction(async t => { + const sequelizeOptions = { + transaction: t + } + + if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) + if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) + if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) + + const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) + await sendUpdateActor(videoChannelInstanceUpdated, t) + }) + + logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + } catch (err) { + logger.debug('Cannot update the video channel.', { err }) + + // Force fields we want to update + // If the transaction is retried, sequelize will think the object has not changed + // So it will skip the SQL request, even if the last one was ROLLBACKed! + resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) + + throw err + } +} + +async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot remove the video channel with many retries.' + } + + await retryTransactionWrapper(removeVideoChannel, options) + + return res.type('json').status(204).end() +} + +async function removeVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInstance: VideoChannelModel = res.locals.videoChannel + + return sequelizeTypescript.transaction(async t => { + await videoChannelInstance.destroy({ transaction: t }) + + logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + }) + +} + +async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { + const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) + + return res.json(videoChannelWithVideos.toFormattedJSON()) +} + +async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { + const videoChannelInstance: VideoChannelModel = res.locals.videoChannel + + const resultList = await VideoModel.listForApi({ + start: req.query.start, + count: req.query.count, + sort: req.query.sort, + hideNSFW: isNSFWHidden(res), + withFiles: false, + videoChannelId: videoChannelInstance.id + }) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + + +async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { const account: AccountModel = res.locals.account - const resultList = await VideoModel.listForApi( - req.query.start as number, - req.query.count as number, - req.query.sort as VideoSortField, - isNSFWHidden(res), - null, - false, - account.id - ) + const resultList = await VideoModel.listForApi({ + start: req.query.start, + count: req.query.count, + sort: req.query.sort, + hideNSFW: isNSFWHidden(res), + withFiles: false, + accountId: account.id + }) return res.json(getFormattedObjects(resultList.data, resultList.total)) } diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts index 964d5d04c..8f63b9535 100644 --- a/server/controllers/api/index.ts +++ b/server/controllers/api/index.ts @@ -7,6 +7,7 @@ import { usersRouter } from './users' import { accountsRouter } from './accounts' import { videosRouter } from './videos' import { badRequest } from '../../helpers/express-utils' +import { videoChannelRouter } from './video-channel' const apiRouter = express.Router() @@ -15,6 +16,7 @@ apiRouter.use('/oauth-clients', oauthClientsRouter) apiRouter.use('/config', configRouter) apiRouter.use('/users', usersRouter) apiRouter.use('/accounts', accountsRouter) +apiRouter.use('/video-channels', videoChannelRouter) apiRouter.use('/videos', videosRouter) apiRouter.use('/jobs', jobsRouter) apiRouter.use('/ping', pong) diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts new file mode 100644 index 000000000..d57273747 --- /dev/null +++ b/server/controllers/api/video-channel.ts @@ -0,0 +1,34 @@ +import * as express from 'express' +import { getFormattedObjects } from '../../helpers/utils' +import { + asyncMiddleware, + paginationValidator, + setDefaultPagination, + setDefaultSort, + videoChannelsSortValidator +} from '../../middlewares' +import { VideoChannelModel } from '../../models/video/video-channel' + +const videoChannelRouter = express.Router() + +videoChannelRouter.get('/', + paginationValidator, + videoChannelsSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listVideoChannels) +) + +// --------------------------------------------------------------------------- + +export { + videoChannelRouter +} + +// --------------------------------------------------------------------------- + +async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) { + const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts deleted file mode 100644 index e547d375f..000000000 --- a/server/controllers/api/videos/channel.ts +++ /dev/null @@ -1,177 +0,0 @@ -import * as express from 'express' -import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared' -import { retryTransactionWrapper } from '../../../helpers/database-utils' -import { logger } from '../../../helpers/logger' -import { getFormattedObjects, resetSequelizeInstance } from '../../../helpers/utils' -import { sequelizeTypescript } from '../../../initializers' -import { setAsyncActorKeys } from '../../../lib/activitypub' -import { sendUpdateActor } from '../../../lib/activitypub/send' -import { createVideoChannel } from '../../../lib/video-channel' -import { - asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination, - videoChannelsAddValidator, videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator, - videoChannelsUpdateValidator -} from '../../../middlewares' -import { AccountModel } from '../../../models/account/account' -import { VideoChannelModel } from '../../../models/video/video-channel' - -const videoChannelRouter = express.Router() - -videoChannelRouter.get('/channels', - paginationValidator, - videoChannelsSortValidator, - setDefaultSort, - setDefaultPagination, - asyncMiddleware(listVideoChannels) -) - -videoChannelRouter.get('/accounts/:accountId/channels', - asyncMiddleware(listVideoAccountChannelsValidator), - asyncMiddleware(listVideoAccountChannels) -) - -videoChannelRouter.post('/channels', - authenticate, - videoChannelsAddValidator, - asyncMiddleware(addVideoChannelRetryWrapper) -) - -videoChannelRouter.put('/channels/:id', - authenticate, - asyncMiddleware(videoChannelsUpdateValidator), - updateVideoChannelRetryWrapper -) - -videoChannelRouter.delete('/channels/:id', - authenticate, - asyncMiddleware(videoChannelsRemoveValidator), - asyncMiddleware(removeVideoChannelRetryWrapper) -) - -videoChannelRouter.get('/channels/:id', - asyncMiddleware(videoChannelsGetValidator), - asyncMiddleware(getVideoChannel) -) - -// --------------------------------------------------------------------------- - -export { - videoChannelRouter -} - -// --------------------------------------------------------------------------- - -async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) { - const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort) - - return res.json(getFormattedObjects(resultList.data, resultList.total)) -} - -async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) { - const resultList = await VideoChannelModel.listByAccount(res.locals.account.id) - - return res.json(getFormattedObjects(resultList.data, resultList.total)) -} - -// Wrapper to video channel add that retry the async function if there is a database error -// We need this because we run the transaction in SERIALIZABLE isolation that can fail -async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot insert the video video channel with many retries.' - } - - const videoChannel = await retryTransactionWrapper(addVideoChannel, options) - return res.json({ - videoChannel: { - id: videoChannel.id - } - }).end() -} - -async function addVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInfo: VideoChannelCreate = req.body - const account: AccountModel = res.locals.oauth.token.User.Account - - const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { - return createVideoChannel(videoChannelInfo, account, t) - }) - - setAsyncActorKeys(videoChannelCreated.Actor) - .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) - - logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) - - return videoChannelCreated -} - -async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot update the video with many retries.' - } - - await retryTransactionWrapper(updateVideoChannel, options) - - return res.type('json').status(204).end() -} - -async function updateVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInstance = res.locals.videoChannel as VideoChannelModel - const videoChannelFieldsSave = videoChannelInstance.toJSON() - const videoChannelInfoToUpdate = req.body as VideoChannelUpdate - - try { - await sequelizeTypescript.transaction(async t => { - const sequelizeOptions = { - transaction: t - } - - if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) - if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) - if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) - - const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) - await sendUpdateActor(videoChannelInstanceUpdated, t) - }) - - logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) - } catch (err) { - logger.debug('Cannot update the video channel.', { err }) - - // Force fields we want to update - // If the transaction is retried, sequelize will think the object has not changed - // So it will skip the SQL request, even if the last one was ROLLBACKed! - resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) - - throw err - } -} - -async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot remove the video channel with many retries.' - } - - await retryTransactionWrapper(removeVideoChannel, options) - - return res.type('json').status(204).end() -} - -async function removeVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInstance: VideoChannelModel = res.locals.videoChannel - - return sequelizeTypescript.transaction(async t => { - await videoChannelInstance.destroy({ transaction: t }) - - logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) - }) - -} - -async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { - const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) - - return res.json(videoChannelWithVideos.toFormattedJSON()) -} diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 61b6c5826..4b3198a74 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -42,7 +42,6 @@ import { VideoModel } from '../../../models/video/video' import { VideoFileModel } from '../../../models/video/video-file' import { abuseVideoRouter } from './abuse' import { blacklistRouter } from './blacklist' -import { videoChannelRouter } from './channel' import { videoCommentRouter } from './comment' import { rateVideoRouter } from './rate' import { VideoFilter } from '../../../../shared/models/videos/video-query.type' @@ -72,7 +71,6 @@ const reqVideoFileUpdate = createReqFiles( videosRouter.use('/', abuseVideoRouter) videosRouter.use('/', blacklistRouter) videosRouter.use('/', rateVideoRouter) -videosRouter.use('/', videoChannelRouter) videosRouter.use('/', videoCommentRouter) videosRouter.get('/categories', listVideoCategories) @@ -397,13 +395,14 @@ async function getVideoDescription (req: express.Request, res: express.Response) } async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { - const resultList = await VideoModel.listForApi( - req.query.start as number, - req.query.count as number, - req.query.sort as VideoSortField, - isNSFWHidden(res), - req.query.filter as VideoFilter - ) + const resultList = await VideoModel.listForApi({ + start: req.query.start, + count: req.query.count, + sort: req.query.sort, + hideNSFW: isNSFWHidden(res), + filter: req.query.filter as VideoFilter, + withFiles: false + }) return res.json(getFormattedObjects(resultList.data, resultList.total)) } diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index 6a6af3e09..7dcaf7004 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts @@ -33,15 +33,15 @@ async function generateFeed (req: express.Request, res: express.Response, next: const account: AccountModel = res.locals.account const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list' - const resultList = await VideoModel.listForApi( + const resultList = await VideoModel.listForApi({ start, - FEEDS.COUNT, - req.query.sort as VideoSortField, + count: FEEDS.COUNT, + sort: req.query.sort, hideNSFW, - req.query.filter, - true, - account ? account.id : null - ) + filter: req.query.filter, + withFiles: true, + accountId: account ? account.id : null + }) // Adding video items to the feed, one at a time resultList.data.forEach(video => { -- cgit v1.2.3