Use async/await in controllers

This commit is contained in:
Chocobozzz 2017-10-25 11:55:06 +02:00
parent 5f04dd2f74
commit eb08047657
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
20 changed files with 823 additions and 992 deletions

View File

@ -2,30 +2,32 @@ import * as express from 'express'
import { isSignupAllowed } from '../../helpers'
import { CONFIG } from '../../initializers'
import { asyncMiddleware } from '../../middlewares'
import { ServerConfig } from '../../../shared'
const configRouter = express.Router()
configRouter.get('/', getConfig)
configRouter.get('/',
asyncMiddleware(getConfig)
)
function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
async function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
const allowed = await isSignupAllowed()
isSignupAllowed().then(allowed => {
const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS)
.filter(key => CONFIG.TRANSCODING.RESOLUTIONS[key] === true)
.map(r => parseInt(r, 10))
const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS)
.filter(key => CONFIG.TRANSCODING.RESOLUTIONS[key] === true)
.map(r => parseInt(r, 10))
const json: ServerConfig = {
signup: {
allowed
},
transcoding: {
enabledResolutions
}
const json: ServerConfig = {
signup: {
allowed
},
transcoding: {
enabledResolutions
}
}
res.json(json)
})
return res.json(json)
}
// ---------------------------------------------------------------------------

View File

@ -2,15 +2,18 @@ import * as express from 'express'
import { CONFIG } from '../../initializers'
import { logger } from '../../helpers'
import { asyncMiddleware } from '../../middlewares'
import { database as db } from '../../initializers/database'
import { OAuthClientLocal } from '../../../shared'
const oauthClientsRouter = express.Router()
oauthClientsRouter.get('/local', getLocalClient)
oauthClientsRouter.get('/local',
asyncMiddleware(getLocalClient)
)
// Get the client credentials for the PeerTube front end
function getLocalClient (req: express.Request, res: express.Response, next: express.NextFunction) {
async function getLocalClient (req: express.Request, res: express.Response, next: express.NextFunction) {
const serverHostname = CONFIG.WEBSERVER.HOSTNAME
const serverPort = CONFIG.WEBSERVER.PORT
let headerHostShouldBe = serverHostname
@ -24,17 +27,14 @@ function getLocalClient (req: express.Request, res: express.Response, next: expr
return res.type('json').status(403).end()
}
db.OAuthClient.loadFirstClient()
.then(client => {
if (!client) throw new Error('No client available.')
const client = await db.OAuthClient.loadFirstClient()
if (!client) throw new Error('No client available.')
const json: OAuthClientLocal = {
client_id: client.clientId,
client_secret: client.clientSecret
}
res.json(json)
})
.catch(err => next(err))
const json: OAuthClientLocal = {
client_id: client.clientId,
client_secret: client.clientSecret
}
return res.json(json)
}
// ---------------------------------------------------------------------------

View File

@ -16,7 +16,8 @@ import {
paginationValidator,
setPagination,
setPodsSort,
podsSortValidator
podsSortValidator,
asyncMiddleware
} from '../../middlewares'
import { PodInstance } from '../../models'
@ -27,25 +28,25 @@ podsRouter.get('/',
podsSortValidator,
setPodsSort,
setPagination,
listPods
asyncMiddleware(listPods)
)
podsRouter.post('/make-friends',
authenticate,
ensureIsAdmin,
makeFriendsValidator,
setBodyHostsPort,
makeFriendsController
asyncMiddleware(makeFriendsController)
)
podsRouter.get('/quit-friends',
authenticate,
ensureIsAdmin,
quitFriendsController
asyncMiddleware(quitFriendsController)
)
podsRouter.delete('/:id',
authenticate,
ensureIsAdmin,
podRemoveValidator,
removeFriendController
asyncMiddleware(removeFriendController)
)
// ---------------------------------------------------------------------------
@ -56,33 +57,33 @@ export {
// ---------------------------------------------------------------------------
function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Pod.listForApi(req.query.start, req.query.count, req.query.sort)
.then(resultList => res.json(getFormattedObjects(resultList.data, resultList.total)))
.catch(err => next(err))
async function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.Pod.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
async function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
const hosts = req.body.hosts as string[]
// Don't wait the process that could be long
makeFriends(hosts)
.then(() => logger.info('Made friends!'))
.catch(err => logger.error('Could not make friends.', err))
// Don't wait the process that could be long
res.type('json').status(204).end()
return res.type('json').status(204).end()
}
function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
quitFriends()
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
async function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
await quitFriends()
return res.type('json').status(204).end()
}
function removeFriendController (req: express.Request, res: express.Response, next: express.NextFunction) {
async function removeFriendController (req: express.Request, res: express.Response, next: express.NextFunction) {
const pod = res.locals.pod as PodInstance
removeFriend(pod)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await removeFriend(pod)
return res.type('json').status(204).end()
}

View File

@ -5,7 +5,8 @@ import {
checkSignature,
signatureValidator,
setBodyHostPort,
remotePodsAddValidator
remotePodsAddValidator,
asyncMiddleware
} from '../../../middlewares'
import { sendOwnedDataToPod } from '../../../lib'
import { getMyPublicCert, getFormattedObjects } from '../../../helpers'
@ -18,15 +19,17 @@ const remotePodsRouter = express.Router()
remotePodsRouter.post('/remove',
signatureValidator,
checkSignature,
removePods
asyncMiddleware(removePods)
)
remotePodsRouter.post('/list', remotePodsList)
remotePodsRouter.post('/list',
asyncMiddleware(remotePodsList)
)
remotePodsRouter.post('/add',
setBodyHostPort, // We need to modify the host before running the validator!
remotePodsAddValidator,
addPods
asyncMiddleware(addPods)
)
// ---------------------------------------------------------------------------
@ -37,35 +40,30 @@ export {
// ---------------------------------------------------------------------------
function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
async function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
const information = req.body
const pod = db.Pod.build(information)
pod.save()
.then(podCreated => {
return sendOwnedDataToPod(podCreated.id)
})
.then(() => {
return getMyPublicCert()
})
.then(cert => {
return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
})
.catch(err => next(err))
const podCreated = await pod.save()
await sendOwnedDataToPod(podCreated.id)
const cert = await getMyPublicCert()
return res.json({ cert, email: CONFIG.ADMIN.EMAIL })
}
function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Pod.list()
.then(podsList => res.json(getFormattedObjects<FormattedPod, PodInstance>(podsList, podsList.length)))
.catch(err => next(err))
async function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) {
const pods = await db.Pod.list()
return res.json(getFormattedObjects<FormattedPod, PodInstance>(pods, pods.length))
}
function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
async function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
const signature: PodSignature = req.body.signature
const host = signature.host
db.Pod.loadByHost(host)
.then(pod => pod.destroy())
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
const pod = await db.Pod.loadByHost(host)
await pod.destroy()
return res.type('json').status(204).end()
}

View File

@ -1,5 +1,5 @@
import * as express from 'express'
import * as Promise from 'bluebird'
import * as Bluebird from 'bluebird'
import * as Sequelize from 'sequelize'
import { database as db } from '../../../initializers/database'
@ -17,7 +17,7 @@ import {
remoteEventsVideosValidator
} from '../../../middlewares'
import { logger, retryTransactionWrapper } from '../../../helpers'
import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib'
import { PodInstance, VideoFileInstance } from '../../../models'
import {
RemoteVideoRequest,
@ -87,7 +87,7 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
const fromPod = res.locals.secure.pod
// We need to process in the same order to keep consistency
Promise.each(requests, request => {
Bluebird.each(requests, request => {
const data = request.data
// Get the function we need to call in order to process the request
@ -109,7 +109,7 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
const requests: RemoteQaduVideoRequest[] = req.body.data
const fromPod = res.locals.secure.pod
Promise.each(requests, request => {
Bluebird.each(requests, request => {
const videoData = request.data
return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
@ -123,7 +123,7 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
const requests: RemoteVideoEventRequest[] = req.body.data
const fromPod = res.locals.secure.pod
Promise.each(requests, request => {
Bluebird.each(requests, request => {
const eventData = request.data
return processVideosEventsRetryWrapper(eventData, fromPod)
@ -133,541 +133,447 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
return res.type('json').status(204).end()
}
function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
const options = {
arguments: [ eventData, fromPod ],
errorMessage: 'Cannot process videos events with many retries.'
}
return retryTransactionWrapper(processVideosEvents, options)
await retryTransactionWrapper(processVideosEvents, options)
}
function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
await db.sequelize.transaction(async t => {
const sequelizeOptions = { transaction: t }
const videoInstance = await fetchVideoByUUID(eventData.uuid, t)
return db.sequelize.transaction(t => {
return fetchVideoByUUID(eventData.uuid, t)
.then(videoInstance => {
const options = { transaction: t }
let columnToUpdate
let qaduType
let columnToUpdate
let qaduType
switch (eventData.eventType) {
case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
columnToUpdate = 'views'
qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
break
switch (eventData.eventType) {
case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
columnToUpdate = 'views'
qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
break
case REQUEST_VIDEO_EVENT_TYPES.LIKES:
columnToUpdate = 'likes'
qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
break
case REQUEST_VIDEO_EVENT_TYPES.LIKES:
columnToUpdate = 'likes'
qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
break
case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
columnToUpdate = 'dislikes'
qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
break
case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
columnToUpdate = 'dislikes'
qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
break
default:
throw new Error('Unknown video event type.')
}
default:
throw new Error('Unknown video event type.')
}
const query = {}
query[columnToUpdate] = eventData.count
const query = {}
query[columnToUpdate] = eventData.count
await videoInstance.increment(query, sequelizeOptions)
return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
})
.then(({ videoInstance, qaduType }) => {
const qadusParams = [
{
videoId: videoInstance.id,
type: qaduType
}
]
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
})
.then(() => logger.info('Remote video event processed for video with uuid %s.', eventData.uuid))
.catch(err => {
logger.debug('Cannot process a video event.', err)
throw err
const qadusParams = [
{
videoId: videoInstance.id,
type: qaduType
}
]
await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
logger.info('Remote video event processed for video with uuid %s.', eventData.uuid)
}
function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
const options = {
arguments: [ videoData, fromPod ],
errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
}
return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
await retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
}
function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
let videoUUID = ''
return db.sequelize.transaction(t => {
return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
.then(videoInstance => {
const options = { transaction: t }
await db.sequelize.transaction(async t => {
const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
const sequelizeOptions = { transaction: t }
videoUUID = videoInstance.uuid
videoUUID = videoInstance.uuid
if (videoData.views) {
videoInstance.set('views', videoData.views)
}
if (videoData.views) {
videoInstance.set('views', videoData.views)
}
if (videoData.likes) {
videoInstance.set('likes', videoData.likes)
}
if (videoData.likes) {
videoInstance.set('likes', videoData.likes)
}
if (videoData.dislikes) {
videoInstance.set('dislikes', videoData.dislikes)
}
if (videoData.dislikes) {
videoInstance.set('dislikes', videoData.dislikes)
}
return videoInstance.save(options)
})
await videoInstance.save(sequelizeOptions)
})
.then(() => logger.info('Remote video with uuid %s quick and dirty updated', videoUUID))
.catch(err => logger.debug('Cannot quick and dirty update the remote video.', err))
logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)
}
// Handle retries on fail
function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
const options = {
arguments: [ videoToCreateData, fromPod ],
errorMessage: 'Cannot insert the remote video with many retries.'
}
return retryTransactionWrapper(addRemoteVideo, options)
await retryTransactionWrapper(addRemoteVideo, options)
}
function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
return db.sequelize.transaction(t => {
return db.Video.loadByUUID(videoToCreateData.uuid)
.then(video => {
if (video) throw new Error('UUID already exists.')
await db.sequelize.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
return db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
const videoFromDatabase = await db.Video.loadByUUID(videoToCreateData.uuid)
if (videoFromDatabase) throw new Error('UUID already exists.')
const videoChannel = await db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
const tags = videoToCreateData.tags
const tagInstances = await db.Tag.findOrCreateTags(tags, t)
const videoData = {
name: videoToCreateData.name,
uuid: videoToCreateData.uuid,
category: videoToCreateData.category,
licence: videoToCreateData.licence,
language: videoToCreateData.language,
nsfw: videoToCreateData.nsfw,
description: videoToCreateData.description,
channelId: videoChannel.id,
duration: videoToCreateData.duration,
createdAt: videoToCreateData.createdAt,
// FIXME: updatedAt does not seems to be considered by Sequelize
updatedAt: videoToCreateData.updatedAt,
views: videoToCreateData.views,
likes: videoToCreateData.likes,
dislikes: videoToCreateData.dislikes,
remote: true
}
const video = db.Video.build(videoData)
await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData)
const videoCreated = await video.save(sequelizeOptions)
const tasks = []
for (const fileData of videoToCreateData.files) {
const videoFileInstance = db.VideoFile.build({
extname: fileData.extname,
infoHash: fileData.infoHash,
resolution: fileData.resolution,
size: fileData.size,
videoId: videoCreated.id
})
.then(videoChannel => {
if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
const tags = videoToCreateData.tags
tasks.push(videoFileInstance.save(sequelizeOptions))
}
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoChannel, tagInstances }))
})
.then(({ videoChannel, tagInstances }) => {
const videoData = {
name: videoToCreateData.name,
uuid: videoToCreateData.uuid,
category: videoToCreateData.category,
licence: videoToCreateData.licence,
language: videoToCreateData.language,
nsfw: videoToCreateData.nsfw,
description: videoToCreateData.description,
channelId: videoChannel.id,
duration: videoToCreateData.duration,
createdAt: videoToCreateData.createdAt,
// FIXME: updatedAt does not seems to be considered by Sequelize
updatedAt: videoToCreateData.updatedAt,
views: videoToCreateData.views,
likes: videoToCreateData.likes,
dislikes: videoToCreateData.dislikes,
remote: true
}
await Promise.all(tasks)
const video = db.Video.build(videoData)
return { tagInstances, video }
})
.then(({ tagInstances, video }) => {
return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
})
.then(({ tagInstances, video }) => {
const options = {
transaction: t
}
return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
})
.then(({ tagInstances, videoCreated }) => {
const tasks = []
const options = {
transaction: t
}
videoToCreateData.files.forEach(fileData => {
const videoFileInstance = db.VideoFile.build({
extname: fileData.extname,
infoHash: fileData.infoHash,
resolution: fileData.resolution,
size: fileData.size,
videoId: videoCreated.id
})
tasks.push(videoFileInstance.save(options))
})
return Promise.all(tasks).then(() => ({ tagInstances, videoCreated }))
})
.then(({ tagInstances, videoCreated }) => {
const options = {
transaction: t
}
return videoCreated.setTags(tagInstances, options)
})
})
.then(() => logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid))
.catch(err => {
logger.debug('Cannot insert the remote video.', err)
throw err
await videoCreated.setTags(tagInstances, sequelizeOptions)
})
logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
}
// Handle retries on fail
function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
const options = {
arguments: [ videoAttributesToUpdate, fromPod ],
errorMessage: 'Cannot update the remote video with many retries'
}
return retryTransactionWrapper(updateRemoteVideo, options)
await retryTransactionWrapper(updateRemoteVideo, options)
}
function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
return db.sequelize.transaction(t => {
return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
.then(videoInstance => {
const tags = videoAttributesToUpdate.tags
try {
await db.sequelize.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
})
.then(({ videoInstance, tagInstances }) => {
const options = { transaction: t }
const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
const tags = videoAttributesToUpdate.tags
videoInstance.set('name', videoAttributesToUpdate.name)
videoInstance.set('category', videoAttributesToUpdate.category)
videoInstance.set('licence', videoAttributesToUpdate.licence)
videoInstance.set('language', videoAttributesToUpdate.language)
videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
videoInstance.set('description', videoAttributesToUpdate.description)
videoInstance.set('duration', videoAttributesToUpdate.duration)
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
videoInstance.set('views', videoAttributesToUpdate.views)
videoInstance.set('likes', videoAttributesToUpdate.likes)
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
const tagInstances = await db.Tag.findOrCreateTags(tags, t)
return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
})
.then(({ tagInstances, videoInstance }) => {
const tasks: Promise<void>[] = []
videoInstance.set('name', videoAttributesToUpdate.name)
videoInstance.set('category', videoAttributesToUpdate.category)
videoInstance.set('licence', videoAttributesToUpdate.licence)
videoInstance.set('language', videoAttributesToUpdate.language)
videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
videoInstance.set('description', videoAttributesToUpdate.description)
videoInstance.set('duration', videoAttributesToUpdate.duration)
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
videoInstance.set('views', videoAttributesToUpdate.views)
videoInstance.set('likes', videoAttributesToUpdate.likes)
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
// Remove old video files
videoInstance.VideoFiles.forEach(videoFile => {
tasks.push(videoFile.destroy({ transaction: t }))
await videoInstance.save(sequelizeOptions)
// Remove old video files
const videoFileDestroyTasks: Bluebird<void>[] = []
for (const videoFile of videoInstance.VideoFiles) {
videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
}
await Promise.all(videoFileDestroyTasks)
const videoFileCreateTasks: Bluebird<VideoFileInstance>[] = []
for (const fileData of videoAttributesToUpdate.files) {
const videoFileInstance = db.VideoFile.build({
extname: fileData.extname,
infoHash: fileData.infoHash,
resolution: fileData.resolution,
size: fileData.size,
videoId: videoInstance.id
})
return Promise.all(tasks).then(() => ({ tagInstances, videoInstance }))
})
.then(({ tagInstances, videoInstance }) => {
const tasks: Promise<VideoFileInstance>[] = []
const options = {
transaction: t
}
videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions))
}
videoAttributesToUpdate.files.forEach(fileData => {
const videoFileInstance = db.VideoFile.build({
extname: fileData.extname,
infoHash: fileData.infoHash,
resolution: fileData.resolution,
size: fileData.size,
videoId: videoInstance.id
})
await Promise.all(videoFileCreateTasks)
tasks.push(videoFileInstance.save(options))
})
await videoInstance.setTags(tagInstances, sequelizeOptions)
})
return Promise.all(tasks).then(() => ({ tagInstances, videoInstance }))
})
.then(({ videoInstance, tagInstances }) => {
const options = { transaction: t }
return videoInstance.setTags(tagInstances, options)
})
})
.then(() => logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid))
.catch(err => {
logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
} catch (err) {
// This is just a debug because we will retry the insert
logger.debug('Cannot update the remote video.', err)
throw err
})
}
}
function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
const options = {
arguments: [ videoToRemoveData, fromPod ],
errorMessage: 'Cannot remove the remote video channel with many retries.'
}
return retryTransactionWrapper(removeRemoteVideo, options)
await retryTransactionWrapper(removeRemoteVideo, options)
}
function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)
return db.sequelize.transaction(t => {
await db.sequelize.transaction(async t => {
// We need the instance because we have to remove some other stuffs (thumbnail etc)
return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
.then(video => video.destroy({ transaction: t }))
})
.then(() => logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid))
.catch(err => {
logger.debug('Cannot remove the remote video.', err)
throw err
const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
await videoInstance.destroy({ transaction: t })
})
logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
}
function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
const options = {
arguments: [ authorToCreateData, fromPod ],
errorMessage: 'Cannot insert the remote video author with many retries.'
}
return retryTransactionWrapper(addRemoteVideoAuthor, options)
await retryTransactionWrapper(addRemoteVideoAuthor, options)
}
function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
logger.debug('Adding remote video author "%s".', authorToCreateData.uuid)
return db.sequelize.transaction(t => {
return db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
.then(author => {
if (author) throw new Error('UUID already exists.')
await db.sequelize.transaction(async t => {
const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.')
return undefined
})
.then(() => {
const videoAuthorData = {
name: authorToCreateData.name,
uuid: authorToCreateData.uuid,
userId: null, // Not on our pod
podId: fromPod.id
}
const videoAuthorData = {
name: authorToCreateData.name,
uuid: authorToCreateData.uuid,
userId: null, // Not on our pod
podId: fromPod.id
}
const author = db.Author.build(videoAuthorData)
return author.save({ transaction: t })
})
const author = db.Author.build(videoAuthorData)
await author.save({ transaction: t })
})
.then(() => logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid))
.catch(err => {
logger.debug('Cannot insert the remote video author.', err)
throw err
})
logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid)
}
function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
const options = {
arguments: [ authorAttributesToRemove, fromPod ],
errorMessage: 'Cannot remove the remote video author with many retries.'
}
return retryTransactionWrapper(removeRemoteVideoAuthor, options)
await retryTransactionWrapper(removeRemoteVideoAuthor, options)
}
function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)
return db.sequelize.transaction(t => {
return db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
.then(videoAuthor => videoAuthor.destroy({ transaction: t }))
})
.then(() => logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid))
.catch(err => {
logger.debug('Cannot remove the remote video author.', err)
throw err
await db.sequelize.transaction(async t => {
const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
await videoAuthor.destroy({ transaction: t })
})
logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid)
}
function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
const options = {
arguments: [ videoChannelToCreateData, fromPod ],
errorMessage: 'Cannot insert the remote video channel with many retries.'
}
return retryTransactionWrapper(addRemoteVideoChannel, options)
await retryTransactionWrapper(addRemoteVideoChannel, options)
}
function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
return db.sequelize.transaction(t => {
return db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
.then(videoChannel => {
if (videoChannel) throw new Error('UUID already exists.')
await db.sequelize.transaction(async t => {
const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
if (videoChannelInDatabase) {
throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.')
}
return undefined
})
.then(() => {
const authorUUID = videoChannelToCreateData.ownerUUID
const podId = fromPod.id
const authorUUID = videoChannelToCreateData.ownerUUID
const podId = fromPod.id
return db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
})
.then(author => {
if (!author) throw new Error('Unknown author UUID.')
const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
if (!author) throw new Error('Unknown author UUID' + authorUUID + '.')
const videoChannelData = {
name: videoChannelToCreateData.name,
description: videoChannelToCreateData.description,
uuid: videoChannelToCreateData.uuid,
createdAt: videoChannelToCreateData.createdAt,
updatedAt: videoChannelToCreateData.updatedAt,
remote: true,
authorId: author.id
}
const videoChannelData = {
name: videoChannelToCreateData.name,
description: videoChannelToCreateData.description,
uuid: videoChannelToCreateData.uuid,
createdAt: videoChannelToCreateData.createdAt,
updatedAt: videoChannelToCreateData.updatedAt,
remote: true,
authorId: author.id
}
const videoChannel = db.VideoChannel.build(videoChannelData)
return videoChannel.save({ transaction: t })
})
})
.then(() => logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid))
.catch(err => {
logger.debug('Cannot insert the remote video channel.', err)
throw err
const videoChannel = db.VideoChannel.build(videoChannelData)
await videoChannel.save({ transaction: t })
})
logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
}
function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
const options = {
arguments: [ videoChannelAttributesToUpdate, fromPod ],
errorMessage: 'Cannot update the remote video channel with many retries.'
}
return retryTransactionWrapper(updateRemoteVideoChannel, options)
await retryTransactionWrapper(updateRemoteVideoChannel, options)
}
function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid)
return db.sequelize.transaction(t => {
return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
.then(videoChannelInstance => {
const options = { transaction: t }
await db.sequelize.transaction(async t => {
const sequelizeOptions = { transaction: t }
videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)
const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)
return videoChannelInstance.save(options)
})
})
.then(() => logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid))
.catch(err => {
// This is just a debug because we will retry the insert
logger.debug('Cannot update the remote video channel.', err)
throw err
await videoChannelInstance.save(sequelizeOptions)
})
logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid)
}
function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
const options = {
arguments: [ videoChannelAttributesToRemove, fromPod ],
errorMessage: 'Cannot remove the remote video channel with many retries.'
}
return retryTransactionWrapper(removeRemoteVideoChannel, options)
await retryTransactionWrapper(removeRemoteVideoChannel, options)
}
function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid)
return db.sequelize.transaction(t => {
return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
.then(videoChannel => videoChannel.destroy({ transaction: t }))
})
.then(() => logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid))
.catch(err => {
logger.debug('Cannot remove the remote video channel.', err)
throw err
await db.sequelize.transaction(async t => {
const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
await videoChannel.destroy({ transaction: t })
})
logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid)
}
function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
const options = {
arguments: [ reportData, fromPod ],
errorMessage: 'Cannot create remote abuse video with many retries.'
}
return retryTransactionWrapper(reportAbuseRemoteVideo, options)
await retryTransactionWrapper(reportAbuseRemoteVideo, options)
}
function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
return db.sequelize.transaction(t => {
return fetchVideoByUUID(reportData.videoUUID, t)
.then(video => {
const videoAbuseData = {
reporterUsername: reportData.reporterUsername,
reason: reportData.reportReason,
reporterPodId: fromPod.id,
videoId: video.id
}
await db.sequelize.transaction(async t => {
const videoInstance = await fetchVideoByUUID(reportData.videoUUID, t)
const videoAbuseData = {
reporterUsername: reportData.reporterUsername,
reason: reportData.reportReason,
reporterPodId: fromPod.id,
videoId: videoInstance.id
}
await db.VideoAbuse.create(videoAbuseData)
return db.VideoAbuse.create(videoAbuseData)
})
})
.then(() => logger.info('Remote abuse for video uuid %s created', reportData.videoUUID))
.catch(err => {
// This is just a debug because we will retry the insert
logger.debug('Cannot create remote abuse video', err)
logger.info('Remote abuse for video uuid %s created', reportData.videoUUID)
}
async function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
try {
const video = await db.Video.loadByUUID(id, t)
if (!video) throw new Error('Video ' + id + ' not found')
return video
} catch (err) {
logger.error('Cannot load owned video from id.', { error: err.stack, id })
throw err
})
}
}
function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
return db.Video.loadByUUID(id, t)
.then(video => {
if (!video) throw new Error('Video not found')
async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
try {
const video = await db.Video.loadByHostAndUUID(podHost, uuid, t)
if (!video) throw new Error('Video not found')
return video
})
.catch(err => {
logger.error('Cannot load owned video from id.', { error: err.stack, id })
throw err
})
}
function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
return db.Video.loadByHostAndUUID(podHost, uuid, t)
.then(video => {
if (!video) throw new Error('Video not found')
return video
})
.catch(err => {
logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
throw err
})
}
function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
return db.VideoChannel.loadByHostAndUUID(podHost, uuid, t)
.then(videoChannel => {
if (!videoChannel) throw new Error('Video channel not found')
return videoChannel
})
.catch(err => {
logger.error('Cannot load video channel from host and uuid.', { error: err.stack, podHost, uuid })
throw err
})
return video
} catch (err) {
logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
throw err
}
}

View File

@ -1,5 +1,5 @@
import * as express from 'express'
import * as Promise from 'bluebird'
import * as Bluebird from 'bluebird'
import {
AbstractRequestScheduler,
@ -7,7 +7,7 @@ import {
getRequestVideoQaduScheduler,
getRequestVideoEventScheduler
} from '../../lib'
import { authenticate, ensureIsAdmin } from '../../middlewares'
import { authenticate, ensureIsAdmin, asyncMiddleware } from '../../middlewares'
import { RequestSchedulerStatsAttributes } from '../../../shared'
const requestSchedulerRouter = express.Router()
@ -15,7 +15,7 @@ const requestSchedulerRouter = express.Router()
requestSchedulerRouter.get('/stats',
authenticate,
ensureIsAdmin,
getRequestSchedulersStats
asyncMiddleware(getRequestSchedulersStats)
)
// ---------------------------------------------------------------------------
@ -26,28 +26,28 @@ export {
// ---------------------------------------------------------------------------
function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
Promise.props({
async function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
const result = await Bluebird.props({
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
})
.then(result => res.json(result))
.catch(err => next(err))
return res.json(result)
}
// ---------------------------------------------------------------------------
function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
return requestScheduler.remainingRequestsCount().then(count => {
const result: RequestSchedulerStatsAttributes = {
totalRequests: count,
requestsLimitPods: requestScheduler.limitPods,
requestsLimitPerPod: requestScheduler.limitPerPod,
remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
milliSecondsInterval: requestScheduler.requestInterval
}
async function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
const count = await requestScheduler.remainingRequestsCount()
return result
})
const result: RequestSchedulerStatsAttributes = {
totalRequests: count,
requestsLimitPods: requestScheduler.limitPods,
requestsLimitPerPod: requestScheduler.limitPerPod,
remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
milliSecondsInterval: requestScheduler.requestInterval
}
return result
}

View File

@ -18,7 +18,8 @@ import {
setPagination,
usersSortValidator,
setUsersSort,
token
token,
asyncMiddleware
} from '../../middlewares'
import {
UserVideoRate as FormattedUserVideoRate,
@ -33,13 +34,13 @@ const usersRouter = express.Router()
usersRouter.get('/me',
authenticate,
getUserInformation
asyncMiddleware(getUserInformation)
)
usersRouter.get('/me/videos/:videoId/rating',
authenticate,
usersVideoRatingValidator,
getUserVideoRating
asyncMiddleware(getUserVideoRating)
)
usersRouter.get('/',
@ -47,7 +48,7 @@ usersRouter.get('/',
usersSortValidator,
setUsersSort,
setPagination,
listUsers
asyncMiddleware(listUsers)
)
usersRouter.get('/:id',
@ -65,27 +66,27 @@ usersRouter.post('/',
usersRouter.post('/register',
ensureUserRegistrationAllowed,
usersRegisterValidator,
registerUser
asyncMiddleware(registerUser)
)
usersRouter.put('/me',
authenticate,
usersUpdateMeValidator,
updateMe
asyncMiddleware(updateMe)
)
usersRouter.put('/:id',
authenticate,
ensureIsAdmin,
usersUpdateValidator,
updateUser
asyncMiddleware(updateUser)
)
usersRouter.delete('/:id',
authenticate,
ensureIsAdmin,
usersRemoveValidator,
removeUser
asyncMiddleware(removeUser)
)
usersRouter.post('/token', token, success)
@ -99,21 +100,19 @@ export {
// ---------------------------------------------------------------------------
function createUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function createUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot insert the user with many retries.'
}
retryTransactionWrapper(createUser, options)
.then(() => {
// TODO : include Location of the new user -> 201
res.type('json').status(204).end()
})
.catch(err => next(err))
await retryTransactionWrapper(createUser, options)
// TODO : include Location of the new user -> 201
return res.type('json').status(204).end()
}
function createUser (req: express.Request, res: express.Response, next: express.NextFunction) {
async function createUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserCreate = req.body
const user = db.User.build({
username: body.username,
@ -124,15 +123,12 @@ function createUser (req: express.Request, res: express.Response, next: express.
videoQuota: body.videoQuota
})
return createUserAuthorAndChannel(user)
.then(() => logger.info('User %s with its channel and author created.', body.username))
.catch((err: Error) => {
logger.debug('Cannot insert the user.', err)
throw err
})
await createUserAuthorAndChannel(user)
logger.info('User %s with its channel and author created.', body.username)
}
function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) {
async function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserCreate = req.body
const user = db.User.build({
@ -144,22 +140,21 @@ function registerUser (req: express.Request, res: express.Response, next: expres
videoQuota: CONFIG.USER.VIDEO_QUOTA
})
return createUserAuthorAndChannel(user)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await createUserAuthorAndChannel(user)
return res.type('json').status(204).end()
}
function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
.then(user => res.json(user.toFormattedJSON()))
.catch(err => next(err))
async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
const user = await db.User.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
return res.json(user.toFormattedJSON())
}
function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
return res.json(res.locals.user.toFormattedJSON())
}
function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoId = +req.params.videoId
const userId = +res.locals.oauth.token.User.id
@ -175,50 +170,45 @@ function getUserVideoRating (req: express.Request, res: express.Response, next:
.catch(err => next(err))
}
function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.listForApi(req.query.start, req.query.count, req.query.sort)
.then(resultList => {
res.json(getFormattedObjects(resultList.data, resultList.total))
})
.catch(err => next(err))
async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.User.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.loadById(req.params.id)
.then(user => user.destroy())
.then(() => res.sendStatus(204))
.catch(err => {
logger.error('Errors when removed the user.', err)
return next(err)
})
async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const user = await db.User.loadById(req.params.id)
await user.destroy()
return res.sendStatus(204)
}
function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) {
async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserUpdateMe = req.body
// FIXME: user is not already a Sequelize instance?
db.User.loadByUsername(res.locals.oauth.token.user.username)
.then(user => {
if (body.password !== undefined) user.password = body.password
if (body.email !== undefined) user.email = body.email
if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
const user = res.locals.oauth.token.user
return user.save()
})
.then(() => res.sendStatus(204))
.catch(err => next(err))
if (body.password !== undefined) user.password = body.password
if (body.email !== undefined) user.email = body.email
if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
await user.save()
return await res.sendStatus(204)
}
function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserUpdate = req.body
const user: UserInstance = res.locals.user
if (body.email !== undefined) user.email = body.email
if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota
return user.save()
.then(() => res.sendStatus(204))
.catch(err => next(err))
await user.save()
return res.sendStatus(204)
}
function success (req: express.Request, res: express.Response, next: express.NextFunction) {

View File

@ -14,7 +14,8 @@ import {
videoAbuseReportValidator,
videoAbusesSortValidator,
setVideoAbusesSort,
setPagination
setPagination,
asyncMiddleware
} from '../../../middlewares'
import { VideoInstance } from '../../../models'
import { VideoAbuseCreate } from '../../../../shared'
@ -28,12 +29,12 @@ abuseVideoRouter.get('/abuse',
videoAbusesSortValidator,
setVideoAbusesSort,
setPagination,
listVideoAbuses
asyncMiddleware(listVideoAbuses)
)
abuseVideoRouter.post('/:id/abuse',
authenticate,
videoAbuseReportValidator,
reportVideoAbuseRetryWrapper
asyncMiddleware(reportVideoAbuseRetryWrapper)
)
// ---------------------------------------------------------------------------
@ -44,55 +45,48 @@ export {
// ---------------------------------------------------------------------------
function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormattedObjects(result.data, result.total)))
.catch(err => next(err))
async function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot report abuse to the video with many retries.'
}
retryTransactionWrapper(reportVideoAbuse, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await retryTransactionWrapper(reportVideoAbuse, options)
return res.type('json').status(204).end()
}
function reportVideoAbuse (req: express.Request, res: express.Response) {
async function reportVideoAbuse (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video as VideoInstance
const reporterUsername = res.locals.oauth.token.User.username
const body: VideoAbuseCreate = req.body
const abuse = {
const abuseToCreate = {
reporterUsername,
reason: body.reason,
videoId: videoInstance.id,
reporterPodId: null // This is our pod that reported this abuse
}
return db.sequelize.transaction(t => {
return db.VideoAbuse.create(abuse, { transaction: t })
.then(abuse => {
// We send the information to the destination pod
if (videoInstance.isOwned() === false) {
const reportData = {
reporterUsername,
reportReason: abuse.reason,
videoUUID: videoInstance.uuid
}
await db.sequelize.transaction(async t => {
const abuse = await db.VideoAbuse.create(abuseToCreate, { transaction: t })
// We send the information to the destination pod
if (videoInstance.isOwned() === false) {
const reportData = {
reporterUsername,
reportReason: abuse.reason,
videoUUID: videoInstance.uuid
}
return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
}
await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
}
})
return videoInstance
})
})
.then((videoInstance: VideoInstance) => logger.info('Abuse report for video %s created.', videoInstance.name))
.catch(err => {
logger.debug('Cannot update the video.', err)
throw err
})
logger.info('Abuse report for video %s created.', videoInstance.name)
}

View File

@ -10,7 +10,8 @@ import {
paginationValidator,
blacklistSortValidator,
setBlacklistSort,
setPagination
setPagination,
asyncMiddleware
} from '../../../middlewares'
import { BlacklistedVideoInstance } from '../../../models'
import { BlacklistedVideo } from '../../../../shared'
@ -21,7 +22,7 @@ blacklistRouter.post('/:videoId/blacklist',
authenticate,
ensureIsAdmin,
videosBlacklistAddValidator,
addVideoToBlacklist
asyncMiddleware(addVideoToBlacklist)
)
blacklistRouter.get('/blacklist',
@ -31,14 +32,14 @@ blacklistRouter.get('/blacklist',
blacklistSortValidator,
setBlacklistSort,
setPagination,
listBlacklist
asyncMiddleware(listBlacklist)
)
blacklistRouter.delete('/:videoId/blacklist',
authenticate,
ensureIsAdmin,
videosBlacklistRemoveValidator,
removeVideoFromBlacklistController
asyncMiddleware(removeVideoFromBlacklistController)
)
// ---------------------------------------------------------------------------
@ -49,37 +50,34 @@ export {
// ---------------------------------------------------------------------------
function addVideoToBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
async function addVideoToBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoInstance = res.locals.video
const toCreate = {
videoId: videoInstance.id
}
db.BlacklistedVideo.create(toCreate)
.then(() => res.type('json').status(204).end())
.catch(err => {
logger.error('Errors when blacklisting video ', err)
return next(err)
})
await db.BlacklistedVideo.create(toCreate)
return res.type('json').status(204).end()
}
function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
db.BlacklistedVideo.listForApi(req.query.start, req.query.count, req.query.sort)
.then(resultList => res.json(getFormattedObjects<BlacklistedVideo, BlacklistedVideoInstance>(resultList.data, resultList.total)))
.catch(err => next(err))
async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.BlacklistedVideo.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects<BlacklistedVideo, BlacklistedVideoInstance>(resultList.data, resultList.total))
}
function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
const blacklistedVideo = res.locals.blacklistedVideo as BlacklistedVideoInstance
blacklistedVideo.destroy()
.then(() => {
logger.info('Video %s removed from blacklist.', res.locals.video.uuid)
res.sendStatus(204)
})
.catch(err => {
logger.error('Some error while removing video %s from blacklist.', res.locals.video.uuid, err)
next(err)
})
try {
await blacklistedVideo.destroy()
logger.info('Video %s removed from blacklist.', res.locals.video.uuid)
return res.sendStatus(204)
} catch (err) {
logger.error('Some error while removing video %s from blacklist.', res.locals.video.uuid, err)
throw err
}
}

View File

@ -4,7 +4,8 @@ import { database as db } from '../../../initializers'
import {
logger,
getFormattedObjects,
retryTransactionWrapper
retryTransactionWrapper,
resetSequelizeInstance
} from '../../../helpers'
import {
authenticate,
@ -16,7 +17,8 @@ import {
videoChannelsRemoveValidator,
videoChannelGetValidator,
videoChannelsUpdateValidator,
listVideoAuthorChannelsValidator
listVideoAuthorChannelsValidator,
asyncMiddleware
} from '../../../middlewares'
import {
createVideoChannel,
@ -32,18 +34,18 @@ videoChannelRouter.get('/channels',
videoChannelsSortValidator,
setVideoChannelsSort,
setPagination,
listVideoChannels
asyncMiddleware(listVideoChannels)
)
videoChannelRouter.get('/authors/:authorId/channels',
listVideoAuthorChannelsValidator,
listVideoAuthorChannels
asyncMiddleware(listVideoAuthorChannels)
)
videoChannelRouter.post('/channels',
authenticate,
videoChannelsAddValidator,
addVideoChannelRetryWrapper
asyncMiddleware(addVideoChannelRetryWrapper)
)
videoChannelRouter.put('/channels/:id',
@ -55,12 +57,12 @@ videoChannelRouter.put('/channels/:id',
videoChannelRouter.delete('/channels/:id',
authenticate,
videoChannelsRemoveValidator,
removeVideoChannelRetryWrapper
asyncMiddleware(removeVideoChannelRetryWrapper)
)
videoChannelRouter.get('/channels/:id',
videoChannelGetValidator,
getVideoChannel
asyncMiddleware(getVideoChannel)
)
// ---------------------------------------------------------------------------
@ -71,126 +73,113 @@ export {
// ---------------------------------------------------------------------------
function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
db.VideoChannel.listForApi(req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormattedObjects(result.data, result.total)))
.catch(err => next(err))
async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.VideoChannel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
function listVideoAuthorChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
db.VideoChannel.listByAuthor(res.locals.author.id)
.then(result => res.json(getFormattedObjects(result.data, result.total)))
.catch(err => next(err))
async function listVideoAuthorChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.VideoChannel.listByAuthor(res.locals.author.id)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
// Wrapper to video channel add that retry the function if there is a database error
// 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
function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
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.'
}
retryTransactionWrapper(addVideoChannel, options)
.then(() => {
// TODO : include Location of the new video channel -> 201
res.type('json').status(204).end()
})
.catch(err => next(err))
await retryTransactionWrapper(addVideoChannel, options)
// TODO : include Location of the new video channel -> 201
return res.type('json').status(204).end()
}
function addVideoChannel (req: express.Request, res: express.Response) {
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
const author: AuthorInstance = res.locals.oauth.token.User.Author
let videoChannelCreated: VideoChannelInstance
return db.sequelize.transaction(t => {
return createVideoChannel(videoChannelInfo, author, t)
})
.then(videoChannelUUID => logger.info('Video channel with uuid %s created.', videoChannelUUID))
.catch((err: Error) => {
logger.debug('Cannot insert the video channel.', err)
throw err
await db.sequelize.transaction(async t => {
videoChannelCreated = await createVideoChannel(videoChannelInfo, author, t)
})
logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid)
}
function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
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.'
}
retryTransactionWrapper(updateVideoChannel, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await retryTransactionWrapper(updateVideoChannel, options)
return res.type('json').status(204).end()
}
function updateVideoChannel (req: express.Request, res: express.Response) {
async function updateVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
const videoChannelFieldsSave = videoChannelInstance.toJSON()
const videoChannelInfoToUpdate: VideoChannelUpdate = req.body
return db.sequelize.transaction(t => {
const options = {
transaction: t
}
try {
await db.sequelize.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.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
return videoChannelInstance.save(options)
.then(() => {
const json = videoChannelInstance.toUpdateRemoteJSON()
await videoChannelInstance.save(sequelizeOptions)
const json = videoChannelInstance.toUpdateRemoteJSON()
// Now we'll update the video channel's meta data to our friends
return updateVideoChannelToFriends(json, t)
// Now we'll update the video channel's meta data to our friends
return updateVideoChannelToFriends(json, t)
})
})
.then(() => {
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.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!
Object.keys(videoChannelFieldsSave).forEach(key => {
const value = videoChannelFieldsSave[key]
videoChannelInstance.set(key, value)
})
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
} catch (err) {
logger.debug('Cannot update the video channel.', err)
throw 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
}
}
function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
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.'
}
retryTransactionWrapper(removeVideoChannel, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await retryTransactionWrapper(removeVideoChannel, options)
return res.type('json').status(204).end()
}
function removeVideoChannel (req: express.Request, res: express.Response) {
async function removeVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
return db.sequelize.transaction(t => {
return videoChannelInstance.destroy({ transaction: t })
})
.then(() => {
logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.uuid)
})
.catch(err => {
logger.error('Errors when removed the video channel.', err)
throw err
await db.sequelize.transaction(async t => {
await videoChannelInstance.destroy({ transaction: t })
})
logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.uuid)
}
function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
db.VideoChannel.loadAndPopulateAuthorAndVideos(res.locals.videoChannel.id)
.then(videoChannelWithVideos => res.json(videoChannelWithVideos.toFormattedJSON()))
.catch(err => next(err))
async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAuthorAndVideos(res.locals.videoChannel.id)
return res.json(videoChannelWithVideos.toFormattedJSON())
}

View File

@ -1,5 +1,4 @@
import * as express from 'express'
import * as Promise from 'bluebird'
import * as multer from 'multer'
import { extname, join } from 'path'
@ -30,7 +29,8 @@ import {
videosSearchValidator,
videosAddValidator,
videosGetValidator,
videosRemoveValidator
videosRemoveValidator,
asyncMiddleware
} from '../../../middlewares'
import {
logger,
@ -38,7 +38,8 @@ import {
generateRandomString,
getFormattedObjects,
renamePromise,
getVideoFileHeight
getVideoFileHeight,
resetSequelizeInstance
} from '../../../helpers'
import { TagInstance, VideoInstance } from '../../../models'
import { VideoCreate, VideoUpdate } from '../../../../shared'
@ -88,18 +89,18 @@ videosRouter.get('/',
videosSortValidator,
setVideosSort,
setPagination,
listVideos
asyncMiddleware(listVideos)
)
videosRouter.put('/:id',
authenticate,
videosUpdateValidator,
updateVideoRetryWrapper
asyncMiddleware(updateVideoRetryWrapper)
)
videosRouter.post('/upload',
authenticate,
reqFiles,
videosAddValidator,
addVideoRetryWrapper
asyncMiddleware(addVideoRetryWrapper)
)
videosRouter.get('/:id',
videosGetValidator,
@ -109,7 +110,7 @@ videosRouter.get('/:id',
videosRouter.delete('/:id',
authenticate,
videosRemoveValidator,
removeVideoRetryWrapper
asyncMiddleware(removeVideoRetryWrapper)
)
videosRouter.get('/search/:value',
@ -119,7 +120,7 @@ videosRouter.get('/search/:value',
setVideosSort,
setPagination,
setVideosSearch,
searchVideos
asyncMiddleware(searchVideos)
)
// ---------------------------------------------------------------------------
@ -144,220 +145,157 @@ function listVideoLanguages (req: express.Request, res: express.Response) {
// Wrapper to video add that retry the function if there is a database error
// We need this because we run the transaction in SERIALIZABLE isolation that can fail
function addVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function addVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res, req.files['videofile'][0] ],
errorMessage: 'Cannot insert the video with many retries.'
}
retryTransactionWrapper(addVideo, options)
.then(() => {
// TODO : include Location of the new video -> 201
res.type('json').status(204).end()
})
.catch(err => next(err))
await retryTransactionWrapper(addVideo, options)
// TODO : include Location of the new video -> 201
res.type('json').status(204).end()
}
function addVideo (req: express.Request, res: express.Response, videoPhysicalFile: Express.Multer.File) {
async function addVideo (req: express.Request, res: express.Response, videoPhysicalFile: Express.Multer.File) {
const videoInfo: VideoCreate = req.body
let videoUUID = ''
return db.sequelize.transaction(t => {
let p: Promise<TagInstance[]>
await db.sequelize.transaction(async t => {
const sequelizeOptions = { transaction: t }
if (!videoInfo.tags) p = Promise.resolve(undefined)
else p = db.Tag.findOrCreateTags(videoInfo.tags, t)
const videoData = {
name: videoInfo.name,
remote: false,
extname: extname(videoPhysicalFile.filename),
category: videoInfo.category,
licence: videoInfo.licence,
language: videoInfo.language,
nsfw: videoInfo.nsfw,
description: videoInfo.description,
duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
channelId: res.locals.videoChannel.id
}
const video = db.Video.build(videoData)
return p
.then(tagInstances => {
const videoData = {
name: videoInfo.name,
remote: false,
extname: extname(videoPhysicalFile.filename),
category: videoInfo.category,
licence: videoInfo.licence,
language: videoInfo.language,
nsfw: videoInfo.nsfw,
description: videoInfo.description,
duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
channelId: res.locals.videoChannel.id
}
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
const videoFileHeight = await getVideoFileHeight(videoFilePath)
const video = db.Video.build(videoData)
return { tagInstances, video }
})
.then(({ tagInstances, video }) => {
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
return getVideoFileHeight(videoFilePath)
.then(height => ({ tagInstances, video, videoFileHeight: height }))
})
.then(({ tagInstances, video, videoFileHeight }) => {
const videoFileData = {
extname: extname(videoPhysicalFile.filename),
resolution: videoFileHeight,
size: videoPhysicalFile.size
}
const videoFileData = {
extname: extname(videoPhysicalFile.filename),
resolution: videoFileHeight,
size: videoPhysicalFile.size
}
const videoFile = db.VideoFile.build(videoFileData)
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
const source = join(videoDir, videoPhysicalFile.filename)
const destination = join(videoDir, video.getVideoFilename(videoFile))
const videoFile = db.VideoFile.build(videoFileData)
return { tagInstances, video, videoFile }
})
.then(({ tagInstances, video, videoFile }) => {
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
const source = join(videoDir, videoPhysicalFile.filename)
const destination = join(videoDir, video.getVideoFilename(videoFile))
await renamePromise(source, destination)
// This is important in case if there is another attempt in the retry process
videoPhysicalFile.filename = video.getVideoFilename(videoFile)
return renamePromise(source, destination)
.then(() => {
// This is important in case if there is another attempt in the retry process
videoPhysicalFile.filename = video.getVideoFilename(videoFile)
return { tagInstances, video, videoFile }
})
})
.then(({ tagInstances, video, videoFile }) => {
const tasks = []
const tasks = []
tasks.push(
video.createTorrentAndSetInfoHash(videoFile),
video.createThumbnail(videoFile),
video.createPreview(videoFile)
)
tasks.push(
video.createTorrentAndSetInfoHash(videoFile),
video.createThumbnail(videoFile),
video.createPreview(videoFile)
)
if (CONFIG.TRANSCODING.ENABLED === true) {
// Put uuid because we don't have id auto incremented for now
const dataInput = {
videoUUID: video.uuid
}
if (CONFIG.TRANSCODING.ENABLED === true) {
// Put uuid because we don't have id auto incremented for now
const dataInput = {
videoUUID: video.uuid
}
tasks.push(
JobScheduler.Instance.createJob(t, 'videoFileOptimizer', dataInput)
)
}
tasks.push(
JobScheduler.Instance.createJob(t, 'videoFileOptimizer', dataInput)
)
}
await Promise.all(tasks)
return Promise.all(tasks).then(() => ({ tagInstances, video, videoFile }))
})
.then(({ tagInstances, video, videoFile }) => {
const options = { transaction: t }
const videoCreated = await video.save(sequelizeOptions)
// Do not forget to add video channel information to the created video
videoCreated.VideoChannel = res.locals.videoChannel
videoUUID = videoCreated.uuid
return video.save(options)
.then(videoCreated => {
// Do not forget to add video channel information to the created video
videoCreated.VideoChannel = res.locals.videoChannel
videoUUID = videoCreated.uuid
videoFile.videoId = video.id
return { tagInstances, video: videoCreated, videoFile }
})
})
.then(({ tagInstances, video, videoFile }) => {
const options = { transaction: t }
videoFile.videoId = video.id
await videoFile.save(sequelizeOptions)
video.VideoFiles = [videoFile]
return videoFile.save(options)
.then(() => video.VideoFiles = [ videoFile ])
.then(() => ({ tagInstances, video }))
})
.then(({ tagInstances, video }) => {
if (!tagInstances) return video
if (videoInfo.tags) {
const tagInstances = await db.Tag.findOrCreateTags(videoInfo.tags, t)
const options = { transaction: t }
return video.setTags(tagInstances, options)
.then(() => {
video.Tags = tagInstances
return video
})
})
.then(video => {
// Let transcoding job send the video to friends because the video file extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
await video.setTags(tagInstances, sequelizeOptions)
video.Tags = tagInstances
}
return video.toAddRemoteJSON()
.then(remoteVideo => {
// Now we'll add the video's meta data to our friends
return addVideoToFriends(remoteVideo, t)
})
})
})
.then(() => logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID))
.catch((err: Error) => {
logger.debug('Cannot insert the video.', err)
throw err
// Let transcoding job send the video to friends because the video file extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
const remoteVideo = await video.toAddRemoteJSON()
// Now we'll add the video's meta data to our friends
return addVideoToFriends(remoteVideo, t)
})
logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID)
}
function updateVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function updateVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot update the video with many retries.'
}
retryTransactionWrapper(updateVideo, options)
.then(() => {
return res.type('json').status(204).end()
})
.catch(err => next(err))
await retryTransactionWrapper(updateVideo, options)
return res.type('json').status(204).end()
}
function updateVideo (req: express.Request, res: express.Response) {
async function updateVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const videoFieldsSave = videoInstance.toJSON()
const videoInfoToUpdate: VideoUpdate = req.body
return db.sequelize.transaction(t => {
let tagsPromise: Promise<TagInstance[]>
if (!videoInfoToUpdate.tags) {
tagsPromise = Promise.resolve(null)
} else {
tagsPromise = db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
}
try {
await db.sequelize.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
return tagsPromise
.then(tagInstances => {
const options = {
transaction: t
}
if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name)
if (videoInfoToUpdate.category !== undefined) videoInstance.set('category', videoInfoToUpdate.category)
if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence)
if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name)
if (videoInfoToUpdate.category !== undefined) videoInstance.set('category', videoInfoToUpdate.category)
if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence)
if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
await videoInstance.save(sequelizeOptions)
return videoInstance.save(options).then(() => tagInstances)
})
.then(tagInstances => {
if (!tagInstances) return
if (videoInfoToUpdate.tags) {
const tagInstances = await db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
const options = { transaction: t }
return videoInstance.setTags(tagInstances, options)
.then(() => {
videoInstance.Tags = tagInstances
await videoInstance.setTags(tagInstances, sequelizeOptions)
videoInstance.Tags = tagInstances
}
return
})
})
.then(() => {
const json = videoInstance.toUpdateRemoteJSON()
const json = videoInstance.toUpdateRemoteJSON()
// Now we'll update the video's meta data to our friends
return updateVideoToFriends(json, t)
})
// Now we'll update the video's meta data to our friends
return updateVideoToFriends(json, t)
})
})
.then(() => {
logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid)
})
.catch(err => {
logger.debug('Cannot update the video.', err)
} catch (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!
Object.keys(videoFieldsSave).forEach(key => {
const value = videoFieldsSave[key]
videoInstance.set(key, value)
})
resetSequelizeInstance(videoInstance, videoFieldsSave)
throw err
})
}
}
function getVideo (req: express.Request, res: express.Response) {
@ -365,17 +303,17 @@ function getVideo (req: express.Request, res: express.Response) {
if (videoInstance.isOwned()) {
// The increment is done directly in the database, not using the instance value
// FIXME: make a real view system
// For example, only add a view when a user watch a video during 30s etc
videoInstance.increment('views')
.then(() => {
// FIXME: make a real view system
// For example, only add a view when a user watch a video during 30s etc
const qaduParams = {
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
}
return quickAndDirtyUpdateVideoToFriends(qaduParams)
})
.catch(err => logger.error('Cannot add view to video %d.', videoInstance.id, err))
.catch(err => logger.error('Cannot add view to video %s.', videoInstance.uuid, err))
} else {
// Just send the event to our friends
const eventParams = {
@ -383,48 +321,48 @@ function getVideo (req: express.Request, res: express.Response) {
type: REQUEST_VIDEO_EVENT_TYPES.VIEWS
}
addEventToRemoteVideo(eventParams)
.catch(err => logger.error('Cannot add event to remote video %s.', videoInstance.uuid, err))
}
// Do not wait the view system
res.json(videoInstance.toFormattedDetailsJSON())
return res.json(videoInstance.toFormattedDetailsJSON())
}
function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormattedObjects(result.data, result.total)))
.catch(err => next(err))
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
function removeVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function removeVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot remove the video with many retries.'
}
retryTransactionWrapper(removeVideo, options)
.then(() => {
return res.type('json').status(204).end()
})
.catch(err => next(err))
await retryTransactionWrapper(removeVideo, options)
return res.type('json').status(204).end()
}
function removeVideo (req: express.Request, res: express.Response) {
async function removeVideo (req: express.Request, res: express.Response) {
const videoInstance: VideoInstance = res.locals.video
return db.sequelize.transaction(t => {
return videoInstance.destroy({ transaction: t })
})
.then(() => {
logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid)
})
.catch(err => {
logger.error('Errors when removed the video.', err)
throw err
await db.sequelize.transaction(async t => {
await videoInstance.destroy({ transaction: t })
})
logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid)
}
function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormattedObjects(result.data, result.total)))
.catch(err => next(err))
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.Video.searchAndPopulateAuthorAndPodAndTags(
req.params.value,
req.query.field,
req.query.start,
req.query.count,
req.query.sort
)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}

View File

@ -1,5 +1,4 @@
import * as express from 'express'
import * as Promise from 'bluebird'
import { database as db } from '../../../initializers/database'
import {
@ -17,7 +16,8 @@ import {
} from '../../../lib'
import {
authenticate,
videoRateValidator
videoRateValidator,
asyncMiddleware
} from '../../../middlewares'
import { UserVideoRateUpdate, VideoRateType } from '../../../../shared'
@ -26,7 +26,7 @@ const rateVideoRouter = express.Router()
rateVideoRouter.put('/:id/rate',
authenticate,
videoRateValidator,
rateVideoRetryWrapper
asyncMiddleware(rateVideoRetryWrapper)
)
// ---------------------------------------------------------------------------
@ -37,126 +37,107 @@ export {
// ---------------------------------------------------------------------------
function rateVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
async function rateVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot update the user video rate.'
}
retryTransactionWrapper(rateVideo, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
await retryTransactionWrapper(rateVideo, options)
return res.type('json').status(204).end()
}
function rateVideo (req: express.Request, res: express.Response) {
async function rateVideo (req: express.Request, res: express.Response) {
const body: UserVideoRateUpdate = req.body
const rateType = body.rating
const videoInstance = res.locals.video
const userInstance = res.locals.oauth.token.User
return db.sequelize.transaction(t => {
return db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
.then(previousRate => {
const options = { transaction: t }
await db.sequelize.transaction(async t => {
const sequelizeOptions = { transaction: t }
const previousRate = await db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
let likesToIncrement = 0
let dislikesToIncrement = 0
let likesToIncrement = 0
let dislikesToIncrement = 0
if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
let promise: Promise<any>
// There was a previous rate, update it
if (previousRate) {
// We will remove the previous rate, so we will need to update the video count attribute
if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
// There was a previous rate, update it
if (previousRate) {
// We will remove the previous rate, so we will need to update the video count attribute
if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
if (rateType === 'none') { // Destroy previous rate
await previousRate.destroy()
} else { // Update previous rate
previousRate.type = rateType as VideoRateType
if (rateType === 'none') { // Destroy previous rate
promise = previousRate.destroy()
} else { // Update previous rate
previousRate.type = rateType as VideoRateType
await previousRate.save()
}
} else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
const query = {
userId: userInstance.id,
videoId: videoInstance.id,
type: rateType
}
promise = previousRate.save()
}
} else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
const query = {
userId: userInstance.id,
videoId: videoInstance.id,
type: rateType
}
await db.UserVideoRate.create(query, sequelizeOptions)
}
promise = db.UserVideoRate.create(query, options)
} else {
promise = Promise.resolve()
}
const incrementQuery = {
likes: likesToIncrement,
dislikes: dislikesToIncrement
}
return promise.then(() => ({ likesToIncrement, dislikesToIncrement }))
})
.then(({ likesToIncrement, dislikesToIncrement }) => {
const options = { transaction: t }
const incrementQuery = {
likes: likesToIncrement,
dislikes: dislikesToIncrement
}
// Even if we do not own the video we increment the attributes
// It is useful for the user to have a feedback
await videoInstance.increment(incrementQuery, sequelizeOptions)
// Even if we do not own the video we increment the attributes
// It is usefull for the user to have a feedback
return videoInstance.increment(incrementQuery, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
})
.then(({ likesToIncrement, dislikesToIncrement }) => {
// No need for an event type, we own the video
if (videoInstance.isOwned()) return { likesToIncrement, dislikesToIncrement }
// Send a event to original pod
if (videoInstance.isOwned() === false) {
const eventsParams = []
const eventsParams = []
if (likesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
count: likesToIncrement
})
}
if (likesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
count: likesToIncrement
})
}
if (dislikesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
count: dislikesToIncrement
})
}
if (dislikesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
count: dislikesToIncrement
})
}
return addEventsToRemoteVideo(eventsParams, t).then(() => ({ likesToIncrement, dislikesToIncrement }))
})
.then(({ likesToIncrement, dislikesToIncrement }) => {
// We do not own the video, there is no need to send a quick and dirty update to friends
// Our rate was already sent by the addEvent function
if (videoInstance.isOwned() === false) return undefined
await addEventsToRemoteVideo(eventsParams, t)
} else { // We own the video, we need to send a quick and dirty update to friends to notify the counts changed
const qadusParams = []
const qadusParams = []
if (likesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.LIKES
})
}
if (likesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.LIKES
})
}
if (dislikesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
})
}
if (dislikesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
})
}
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
})
.then(() => logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username))
.catch(err => {
// This is just a debug because we will retry the insert
logger.debug('Cannot add the user video rate.', err)
throw err
await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
}
})
logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
}

View File

@ -1,7 +1,7 @@
import * as express from 'express'
import { join } from 'path'
import * as validator from 'validator'
import * as Promise from 'bluebird'
import * as Bluebird from 'bluebird'
import { database as db } from '../initializers/database'
import {
@ -11,6 +11,7 @@ import {
OPENGRAPH_AND_OEMBED_COMMENT
} from '../initializers'
import { root, readFileBufferPromise, escapeHTML } from '../helpers'
import { asyncMiddleware } from '../middlewares'
import { VideoInstance } from '../models'
const clientsRouter = express.Router()
@ -21,7 +22,9 @@ const indexPath = join(distPath, 'index.html')
// Special route that add OpenGraph and oEmbed tags
// Do not use a template engine for a so little thing
clientsRouter.use('/videos/watch/:id', generateWatchHtmlPage)
clientsRouter.use('/videos/watch/:id',
asyncMiddleware(generateWatchHtmlPage)
)
clientsRouter.use('/videos/embed', (req: express.Request, res: express.Response, next: express.NextFunction) => {
res.sendFile(embedPath)
@ -90,9 +93,9 @@ function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoInstance
return htmlStringPage.replace(OPENGRAPH_AND_OEMBED_COMMENT, tagsString)
}
function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) {
async function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoId = '' + req.params.id
let videoPromise: Promise<VideoInstance>
let videoPromise: Bluebird<VideoInstance>
// Let Angular application handle errors
if (validator.isUUID(videoId, 4)) {
@ -103,21 +106,19 @@ function generateWatchHtmlPage (req: express.Request, res: express.Response, nex
return res.sendFile(indexPath)
}
Promise.all([
let [ file, video ] = await Promise.all([
readFileBufferPromise(indexPath),
videoPromise
])
.then(([ file, video ]) => {
file = file as Buffer
video = video as VideoInstance
const html = file.toString()
file = file as Buffer
video = video as VideoInstance
// Let Angular application handle errors
if (!video) return res.sendFile(indexPath)
const html = file.toString()
const htmlStringPageWithTags = addOpenGraphAndOEmbedTags(html, video)
res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
})
.catch(err => next(err))
// Let Angular application handle errors
if (!video) return res.sendFile(indexPath)
const htmlStringPageWithTags = addOpenGraphAndOEmbedTags(html, video)
res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
}

View File

@ -7,6 +7,7 @@ import {
STATIC_PATHS
} from '../initializers'
import { VideosPreviewCache } from '../lib'
import { asyncMiddleware } from '../middlewares'
const staticRouter = express.Router()
@ -39,7 +40,7 @@ staticRouter.use(
// Video previews path for express
staticRouter.use(
STATIC_PATHS.PREVIEWS + ':uuid.jpg',
getPreview
asyncMiddleware(getPreview)
)
// ---------------------------------------------------------------------------
@ -50,11 +51,9 @@ export {
// ---------------------------------------------------------------------------
function getPreview (req: express.Request, res: express.Response, next: express.NextFunction) {
VideosPreviewCache.Instance.getPreviewPath(req.params.uuid)
.then(path => {
if (!path) return res.sendStatus(404)
async function getPreview (req: express.Request, res: express.Response, next: express.NextFunction) {
const path = await VideosPreviewCache.Instance.getPreviewPath(req.params.uuid)
if (!path) return res.sendStatus(404)
return res.sendFile(path, { maxAge: STATIC_MAX_AGE })
})
return res.sendFile(path, { maxAge: STATIC_MAX_AGE })
}

View File

@ -1,6 +1,5 @@
// TODO: import from ES6 when retry typing file will include errorFilter function
import * as retry from 'async/retry'
import * as Promise from 'bluebird'
import { logger } from './logger'

View File

@ -1,4 +1,5 @@
import * as express from 'express'
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { pseudoRandomBytesPromise } from './core-utils'
@ -69,6 +70,13 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
return resolutionsEnabled
}
function resetSequelizeInstance (instance: Sequelize.Instance<any>, savedFields: object) {
Object.keys(savedFields).forEach(key => {
const value = savedFields[key]
instance.set(key, value)
})
}
type SortType = { sortModel: any, sortValue: string }
// ---------------------------------------------------------------------------
@ -79,5 +87,6 @@ export {
getFormattedObjects,
isSignupAllowed,
computeResolutionsToTranscode,
resetSequelizeInstance,
SortType
}

View File

@ -63,6 +63,7 @@ const sequelize = new Sequelize(dbname, username, password, {
host: CONFIG.DATABASE.HOSTNAME,
port: CONFIG.DATABASE.PORT,
benchmark: isTestInstance(),
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE,
logging: (message: string, benchmark: number) => {
let newMessage = message

View File

@ -2,12 +2,11 @@ import * as Sequelize from 'sequelize'
import { addVideoChannelToFriends } from './friends'
import { database as db } from '../initializers'
import { logger } from '../helpers'
import { AuthorInstance } from '../models'
import { VideoChannelCreate } from '../../shared/models'
function createVideoChannel (videoChannelInfo: VideoChannelCreate, author: AuthorInstance, t: Sequelize.Transaction) {
let videoChannelUUID = ''
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, author: AuthorInstance, t: Sequelize.Transaction) {
const videoChannelData = {
name: videoChannelInfo.name,
description: videoChannelInfo.description,
@ -18,25 +17,34 @@ function createVideoChannel (videoChannelInfo: VideoChannelCreate, author: Autho
const videoChannel = db.VideoChannel.build(videoChannelData)
const options = { transaction: t }
return videoChannel.save(options)
.then(videoChannelCreated => {
// Do not forget to add Author information to the created video channel
videoChannelCreated.Author = author
videoChannelUUID = videoChannelCreated.uuid
const videoChannelCreated = await videoChannel.save(options)
return videoChannelCreated
})
.then(videoChannel => {
const remoteVideoChannel = videoChannel.toAddRemoteJSON()
// Do not forget to add Author information to the created video channel
videoChannelCreated.Author = author
// Now we'll add the video channel's meta data to our friends
return addVideoChannelToFriends(remoteVideoChannel, t)
})
.then(() => videoChannelUUID) // Return video channel UUID
const remoteVideoChannel = videoChannelCreated.toAddRemoteJSON()
// Now we'll add the video channel's meta data to our friends
await addVideoChannelToFriends(remoteVideoChannel, t)
return videoChannelCreated
}
async function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
try {
const videoChannel = await db.VideoChannel.loadByHostAndUUID(podHost, uuid, t)
if (!videoChannel) throw new Error('Video channel not found')
return videoChannel
} catch (err) {
logger.error('Cannot load video channel from host and uuid.', { error: err.stack, podHost, uuid })
throw err
}
}
// ---------------------------------------------------------------------------
export {
createVideoChannel
createVideoChannel,
fetchVideoChannelByHostAndUUID
}

View File

@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from 'express'
// Syntactic sugar to avoid try/catch in express controllers
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
function asyncMiddleware (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) {
return (req: Request, res: Response, next: NextFunction) => {
return Promise.resolve(fn(req, res, next))
.catch(next)
}
}
// ---------------------------------------------------------------------------
export {
asyncMiddleware
}

View File

@ -1,5 +1,6 @@
export * from './validators'
export * from './admin'
export * from './async'
export * from './oauth'
export * from './pagination'
export * from './pods'