"async": "^2.0.0",
"bcrypt": "^1.0.2",
"bittorrent-tracker": "^9.0.0",
+ "bluebird": "^3.5.0",
"body-parser": "^1.12.4",
"concurrently": "^3.1.0",
"config": "^1.14.0",
"scripty": "^1.5.0",
"sequelize": "4.0.0-2",
"ts-node": "^3.0.6",
- "typescript": "^2.3.4",
+ "typescript": "^2.4.1",
"validator": "^7.0.0",
"winston": "^2.1.1",
"ws": "^2.0.0"
"@types/mkdirp": "^0.3.29",
"@types/morgan": "^1.7.32",
"@types/multer": "^0.0.34",
- "@types/node": "^7.0.18",
+ "@types/node": "^8.0.3",
"@types/request": "^0.0.44",
"@types/sequelize": "^4.0.55",
"@types/validator": "^6.2.0",
-import * as eachSeries from 'async/eachSeries'
import * as rimraf from 'rimraf'
+import * as Promise from 'bluebird'
import { CONFIG } from '../../../server/initializers/constants'
import { database as db } from '../../../server/initializers/database'
-db.init(true, function () {
- db.sequelize.drop().asCallback(function (err) {
- if (err) throw err
-
+db.init(true)
+ .then(() => {
+ return db.sequelize.drop()
+ })
+ .then(() => {
console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
const STORAGE = CONFIG.STORAGE
- eachSeries(Object.keys(STORAGE), function (storage, callbackEach) {
+ Promise.mapSeries(Object.keys(STORAGE), storage => {
const storageDir = STORAGE[storage]
- rimraf(storageDir, function (err) {
- console.info('%s deleted.', storageDir)
- return callbackEach(err)
+ return new Promise((res, rej) => {
+ rimraf(storageDir, function (err) {
+ if (err) return rej(err)
+
+ console.info('%s deleted.', storageDir)
+ return res()
+ })
})
- }, function () {
- process.exit(0)
})
+ .then(() => process.exit(0))
})
-})
process.exit(-1)
}
-db.init(true, function () {
- db.User.loadByUsername(program.user, function (err, user) {
- if (err) {
- console.error(err)
- return
- }
-
+db.init(true)
+ .then(() => {
+ return db.User.loadByUsername(program.user)
+ })
+ .then(user => {
if (!user) {
console.error('User unknown.')
return
rl.on('line', function (password) {
user.password = password
- user.save().asCallback(function (err) {
- if (err) {
- console.error(err)
- } else {
- console.log('User password updated.')
- }
-
- process.exit(0)
- })
+ user.save()
+ .then(() => console.log('User password updated.'))
+ .catch(err => console.error(err))
+ .finally(() => process.exit(0))
})
})
-})
-#!/usr/bin/env sh
+#!/bin/bash
npm run build:server
npm test || exit -1
cd .. || exit -1
-npm run tslint -- --type-check --project ./tsconfig.json -c ./tslint.json server.ts server/**/*.ts || exit -1
+npm run tslint -- --type-check --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" || exit -1
mocha --bail server/tests
import { database as db } from '../server/initializers/database'
import { hasFriends } from '../server/lib/friends'
-db.init(true, function () {
- hasFriends(function (err, itHasFriends) {
- if (err) throw err
-
+db.init(true)
+ .then(() => {
+ return hasFriends()
+ })
+ .then(itHasFriends => {
if (itHasFriends === true) {
console.log('Cannot update host because you have friends!')
process.exit(-1)
}
console.log('Updating torrent files.')
- db.Video.list(function (err, videos) {
- if (err) throw err
-
- videos.forEach(function (video) {
- const torrentName = video.id + '.torrent'
- const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
- const filename = video.id + video.extname
-
- const parsed = parseTorrent(readFileSync(torrentPath))
- parsed.announce = [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOST + '/tracker/socket' ]
- parsed.urlList = [ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + filename ]
-
- const buf = parseTorrent.toTorrentFile(parsed)
- writeFileSync(torrentPath, buf)
- })
-
- process.exit(0)
+ return db.Video.list()
+ })
+ .then(videos => {
+ videos.forEach(function (video) {
+ const torrentName = video.id + '.torrent'
+ const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
+ const filename = video.id + video.extname
+
+ const parsed = parseTorrent(readFileSync(torrentPath))
+ parsed.announce = [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOST + '/tracker/socket' ]
+ parsed.urlList = [ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + filename ]
+
+ const buf = parseTorrent.toTorrentFile(parsed)
+ writeFileSync(torrentPath, buf)
})
+
+ process.exit(0)
})
-})
import { API_VERSION, CONFIG } from './server/initializers/constants'
// Initialize database and models
import { database as db } from './server/initializers/database'
-db.init(false, onDatabaseInitDone)
+db.init(false).then(() => onDatabaseInitDone())
// ----------- Checker -----------
import { checkMissedConfig, checkFFmpeg, checkConfig } from './server/initializers/checker'
if (missed.length !== 0) {
throw new Error('Miss some configurations keys : ' + missed)
}
-checkFFmpeg(function (err) {
- if (err) {
- throw err
- }
-})
+checkFFmpeg()
const errorMessage = checkConfig()
if (errorMessage !== null) {
function onDatabaseInitDone () {
const port = CONFIG.LISTEN.PORT
// Run the migration scripts if needed
- migrate(function (err) {
- if (err) throw err
-
- installApplication(function (err) {
- if (err) throw err
-
+ migrate()
+ .then(() => {
+ return installApplication()
+ })
+ .then(() => {
// ----------- Make the server listening -----------
server.listen(port, function () {
// Activate the communication with friends
logger.info('Webserver: %s', CONFIG.WEBSERVER.URL)
})
})
- })
}
return res.type('json').status(403).end()
}
- db.OAuthClient.loadFirstClient(function (err, client) {
- if (err) return next(err)
- if (!client) return next(new Error('No client available.'))
-
- const json: OAuthClientLocal = {
- client_id: client.clientId,
- client_secret: client.clientSecret
- }
- res.json(json)
- })
+ db.OAuthClient.loadFirstClient()
+ .then(client => {
+ 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))
}
// ---------------------------------------------------------------------------
import * as express from 'express'
-import { waterfall } from 'async'
import { database as db } from '../../initializers/database'
import { CONFIG } from '../../initializers'
function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
const informations = req.body
- waterfall<string, Error>([
- function addPod (callback) {
- const pod = db.Pod.build(informations)
- pod.save().asCallback(function (err, podCreated) {
- // Be sure about the number of parameters for the callback
- return callback(err, podCreated)
- })
- },
-
- function sendMyVideos (podCreated: PodInstance, callback) {
- sendOwnedVideosToPod(podCreated.id)
-
- callback(null)
- },
-
- function fetchMyCertificate (callback) {
- getMyPublicCert(function (err, cert) {
- if (err) {
- logger.error('Cannot read cert file.')
- return callback(err)
- }
-
- return callback(null, cert)
- })
- }
- ], function (err, cert) {
- if (err) return next(err)
-
- return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
- })
+ const pod = db.Pod.build(informations)
+ pod.save()
+ .then(podCreated => {
+ return sendOwnedVideosToPod(podCreated.id)
+ })
+ .then(() => {
+ return getMyPublicCert()
+ })
+ .then(cert => {
+ return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
+ })
+ .catch(err => next(err))
}
function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
- db.Pod.list(function (err, podsList) {
- if (err) return next(err)
-
- res.json(getFormatedObjects(podsList, podsList.length))
- })
+ db.Pod.list()
+ .then(podsList => res.json(getFormatedObjects(podsList, podsList.length)))
+ .catch(err => next(err))
}
function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
const hosts = req.body.hosts as string[]
- makeFriends(hosts, function (err) {
- if (err) {
- logger.error('Could not make friends.', { error: err })
- return
- }
-
- logger.info('Made friends!')
- })
+ makeFriends(hosts)
+ .then(() => logger.info('Made friends!'))
+ .catch(err => logger.error('Could not make friends.', { error: err }))
+ // Don't wait the process that could be long
res.type('json').status(204).end()
}
function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
- quitFriends(function (err) {
- if (err) return next(err)
-
- res.type('json').status(204).end()
- })
+ quitFriends()
+ .then(() => res.type('json').status(204).end())
+ .catch(err => next(err))
}
import * as express from 'express'
-import * as waterfall from 'async/waterfall'
import { database as db } from '../../../initializers/database'
import { checkSignature, signatureValidator } from '../../../middlewares'
function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
const host = req.body.signature.host
- waterfall([
- function loadPod (callback) {
- db.Pod.loadByHost(host, callback)
- },
-
- function deletePod (pod, callback) {
- pod.destroy().asCallback(callback)
- }
- ], function (err) {
- if (err) return next(err)
-
- return res.type('json').status(204).end()
- })
+ db.Pod.loadByHost(host)
+ .then(pod => {
+ return pod.destroy()
+ })
+ .then(() => res.type('json').status(204).end())
+ .catch(err => next(err))
}
import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { eachSeries, waterfall } from 'async'
+import * as Promise from 'bluebird'
import { database as db } from '../../../initializers/database'
import {
remoteQaduVideosValidator,
remoteEventsVideosValidator
} from '../../../middlewares'
-import {
- logger,
- commitTransaction,
- retryTransactionWrapper,
- rollbackTransaction,
- startSerializableTransaction
-} from '../../../helpers'
+import { logger, retryTransactionWrapper } from '../../../helpers'
import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
import { PodInstance, VideoInstance } from '../../../models'
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
// Functions to call when processing a remote request
-const functionsHash = {}
+const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
// We need to process in the same order to keep consistency
// TODO: optimization
- eachSeries(requests, function (request: any, callbackEach) {
+ Promise.mapSeries(requests, (request: any) => {
const data = request.data
// Get the function we need to call in order to process the request
const fun = functionsHash[request.type]
if (fun === undefined) {
logger.error('Unkown remote request type %s.', request.type)
- return callbackEach(null)
+ return
}
- fun.call(this, data, fromPod, callbackEach)
- }, function (err) {
- if (err) logger.error('Error managing remote videos.', { error: err })
+ return fun.call(this, data, fromPod)
})
+ .catch(err => logger.error('Error managing remote videos.', { error: err }))
// We don't need to keep the other pod waiting
return res.type('json').status(204).end()
const requests = req.body.data
const fromPod = res.locals.secure.pod
- eachSeries(requests, function (request: any, callbackEach) {
+ Promise.mapSeries(requests, (request: any) => {
const videoData = request.data
- quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod, callbackEach)
- }, function (err) {
- if (err) logger.error('Error managing remote videos.', { error: err })
+ return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
})
+ .catch(err => logger.error('Error managing remote videos.', { error: err }))
return res.type('json').status(204).end()
}
const requests = req.body.data
const fromPod = res.locals.secure.pod
- eachSeries(requests, function (request: any, callbackEach) {
+ Promise.mapSeries(requests, (request: any) => {
const eventData = request.data
- processVideosEventsRetryWrapper(eventData, fromPod, callbackEach)
- }, function (err) {
- if (err) logger.error('Error managing remote videos.', { error: err })
+ return processVideosEventsRetryWrapper(eventData, fromPod)
})
+ .catch(err => logger.error('Error managing remote videos.', { error: err }))
return res.type('json').status(204).end()
}
-function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance) {
const options = {
arguments: [ eventData, fromPod ],
errorMessage: 'Cannot process videos events with many retries.'
}
- retryTransactionWrapper(processVideosEvents, options, finalCallback)
+ return retryTransactionWrapper(processVideosEvents, options)
}
-function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
- waterfall([
- startSerializableTransaction,
-
- function findVideo (t, callback) {
- fetchOwnedVideo(eventData.remoteId, function (err, videoInstance) {
- return callback(err, t, videoInstance)
- })
- },
+function processVideosEvents (eventData: any, fromPod: PodInstance) {
- function updateVideoIntoDB (t, videoInstance, callback) {
- const options = { transaction: t }
+ return db.sequelize.transaction(t => {
+ return fetchOwnedVideo(eventData.remoteId)
+ .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:
- return callback(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
- videoInstance.increment(query, options).asCallback(function (err) {
- return callback(err, t, videoInstance, qaduType)
+ return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
})
- },
-
- function sendQaduToFriends (t, videoInstance, qaduType, callback) {
- const qadusParams = [
- {
- videoId: videoInstance.id,
- type: qaduType
- }
- ]
-
- quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
- return callback(err, t)
+ .then(({ videoInstance, qaduType }) => {
+ const qadusParams = [
+ {
+ videoId: videoInstance.id,
+ type: qaduType
+ }
+ ]
+
+ return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
- },
-
- commitTransaction
-
- ], function (err: Error, t: Sequelize.Transaction) {
- if (err) {
- logger.debug('Cannot process a video event.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
-
- logger.info('Remote video event processed for video %s.', eventData.remoteId)
- return finalCallback(null)
+ })
+ .then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
+ .catch(err => {
+ logger.debug('Cannot process a video event.', { error: err })
+ throw err
})
}
-function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance) {
const options = {
arguments: [ videoData, fromPod ],
errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
}
- retryTransactionWrapper(quickAndDirtyUpdateVideo, options, finalCallback)
+ return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
}
-function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance) {
let videoName
- waterfall([
- startSerializableTransaction,
-
- function findVideo (t, callback) {
- fetchRemoteVideo(fromPod.host, videoData.remoteId, function (err, videoInstance) {
- return callback(err, t, videoInstance)
- })
- },
-
- function updateVideoIntoDB (t, videoInstance, callback) {
- const options = { transaction: t }
+ return db.sequelize.transaction(t => {
+ return fetchRemoteVideo(fromPod.host, videoData.remoteId)
+ .then(videoInstance => {
+ const options = { transaction: t }
- videoName = videoInstance.name
+ videoName = videoInstance.name
- 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)
+ }
- videoInstance.save(options).asCallback(function (err) {
- return callback(err, t)
+ return videoInstance.save(options)
})
- },
-
- commitTransaction
-
- ], function (err: Error, t: Sequelize.Transaction) {
- if (err) {
- logger.debug('Cannot quick and dirty update the remote video.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
-
- logger.info('Remote video %s quick and dirty updated', videoName)
- return finalCallback(null)
})
+ .then(() => logger.info('Remote video %s quick and dirty updated', videoName))
+ .catch(err => logger.debug('Cannot quick and dirty update the remote video.', { error: err }))
}
// Handle retries on fail
-function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance) {
const options = {
arguments: [ videoToCreateData, fromPod ],
errorMessage: 'Cannot insert the remote video with many retries.'
}
- retryTransactionWrapper(addRemoteVideo, options, finalCallback)
+ return retryTransactionWrapper(addRemoteVideo, options)
}
-function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance) {
logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
- waterfall([
-
- startSerializableTransaction,
-
- function assertRemoteIdAndHostUnique (t, callback) {
- db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId, function (err, video) {
- if (err) return callback(err)
+ return db.sequelize.transaction(t => {
+ return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId)
+ .then(video => {
+ if (video) throw new Error('RemoteId and host pair is not unique.')
- if (video) return callback(new Error('RemoteId and host pair is not unique.'))
-
- return callback(null, t)
+ return undefined
})
- },
-
- function findOrCreateAuthor (t, callback) {
- const name = videoToCreateData.author
- const podId = fromPod.id
- // This author is from another pod so we do not associate a user
- const userId = null
+ .then(() => {
+ const name = videoToCreateData.author
+ const podId = fromPod.id
+ // This author is from another pod so we do not associate a user
+ const userId = null
- db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
- return callback(err, t, authorInstance)
+ return db.Author.findOrCreateAuthor(name, podId, userId, t)
})
- },
+ .then(author => {
+ const tags = videoToCreateData.tags
- function findOrCreateTags (t, author, callback) {
- const tags = videoToCreateData.tags
-
- db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
- return callback(err, t, author, tagInstances)
+ return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
})
- },
-
- function createVideoObject (t, author, tagInstances, callback) {
- const videoData = {
- name: videoToCreateData.name,
- remoteId: videoToCreateData.remoteId,
- extname: videoToCreateData.extname,
- infoHash: videoToCreateData.infoHash,
- category: videoToCreateData.category,
- licence: videoToCreateData.licence,
- language: videoToCreateData.language,
- nsfw: videoToCreateData.nsfw,
- description: videoToCreateData.description,
- authorId: author.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
- }
-
- const video = db.Video.build(videoData)
-
- return callback(null, t, tagInstances, video)
- },
-
- function generateThumbnail (t, tagInstances, video, callback) {
- db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
- if (err) {
- logger.error('Cannot generate thumbnail from data.', { error: err })
- return callback(err)
+ .then(({ author, tagInstances }) => {
+ const videoData = {
+ name: videoToCreateData.name,
+ remoteId: videoToCreateData.remoteId,
+ extname: videoToCreateData.extname,
+ infoHash: videoToCreateData.infoHash,
+ category: videoToCreateData.category,
+ licence: videoToCreateData.licence,
+ language: videoToCreateData.language,
+ nsfw: videoToCreateData.nsfw,
+ description: videoToCreateData.description,
+ authorId: author.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
}
- return callback(err, t, tagInstances, video)
+ const video = db.Video.build(videoData)
+ return { tagInstances, video }
})
- },
-
- function insertVideoIntoDB (t, tagInstances, video, callback) {
- const options = {
- transaction: t
- }
-
- video.save(options).asCallback(function (err, videoCreated) {
- return callback(err, t, tagInstances, videoCreated)
+ .then(({ tagInstances, video }) => {
+ return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
})
- },
-
- function associateTagsToVideo (t, tagInstances, video, callback) {
- const options = {
- transaction: t
- }
+ .then(({ tagInstances, video }) => {
+ const options = {
+ transaction: t
+ }
- video.setTags(tagInstances, options).asCallback(function (err) {
- return callback(err, t)
+ return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
})
- },
-
- commitTransaction
-
- ], function (err: Error, t: Sequelize.Transaction) {
- if (err) {
- // This is just a debug because we will retry the insert
- logger.debug('Cannot insert the remote video.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
+ .then(({ tagInstances, videoCreated }) => {
+ const options = {
+ transaction: t
+ }
- logger.info('Remote video %s inserted.', videoToCreateData.name)
- return finalCallback(null)
+ return videoCreated.setTags(tagInstances, options)
+ })
+ })
+ .then(() => logger.info('Remote video %s inserted.', videoToCreateData.name))
+ .catch(err => {
+ logger.debug('Cannot insert the remote video.', { error: err })
+ throw err
})
}
// Handle retries on fail
-function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance) {
const options = {
arguments: [ videoAttributesToUpdate, fromPod ],
errorMessage: 'Cannot update the remote video with many retries'
}
- retryTransactionWrapper(updateRemoteVideo, options, finalCallback)
+ return retryTransactionWrapper(updateRemoteVideo, options)
}
-function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance) {
logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
- waterfall([
+ return db.sequelize.transaction(t => {
+ return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
+ .then(videoInstance => {
+ const tags = videoAttributesToUpdate.tags
- startSerializableTransaction,
-
- function findVideo (t, callback) {
- fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
- return callback(err, t, videoInstance)
+ return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
})
- },
-
- function findOrCreateTags (t, videoInstance, callback) {
- const tags = videoAttributesToUpdate.tags
-
- db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
- return callback(err, t, videoInstance, tagInstances)
- })
- },
-
- function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
- const options = { transaction: t }
-
- 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('infoHash', videoAttributesToUpdate.infoHash)
- videoInstance.set('duration', videoAttributesToUpdate.duration)
- videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
- videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
- videoInstance.set('extname', videoAttributesToUpdate.extname)
- videoInstance.set('views', videoAttributesToUpdate.views)
- videoInstance.set('likes', videoAttributesToUpdate.likes)
- videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
-
- videoInstance.save(options).asCallback(function (err) {
- return callback(err, t, videoInstance, tagInstances)
+ .then(({ videoInstance, tagInstances }) => {
+ const options = { transaction: t }
+
+ 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('infoHash', videoAttributesToUpdate.infoHash)
+ videoInstance.set('duration', videoAttributesToUpdate.duration)
+ videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
+ videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
+ videoInstance.set('extname', videoAttributesToUpdate.extname)
+ videoInstance.set('views', videoAttributesToUpdate.views)
+ videoInstance.set('likes', videoAttributesToUpdate.likes)
+ videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
+
+ return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
})
- },
-
- function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
- const options = { transaction: t }
+ .then(({ videoInstance, tagInstances }) => {
+ const options = { transaction: t }
- videoInstance.setTags(tagInstances, options).asCallback(function (err) {
- return callback(err, t)
+ return videoInstance.setTags(tagInstances, options)
})
- },
-
- commitTransaction
-
- ], function (err: Error, t: Sequelize.Transaction) {
- if (err) {
- // This is just a debug because we will retry the insert
- logger.debug('Cannot update the remote video.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
-
- logger.info('Remote video %s updated', videoAttributesToUpdate.name)
- return finalCallback(null)
+ })
+ .then(() => logger.info('Remote video %s updated', videoAttributesToUpdate.name))
+ .catch(err => {
+ // This is just a debug because we will retry the insert
+ logger.debug('Cannot update the remote video.', { error: err })
+ throw err
})
}
-function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance, callback: (err: Error) => void) {
+function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance) {
// We need the instance because we have to remove some other stuffs (thumbnail etc)
- fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
- // Do not return the error, continue the process
- if (err) return callback(null)
-
- logger.debug('Removing remote video %s.', video.remoteId)
- video.destroy().asCallback(function (err) {
- // Do not return the error, continue the process
- if (err) {
- logger.error('Cannot remove remote video with id %s.', videoToRemoveData.remoteId, { error: err })
- }
-
- return callback(null)
+ return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
+ .then(video => {
+ logger.debug('Removing remote video %s.', video.remoteId)
+ return video.destroy()
+ })
+ .catch(err => {
+ logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err })
})
- })
}
-function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance, callback: (err: Error) => void) {
- fetchOwnedVideo(reportData.videoRemoteId, function (err, video) {
- if (err || !video) {
- if (!err) err = new Error('video not found')
-
- logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId })
- // Do not return the error, continue the process
- return callback(null)
- }
-
- logger.debug('Reporting remote abuse for video %s.', video.id)
-
- const videoAbuseData = {
- reporterUsername: reportData.reporterUsername,
- reason: reportData.reportReason,
- reporterPodId: fromPod.id,
- videoId: video.id
- }
+function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance) {
+ return fetchOwnedVideo(reportData.videoRemoteId)
+ .then(video => {
+ logger.debug('Reporting remote abuse for video %s.', video.id)
- db.VideoAbuse.create(videoAbuseData).asCallback(function (err) {
- if (err) {
- logger.error('Cannot create remote abuse video.', { error: err })
+ const videoAbuseData = {
+ reporterUsername: reportData.reporterUsername,
+ reason: reportData.reportReason,
+ reporterPodId: fromPod.id,
+ videoId: video.id
}
- return callback(null)
+ return db.VideoAbuse.create(videoAbuseData)
})
- })
+ .catch(err => logger.error('Cannot create remote abuse video.', { error: err }))
}
-function fetchOwnedVideo (id: string, callback: (err: Error, video?: VideoInstance) => void) {
- db.Video.load(id, function (err, video) {
- if (err || !video) {
- if (!err) err = new Error('video not found')
+function fetchOwnedVideo (id: string) {
+ return db.Video.load(id)
+ .then(video => {
+ if (!video) throw new Error('Video not found')
+ return video
+ })
+ .catch(err => {
logger.error('Cannot load owned video from id.', { error: err, id })
- return callback(err)
- }
-
- return callback(null, video)
- })
+ throw err
+ })
}
-function fetchRemoteVideo (podHost: string, remoteId: string, callback: (err: Error, video?: VideoInstance) => void) {
- db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
- if (err || !video) {
- if (!err) err = new Error('video not found')
+function fetchRemoteVideo (podHost: string, remoteId: string) {
+ return db.Video.loadByHostAndRemoteId(podHost, remoteId)
+ .then(video => {
+ if (!video) throw new Error('Video not found')
+ return video
+ })
+ .catch(err => {
logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId })
- return callback(err)
- }
-
- return callback(null, video)
- })
+ throw err
+ })
}
import * as express from 'express'
-import { parallel } from 'async'
+import * as Promise from 'bluebird'
import {
AbstractRequestScheduler,
// ---------------------------------------------------------------------------
function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
- parallel({
+ Promise.props({
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
- }, function (err, result) {
- if (err) return next(err)
-
- return res.json(result)
})
+ .then(result => res.json(result))
+ .catch(err => next(err))
}
// ---------------------------------------------------------------------------
-function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler) {
- return function (callback) {
- requestScheduler.remainingRequestsCount(function (err, count) {
- if (err) return callback(err)
-
- const result: RequestSchedulerStatsAttributes = {
- totalRequests: count,
- requestsLimitPods: requestScheduler.limitPods,
- requestsLimitPerPod: requestScheduler.limitPerPod,
- remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
- milliSecondsInterval: requestScheduler.requestInterval
- }
-
- return callback(null, 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
+ }
+
+ return result
+ })
}
import * as express from 'express'
-import { waterfall } from 'async'
import { database as db } from '../../initializers/database'
-import { CONFIG, USER_ROLES } from '../../initializers'
+import { USER_ROLES } from '../../initializers'
import { logger, getFormatedObjects } from '../../helpers'
import {
authenticate,
role: USER_ROLES.USER
})
- user.save().asCallback(function (err) {
- if (err) return next(err)
-
- return res.type('json').status(204).end()
- })
+ user.save()
+ .then(() => res.type('json').status(204).end())
+ .catch(err => next(err))
}
function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
- db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
- if (err) return next(err)
-
- return res.json(user.toFormatedJSON())
- })
+ db.User.loadByUsername(res.locals.oauth.token.user.username)
+ .then(user => res.json(user.toFormatedJSON()))
+ .catch(err => next(err))
}
function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoId = '' + req.params.videoId
const userId = +res.locals.oauth.token.User.id
- db.UserVideoRate.load(userId, videoId, null, function (err, ratingObj) {
- if (err) return next(err)
-
- const rating = ratingObj ? ratingObj.type : 'none'
-
- const json: FormatedUserVideoRate = {
- videoId,
- rating
- }
- res.json(json)
- })
+ db.UserVideoRate.load(userId, videoId, null)
+ .then(ratingObj => {
+ const rating = ratingObj ? ratingObj.type : 'none'
+ const json: FormatedUserVideoRate = {
+ videoId,
+ rating
+ }
+ res.json(json)
+ })
+ .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, function (err, usersList, usersTotal) {
- if (err) return next(err)
-
- res.json(getFormatedObjects(usersList, usersTotal))
- })
+ db.User.listForApi(req.query.start, req.query.count, req.query.sort)
+ .then(resultList => {
+ res.json(getFormatedObjects(resultList.data, resultList.total))
+ })
+ .catch(err => next(err))
}
function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
- waterfall([
- function loadUser (callback) {
- db.User.loadById(req.params.id, callback)
- },
-
- function deleteUser (user, callback) {
- user.destroy().asCallback(callback)
- }
- ], function andFinally (err) {
- if (err) {
+ db.User.loadById(req.params.id)
+ .then(user => user.destroy())
+ .then(() => res.sendStatus(204))
+ .catch(err => {
logger.error('Errors when removed the user.', { error: err })
return next(err)
- }
-
- return res.sendStatus(204)
- })
+ })
}
function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
- db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
- if (err) return next(err)
-
- if (req.body.password) user.password = req.body.password
- if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
+ db.User.loadByUsername(res.locals.oauth.token.user.username)
+ .then(user => {
+ if (req.body.password) user.password = req.body.password
+ if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
- user.save().asCallback(function (err) {
- if (err) return next(err)
-
- return res.sendStatus(204)
+ return user.save()
})
- })
+ .then(() => res.sendStatus(204))
+ .catch(err => next(err))
}
function success (req: express.Request, res: express.Response, next: express.NextFunction) {
import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import * as friends from '../../../lib/friends'
import {
logger,
getFormatedObjects,
- retryTransactionWrapper,
- startSerializableTransaction,
- commitTransaction,
- rollbackTransaction
+ retryTransactionWrapper
} from '../../../helpers'
import {
authenticate,
setVideoAbusesSort,
setPagination
} from '../../../middlewares'
+import { VideoInstance } from '../../../models'
const abuseVideoRouter = express.Router()
// ---------------------------------------------------------------------------
function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
- db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) {
- if (err) return next(err)
-
- res.json(getFormatedObjects(abusesList, abusesTotal))
- })
+ db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
+ .then(result => res.json(getFormatedObjects(result.data, result.total)))
+ .catch(err => next(err))
}
function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
errorMessage: 'Cannot report abuse to the video with many retries.'
}
- retryTransactionWrapper(reportVideoAbuse, options, function (err) {
- if (err) return next(err)
-
- return res.type('json').status(204).end()
- })
+ retryTransactionWrapper(reportVideoAbuse, options)
+ .then(() => res.type('json').status(204).end())
+ .catch(err => next(err))
}
-function reportVideoAbuse (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function reportVideoAbuse (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const reporterUsername = res.locals.oauth.token.User.username
reporterPodId: null // This is our pod that reported this abuse
}
- waterfall([
-
- startSerializableTransaction,
-
- function createAbuse (t, callback) {
- db.VideoAbuse.create(abuse).asCallback(function (err, abuse) {
- return callback(err, t, abuse)
- })
- },
-
- function sendToFriendsIfNeeded (t, abuse, callback) {
- // We send the information to the destination pod
- if (videoInstance.isOwned() === false) {
- const reportData = {
- reporterUsername,
- reportReason: abuse.reason,
- videoRemoteId: videoInstance.remoteId
+ 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,
+ videoRemoteId: videoInstance.remoteId
+ }
+
+ return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
}
- friends.reportAbuseVideoToFriend(reportData, videoInstance)
- }
-
- return callback(null, t)
- },
-
- commitTransaction
-
- ], function andFinally (err: Error, t: Sequelize.Transaction) {
- if (err) {
- logger.debug('Cannot update the video.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
-
- logger.info('Abuse report for video %s created.', videoInstance.name)
- return finalCallback(null)
+ return videoInstance
+ })
+ })
+ .then((videoInstance: VideoInstance) => logger.info('Abuse report for video %s created.', videoInstance.name))
+ .catch(err => {
+ logger.debug('Cannot update the video.', { error: err })
+ throw err
})
}
videoId: videoInstance.id
}
- db.BlacklistedVideo.create(toCreate).asCallback(function (err) {
- if (err) {
+ db.BlacklistedVideo.create(toCreate)
+ .then(() => res.type('json').status(204).end())
+ .catch(err => {
logger.error('Errors when blacklisting video ', { error: err })
return next(err)
- }
-
- return res.type('json').status(204).end()
- })
+ })
}
import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import * as fs from 'fs'
+import * as Promise from 'bluebird'
import * as multer from 'multer'
import * as path from 'path'
-import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import {
} from '../../../middlewares'
import {
logger,
- commitTransaction,
retryTransactionWrapper,
- rollbackTransaction,
- startSerializableTransaction,
generateRandomString,
- getFormatedObjects
+ getFormatedObjects,
+ renamePromise
} from '../../../helpers'
+import { TagInstance } from '../../../models'
import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist'
if (file.mimetype === 'video/webm') extension = 'webm'
else if (file.mimetype === 'video/mp4') extension = 'mp4'
else if (file.mimetype === 'video/ogg') extension = 'ogv'
- generateRandomString(16, function (err, randomString) {
- const fieldname = err ? undefined : randomString
- cb(null, fieldname + '.' + extension)
- })
+ generateRandomString(16)
+ .then(randomString => {
+ const filename = randomString
+ cb(null, filename + '.' + extension)
+ })
+ .catch(err => {
+ logger.error('Cannot generate random string for file name.', { error: err })
+ throw err
+ })
}
})
errorMessage: 'Cannot insert the video with many retries.'
}
- retryTransactionWrapper(addVideo, options, function (err) {
- if (err) return next(err)
-
- // TODO : include Location of the new video -> 201
- return res.type('json').status(204).end()
- })
+ retryTransactionWrapper(addVideo, options)
+ .then(() => {
+ // TODO : include Location of the new video -> 201
+ res.type('json').status(204).end()
+ })
+ .catch(err => next(err))
}
-function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File, finalCallback: (err: Error) => void) {
+function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File) {
const videoInfos = req.body
- waterfall([
-
- startSerializableTransaction,
+ return db.sequelize.transaction(t => {
+ const user = res.locals.oauth.token.User
- function findOrCreateAuthor (t, callback) {
- const user = res.locals.oauth.token.User
+ const name = user.username
+ // null because it is OUR pod
+ const podId = null
+ const userId = user.id
- const name = user.username
- // null because it is OUR pod
- const podId = null
- const userId = user.id
+ return db.Author.findOrCreateAuthor(name, podId, userId, t)
+ .then(author => {
+ const tags = videoInfos.tags
+ if (!tags) return { author, tagInstances: undefined }
- db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
- return callback(err, t, authorInstance)
+ return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
})
- },
-
- function findOrCreateTags (t, author, callback) {
- const tags = videoInfos.tags
-
- db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
- return callback(err, t, author, tagInstances)
+ .then(({ author, tagInstances }) => {
+ const videoData = {
+ name: videoInfos.name,
+ remoteId: null,
+ extname: path.extname(videoFile.filename),
+ category: videoInfos.category,
+ licence: videoInfos.licence,
+ language: videoInfos.language,
+ nsfw: videoInfos.nsfw,
+ description: videoInfos.description,
+ duration: videoFile['duration'], // duration was added by a previous middleware
+ authorId: author.id
+ }
+
+ const video = db.Video.build(videoData)
+ return { author, tagInstances, video }
})
- },
-
- function createVideoObject (t, author, tagInstances, callback) {
- const videoData = {
- name: videoInfos.name,
- remoteId: null,
- extname: path.extname(videoFile.filename),
- category: videoInfos.category,
- licence: videoInfos.licence,
- language: videoInfos.language,
- nsfw: videoInfos.nsfw,
- description: videoInfos.description,
- duration: videoFile['duration'], // duration was added by a previous middleware
- authorId: author.id
- }
-
- const video = db.Video.build(videoData)
-
- return callback(null, t, author, tagInstances, video)
- },
-
- // Set the videoname the same as the id
- function renameVideoFile (t, author, tagInstances, video, callback) {
- const videoDir = CONFIG.STORAGE.VIDEOS_DIR
- const source = path.join(videoDir, videoFile.filename)
- const destination = path.join(videoDir, video.getVideoFilename())
-
- fs.rename(source, destination, function (err) {
- if (err) return callback(err)
-
- // This is important in case if there is another attempt
- videoFile.filename = video.getVideoFilename()
- return callback(null, t, author, tagInstances, video)
+ .then(({ author, tagInstances, video }) => {
+ const videoDir = CONFIG.STORAGE.VIDEOS_DIR
+ const source = path.join(videoDir, videoFile.filename)
+ const destination = path.join(videoDir, video.getVideoFilename())
+
+ return renamePromise(source, destination)
+ .then(() => {
+ // This is important in case if there is another attempt in the retry process
+ videoFile.filename = video.getVideoFilename()
+ return { author, tagInstances, video }
+ })
})
- },
-
- function insertVideoIntoDB (t, author, tagInstances, video, callback) {
- const options = { transaction: t }
-
- // Add tags association
- video.save(options).asCallback(function (err, videoCreated) {
- if (err) return callback(err)
+ .then(({ author, tagInstances, video }) => {
+ const options = { transaction: t }
- // Do not forget to add Author informations to the created video
- videoCreated.Author = author
+ return video.save(options)
+ .then(videoCreated => {
+ // Do not forget to add Author informations to the created video
+ videoCreated.Author = author
- return callback(err, t, tagInstances, videoCreated)
+ return { tagInstances, video: videoCreated }
+ })
})
- },
-
- function associateTagsToVideo (t, tagInstances, video, callback) {
- const options = { transaction: t }
+ .then(({ tagInstances, video }) => {
+ if (!tagInstances) return video
- video.setTags(tagInstances, options).asCallback(function (err) {
- video.Tags = tagInstances
-
- return callback(err, t, video)
+ const options = { transaction: t }
+ return video.setTags(tagInstances, options)
+ .then(() => {
+ video.Tags = tagInstances
+ return video
+ })
})
- },
-
- function sendToFriends (t, video, callback) {
- // Let transcoding job send the video to friends because the videofile extension might change
- if (CONFIG.TRANSCODING.ENABLED === true) return callback(null, t)
-
- video.toAddRemoteJSON(function (err, remoteVideo) {
- if (err) return callback(err)
-
- // Now we'll add the video's meta data to our friends
- addVideoToFriends(remoteVideo, t, function (err) {
- return callback(err, t)
- })
+ .then(video => {
+ // Let transcoding job send the video to friends because the videofile extension might change
+ if (CONFIG.TRANSCODING.ENABLED === true) return undefined
+
+ return video.toAddRemoteJSON()
+ .then(remoteVideo => {
+ // Now we'll add the video's meta data to our friends
+ return addVideoToFriends(remoteVideo, t)
+ })
})
- },
-
- commitTransaction
-
- ], function andFinally (err: Error, t: Sequelize.Transaction) {
- if (err) {
- // This is just a debug because we will retry the insert
- logger.debug('Cannot insert the video.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
-
- logger.info('Video with name %s created.', videoInfos.name)
- return finalCallback(null)
+ })
+ .then(() => logger.info('Video with name %s created.', videoInfos.name))
+ .catch((err: Error) => {
+ logger.debug('Cannot insert the video.', { error: err.stack })
+ throw err
})
}
errorMessage: 'Cannot update the video with many retries.'
}
- retryTransactionWrapper(updateVideo, options, function (err) {
- if (err) return next(err)
-
- // TODO : include Location of the new video -> 201
- return res.type('json').status(204).end()
- })
+ retryTransactionWrapper(updateVideo, options)
+ .then(() => {
+ // TODO : include Location of the new video -> 201
+ return res.type('json').status(204).end()
+ })
+ .catch(err => next(err))
}
-function updateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function updateVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const videoFieldsSave = videoInstance.toJSON()
const videoInfosToUpdate = req.body
- waterfall([
-
- startSerializableTransaction,
-
- function findOrCreateTags (t, callback) {
- if (videoInfosToUpdate.tags) {
- db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
- return callback(err, t, tagInstances)
- })
- } else {
- return callback(null, t, null)
- }
- },
-
- function updateVideoIntoDB (t, tagInstances, callback) {
- const options = {
- transaction: t
- }
-
- if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
- if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
- if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
- if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
- if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
- if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
-
- videoInstance.save(options).asCallback(function (err) {
- return callback(err, t, tagInstances)
- })
- },
-
- function associateTagsToVideo (t, tagInstances, callback) {
- if (tagInstances) {
- const options = { transaction: t }
-
- videoInstance.setTags(tagInstances, options).asCallback(function (err) {
- videoInstance.Tags = tagInstances
+ return db.sequelize.transaction(t => {
+ let tagsPromise: Promise<TagInstance[]>
+ if (!videoInfosToUpdate.tags) {
+ tagsPromise = Promise.resolve(null)
+ } else {
+ tagsPromise = db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t)
+ }
- return callback(err, t)
- })
- } else {
- return callback(null, t)
- }
- },
+ return tagsPromise
+ .then(tagInstances => {
+ const options = {
+ transaction: t
+ }
- function sendToFriends (t, callback) {
- const json = videoInstance.toUpdateRemoteJSON()
+ if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
+ if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
+ if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
+ if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
+ if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
+ if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
- // Now we'll update the video's meta data to our friends
- updateVideoToFriends(json, t, function (err) {
- return callback(err, t)
+ return videoInstance.save(options).then(() => tagInstances)
})
- },
-
- commitTransaction
+ .then(tagInstances => {
+ if (!tagInstances) return
- ], function andFinally (err: Error, t: Sequelize.Transaction) {
- if (err) {
- logger.debug('Cannot update the video.', { error: err })
+ const options = { transaction: t }
+ return videoInstance.setTags(tagInstances, options)
+ .then(() => {
+ videoInstance.Tags = tagInstances
- // 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(function (key) {
- const value = videoFieldsSave[key]
- videoInstance.set(key, value)
+ return
+ })
})
+ .then(() => {
+ const json = videoInstance.toUpdateRemoteJSON()
- return rollbackTransaction(err, t, finalCallback)
- }
-
+ // Now we'll update the video's meta data to our friends
+ return updateVideoToFriends(json, t)
+ })
+ })
+ .then(() => {
logger.info('Video with name %s updated.', videoInstance.name)
- return finalCallback(null)
+ })
+ .catch(err => {
+ logger.debug('Cannot update the video.', { error: 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(function (key) {
+ const value = videoFieldsSave[key]
+ videoInstance.set(key, value)
+ })
+
+ throw err
})
}
if (videoInstance.isOwned()) {
// The increment is done directly in the database, not using the instance value
- videoInstance.increment('views').asCallback(function (err) {
- if (err) {
- logger.error('Cannot add view to video %d.', videoInstance.id)
- return
- }
-
- // 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
- }
- quickAndDirtyUpdateVideoToFriends(qaduParams)
- })
+ 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, { error: err }))
} else {
// Just send the event to our friends
const eventParams = {
}
function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
- db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
- if (err) return next(err)
-
- res.json(getFormatedObjects(videosList, videosTotal))
- })
+ db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
+ .then(result => res.json(getFormatedObjects(result.data, result.total)))
+ .catch(err => next(err))
}
function removeVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoInstance = res.locals.video
- videoInstance.destroy().asCallback(function (err) {
- if (err) {
+ videoInstance.destroy()
+ .then(() => res.type('json').status(204).end())
+ .catch(err => {
logger.error('Errors when removed the video.', { error: err })
return next(err)
- }
-
- return res.type('json').status(204).end()
- })
+ })
}
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,
- function (err, videosList, videosTotal) {
- if (err) return next(err)
-
- res.json(getFormatedObjects(videosList, videosTotal))
- }
- )
+ db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
+ .then(result => res.json(getFormatedObjects(result.data, result.total)))
+ .catch(err => next(err))
}
import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import {
logger,
- retryTransactionWrapper,
- startSerializableTransaction,
- commitTransaction,
- rollbackTransaction
+ retryTransactionWrapper
} from '../../../helpers'
import {
VIDEO_RATE_TYPES,
errorMessage: 'Cannot update the user video rate.'
}
- retryTransactionWrapper(rateVideo, options, function (err) {
- if (err) return next(err)
-
- return res.type('json').status(204).end()
- })
+ retryTransactionWrapper(rateVideo, options)
+ .then(() => res.type('json').status(204).end())
+ .catch(err => next(err))
}
-function rateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function rateVideo (req: express.Request, res: express.Response) {
const rateType = req.body.rating
const videoInstance = res.locals.video
const userInstance = res.locals.oauth.token.User
- waterfall([
- startSerializableTransaction,
-
- function findPreviousRate (t, callback) {
- db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) {
- return callback(err, t, previousRate)
- })
- },
+ return db.sequelize.transaction(t => {
+ return db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
+ .then(previousRate => {
+ const options = { transaction: t }
- function insertUserRateIntoDB (t, previousRate, callback) {
- const options = { transaction: 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++
+ // There was a previous rate, update it
+ if (previousRate) {
+ // We will remove the previous rate, so we will need to remove it from the video 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 remove it from the video attribute
- if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
- else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
+ previousRate.type = rateType
- previousRate.type = rateType
+ return previousRate.save(options).then(() => ({ t, likesToIncrement, dislikesToIncrement }))
+ } else { // There was not a previous rate, insert a new one
+ const query = {
+ userId: userInstance.id,
+ videoId: videoInstance.id,
+ type: rateType
+ }
- previousRate.save(options).asCallback(function (err) {
- return callback(err, t, likesToIncrement, dislikesToIncrement)
- })
- } else { // There was not a previous rate, insert a new one
- const query = {
- userId: userInstance.id,
- videoId: videoInstance.id,
- type: rateType
+ return db.UserVideoRate.create(query, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
}
-
- db.UserVideoRate.create(query, options).asCallback(function (err) {
- return callback(err, t, likesToIncrement, dislikesToIncrement)
- })
- }
- },
-
- function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) {
- const options = { transaction: t }
- const incrementQuery = {
- likes: likesToIncrement,
- dislikes: dislikesToIncrement
- }
-
- // Even if we do not own the video we increment the attributes
- // It is usefull for the user to have a feedback
- videoInstance.increment(incrementQuery, options).asCallback(function (err) {
- return callback(err, t, likesToIncrement, dislikesToIncrement)
- })
- },
-
- function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
- // No need for an event type, we own the video
- if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement)
-
- const eventsParams = []
-
- 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
- })
- }
-
- addEventsToRemoteVideo(eventsParams, t, function (err) {
- return callback(err, t, likesToIncrement, dislikesToIncrement)
})
- },
-
- function sendQaduToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
- // 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 callback(null, t)
-
- const qadusParams = []
-
- 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
- })
- }
-
- quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
- return callback(err, t)
+ .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 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 }
+
+ const eventsParams = []
+
+ 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
+ })
+ }
- commitTransaction
+ 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
+
+ const qadusParams = []
+
+ if (likesToIncrement !== 0) {
+ qadusParams.push({
+ videoId: videoInstance.id,
+ type: REQUEST_VIDEO_QADU_TYPES.LIKES
+ })
+ }
- ], function (err: Error, t: Sequelize.Transaction) {
- if (err) {
- // This is just a debug because we will retry the insert
- logger.debug('Cannot add the user video rate.', { error: err })
- return rollbackTransaction(err, t, finalCallback)
- }
+ if (dislikesToIncrement !== 0) {
+ qadusParams.push({
+ videoId: videoInstance.id,
+ type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
+ })
+ }
- logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
- return finalCallback(null)
+ 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.', { error: err })
+ throw err
})
}
-import { parallel } from 'async'
import * as express from 'express'
-import * as fs from 'fs'
import { join } from 'path'
import * as validator from 'validator'
+import * as Promise from 'bluebird'
import { database as db } from '../initializers/database'
import {
STATIC_PATHS,
STATIC_MAX_AGE
} from '../initializers'
-import { root } from '../helpers'
+import { root, readFileBufferPromise } from '../helpers'
import { VideoInstance } from '../models'
const clientsRouter = express.Router()
// Let Angular application handle errors
if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
- parallel({
- file: function (callback) {
- fs.readFile(indexPath, callback)
- },
+ Promise.all([
+ readFileBufferPromise(indexPath),
+ db.Video.loadAndPopulateAuthorAndPodAndTags(videoId)
+ ])
+ .then(([ file, video ]) => {
+ file = file as Buffer
+ video = video as VideoInstance
- video: function (callback) {
- db.Video.loadAndPopulateAuthorAndPodAndTags(videoId, callback)
- }
- }, function (err: Error, result: { file: Buffer, video: VideoInstance }) {
- if (err) return next(err)
-
- const html = result.file.toString()
- const video = result.video
+ const html = file.toString()
// Let Angular application handle errors
if (!video) return res.sendFile(indexPath)
const htmlStringPageWithTags = addOpenGraphTags(html, video)
res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
})
+ .catch(err => next(err))
}
*/
import { join } from 'path'
+import { pseudoRandomBytes } from 'crypto'
+import {
+ readdir,
+ readFile,
+ rename,
+ unlink,
+ writeFile,
+ access
+} from 'fs'
+import * as mkdirp from 'mkdirp'
+import * as bcrypt from 'bcrypt'
+import * as createTorrent from 'create-torrent'
+import * as openssl from 'openssl-wrapper'
+import * as Promise from 'bluebird'
function isTestInstance () {
return process.env.NODE_ENV === 'test'
return join(__dirname, '..', '..', '..')
}
+function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
+ return function promisified (): Promise<A> {
+ return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+ func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
+ })
+ }
+}
+
+// Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
+function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
+ return function promisified (arg: T): Promise<A> {
+ return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+ func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
+ })
+ }
+}
+
+function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
+ return function promisified (arg: T): Promise<void> {
+ return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
+ func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
+ })
+ }
+}
+
+function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
+ return function promisified (arg1: T, arg2: U): Promise<A> {
+ return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+ func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
+ })
+ }
+}
+
+function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
+ return function promisified (arg1: T, arg2: U): Promise<void> {
+ return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
+ func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
+ })
+ }
+}
+
+const readFilePromise = promisify2<string, string, string>(readFile)
+const readFileBufferPromise = promisify1<string, Buffer>(readFile)
+const unlinkPromise = promisify1WithVoid<string>(unlink)
+const renamePromise = promisify2WithVoid<string, string>(rename)
+const writeFilePromise = promisify2<string, any, void>(writeFile)
+const readdirPromise = promisify1<string, string[]>(readdir)
+const mkdirpPromise = promisify1<string, string>(mkdirp)
+const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
+const accessPromise = promisify1WithVoid<string|Buffer>(access)
+const opensslExecPromise = promisify2WithVoid<string, any>(openssl.exec)
+const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
+const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
+const bcryptHashPromise = promisify2<any, string|number, string>(bcrypt.hash)
+const createTorrentPromise = promisify2<string, any, any>(createTorrent)
+
// ---------------------------------------------------------------------------
export {
isTestInstance,
- root
+ root,
+
+ promisify0,
+ promisify1,
+ readdirPromise,
+ readFilePromise,
+ readFileBufferPromise,
+ unlinkPromise,
+ renamePromise,
+ writeFilePromise,
+ mkdirpPromise,
+ pseudoRandomBytesPromise,
+ accessPromise,
+ opensslExecPromise,
+ bcryptComparePromise,
+ bcryptGenSaltPromise,
+ bcryptHashPromise,
+ createTorrentPromise
}
-import * as Sequelize from 'sequelize'
// TODO: import from ES6 when retry typing file will include errorFilter function
import * as retry from 'async/retry'
+import * as Promise from 'bluebird'
-import { database as db } from '../initializers/database'
import { logger } from './logger'
-function commitTransaction (t: Sequelize.Transaction, callback: (err: Error) => void) {
- return t.commit().asCallback(callback)
-}
-
-function rollbackTransaction (err: Error, t: Sequelize.Transaction, callback: (err: Error) => void) {
- // Try to rollback transaction
- if (t) {
- // Do not catch err, report the original one
- t.rollback().asCallback(function () {
- return callback(err)
- })
- } else {
- return callback(err)
- }
-}
-
type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] }
-function retryTransactionWrapper (functionToRetry: Function, options: RetryTransactionWrapperOptions, finalCallback: Function) {
+function retryTransactionWrapper (functionToRetry: (... args) => Promise<any>, options: RetryTransactionWrapperOptions) {
const args = options.arguments ? options.arguments : []
- transactionRetryer(
+ return transactionRetryer(
function (callback) {
- return functionToRetry.apply(this, args.concat([ callback ]))
- },
- function (err) {
- if (err) {
- logger.error(options.errorMessage, { error: err })
- }
-
- // Do not return the error, continue the process
- return finalCallback(null)
+ functionToRetry.apply(this, args)
+ .then(result => callback(null, result))
+ .catch(err => callback(err))
}
)
+ .catch(err => {
+ // Do not throw the error, continue the process
+ logger.error(options.errorMessage, { error: err })
+ })
}
-function transactionRetryer (func: Function, callback: (err: Error) => void) {
- retry({
- times: 5,
-
- errorFilter: function (err) {
- const willRetry = (err.name === 'SequelizeDatabaseError')
- logger.debug('Maybe retrying the transaction function.', { willRetry })
- return willRetry
- }
- }, func, callback)
-}
+function transactionRetryer (func: Function) {
+ return new Promise((res, rej) => {
+ retry({
+ times: 5,
-function startSerializableTransaction (callback: (err: Error, t: Sequelize.Transaction) => void) {
- db.sequelize.transaction(/* { isolationLevel: 'SERIALIZABLE' } */).asCallback(function (err, t) {
- // We force to return only two parameters
- return callback(err, t)
+ errorFilter: function (err) {
+ const willRetry = (err.name === 'SequelizeDatabaseError')
+ logger.debug('Maybe retrying the transaction function.', { willRetry })
+ return willRetry
+ }
+ }, func, function (err) {
+ err ? rej(err) : res()
+ })
})
}
// ---------------------------------------------------------------------------
export {
- commitTransaction,
retryTransactionWrapper,
- rollbackTransaction,
- startSerializableTransaction,
transactionRetryer
}
import * as crypto from 'crypto'
-import * as bcrypt from 'bcrypt'
import * as fs from 'fs'
-import * as openssl from 'openssl-wrapper'
import { join } from 'path'
import {
BCRYPT_SALT_SIZE,
PUBLIC_CERT_NAME
} from '../initializers'
+import {
+ readFilePromise,
+ bcryptComparePromise,
+ bcryptGenSaltPromise,
+ bcryptHashPromise,
+ accessPromise,
+ opensslExecPromise
+} from './core-utils'
import { logger } from './logger'
function checkSignature (publicKey: string, data: string, hexSignature: string) {
return signature
}
-function comparePassword (plainPassword: string, hashPassword: string, callback: (err: Error, match?: boolean) => void) {
- bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
- if (err) return callback(err)
-
- return callback(null, isPasswordMatch)
- })
+function comparePassword (plainPassword: string, hashPassword: string) {
+ return bcryptComparePromise(plainPassword, hashPassword)
}
-function createCertsIfNotExist (callback: (err: Error) => void) {
- certsExist(function (err, exist) {
- if (err) return callback(err)
-
+function createCertsIfNotExist () {
+ return certsExist().then(exist => {
if (exist === true) {
- return callback(null)
+ return undefined
}
- createCerts(function (err) {
- return callback(err)
- })
+ return createCerts()
})
}
-function cryptPassword (password: string, callback: (err: Error, hash?: string) => void) {
- bcrypt.genSalt(BCRYPT_SALT_SIZE, function (err, salt) {
- if (err) return callback(err)
-
- bcrypt.hash(password, salt, function (err, hash) {
- return callback(err, hash)
- })
- })
+function cryptPassword (password: string) {
+ return bcryptGenSaltPromise(BCRYPT_SALT_SIZE).then(salt => bcryptHashPromise(password, salt))
}
-function getMyPrivateCert (callback: (err: Error, privateCert: string) => void) {
+function getMyPrivateCert () {
const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
- fs.readFile(certPath, 'utf8', callback)
+ return readFilePromise(certPath, 'utf8')
}
-function getMyPublicCert (callback: (err: Error, publicCert: string) => void) {
+function getMyPublicCert () {
const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
- fs.readFile(certPath, 'utf8', callback)
+ return readFilePromise(certPath, 'utf8')
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-function certsExist (callback: (err: Error, certsExist: boolean) => void) {
+function certsExist () {
const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
- fs.access(certPath, function (err) {
- // If there is an error the certificates do not exist
- const exists = !err
- return callback(null, exists)
- })
-}
-function createCerts (callback: (err: Error) => void) {
- certsExist(function (err, exist) {
- if (err) return callback(err)
+ // If there is an error the certificates do not exist
+ return accessPromise(certPath)
+ .then(() => true)
+ .catch(() => false)
+}
+function createCerts () {
+ return certsExist().then(exist => {
if (exist === true) {
const errorMessage = 'Certs already exist.'
logger.warning(errorMessage)
- return callback(new Error(errorMessage))
+ throw new Error(errorMessage)
}
logger.info('Generating a RSA key...')
'out': privateCertPath,
'2048': false
}
- openssl.exec('genrsa', genRsaOptions, function (err) {
- if (err) {
- logger.error('Cannot create private key on this pod.')
- return callback(err)
- }
-
- logger.info('RSA key generated.')
- logger.info('Managing public key...')
-
- const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
- const rsaOptions = {
- 'in': privateCertPath,
- 'pubout': true,
- 'out': publicCertPath
- }
- openssl.exec('rsa', rsaOptions, function (err) {
- if (err) {
- logger.error('Cannot create public key on this pod.')
- return callback(err)
+ return opensslExecPromise('genrsa', genRsaOptions)
+ .then(() => {
+ logger.info('RSA key generated.')
+ logger.info('Managing public key...')
+
+ const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
+ const rsaOptions = {
+ 'in': privateCertPath,
+ 'pubout': true,
+ 'out': publicCertPath
}
-
- logger.info('Public key managed.')
- return callback(null)
+ return opensslExecPromise('rsa', rsaOptions)
+ .then(() => logger.info('Public key managed.'))
+ .catch(err => {
+ logger.error('Cannot create public key on this pod.')
+ throw err
+ })
+ })
+ .catch(err => {
+ logger.error('Cannot create private key on this pod.')
+ throw err
})
- })
})
}
import * as replay from 'request-replay'
import * as request from 'request'
+import * as Promise from 'bluebird'
import {
RETRY_REQUESTS,
method: 'GET'|'POST',
json: Object
}
-function makeRetryRequest (params: MakeRetryRequestParams, callback: request.RequestCallback) {
- replay(
- request(params, callback),
- {
- retries: RETRY_REQUESTS,
- factor: 3,
- maxTimeout: Infinity,
- errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
- }
- )
+function makeRetryRequest (params: MakeRetryRequestParams) {
+ return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
+ replay(
+ request(params, (err, response, body) => err ? rej(err) : res({ response, body })),
+ {
+ retries: RETRY_REQUESTS,
+ factor: 3,
+ maxTimeout: Infinity,
+ errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
+ }
+ )
+ })
}
type MakeSecureRequestParams = {
sign: boolean
data?: Object
}
-function makeSecureRequest (params: MakeSecureRequestParams, callback: request.RequestCallback) {
- const requestParams = {
- url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
- json: {}
- }
+function makeSecureRequest (params: MakeSecureRequestParams) {
+ return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
+ const requestParams = {
+ url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
+ json: {}
+ }
- if (params.method !== 'POST') {
- return callback(new Error('Cannot make a secure request with a non POST method.'), null, null)
- }
+ if (params.method !== 'POST') {
+ return rej(new Error('Cannot make a secure request with a non POST method.'))
+ }
- // Add signature if it is specified in the params
- if (params.sign === true) {
- const host = CONFIG.WEBSERVER.HOST
+ // Add signature if it is specified in the params
+ if (params.sign === true) {
+ const host = CONFIG.WEBSERVER.HOST
- let dataToSign
- if (params.data) {
- dataToSign = params.data
- } else {
- // We do not have data to sign so we just take our host
- // It is not ideal but the connection should be in HTTPS
- dataToSign = host
- }
+ let dataToSign
+ if (params.data) {
+ dataToSign = params.data
+ } else {
+ // We do not have data to sign so we just take our host
+ // It is not ideal but the connection should be in HTTPS
+ dataToSign = host
+ }
- requestParams.json['signature'] = {
- host, // Which host we pretend to be
- signature: sign(dataToSign)
+ requestParams.json['signature'] = {
+ host, // Which host we pretend to be
+ signature: sign(dataToSign)
+ }
}
- }
- // If there are data informations
- if (params.data) {
- requestParams.json['data'] = params.data
- }
+ // If there are data informations
+ if (params.data) {
+ requestParams.json['data'] = params.data
+ }
- request.post(requestParams, callback)
+ request.post(requestParams, (err, response, body) => err ? rej(err) : res({ response, body }))
+ })
}
// ---------------------------------------------------------------------------
import * as express from 'express'
-import { pseudoRandomBytes } from 'crypto'
-
-import { logger } from './logger'
+import { pseudoRandomBytesPromise } from './core-utils'
+import { ResultList } from '../../shared'
function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
res.type('json').status(400).end()
}
-function generateRandomString (size: number, callback: (err: Error, randomString?: string) => void) {
- pseudoRandomBytes(size, function (err, raw) {
- if (err) return callback(err)
-
- callback(null, raw.toString('hex'))
- })
-}
-
-function createEmptyCallback () {
- return function (err) {
- if (err) logger.error('Error in empty callback.', { error: err })
- }
+function generateRandomString (size: number) {
+ return pseudoRandomBytesPromise(size).then(raw => raw.toString('hex'))
}
interface FormatableToJSON {
formatedObjects.push(object.toFormatedJSON())
})
- return {
+ const res: ResultList<U> = {
total: objectsTotal,
data: formatedObjects
}
+
+ return res
}
// ---------------------------------------------------------------------------
export {
badRequest,
- createEmptyCallback,
generateRandomString,
getFormatedObjects
}
import { database as db } from './database'
import { CONFIG } from './constants'
+import { promisify0 } from '../helpers/core-utils'
// Some checks on configuration files
function checkConfig () {
}
// Check the available codecs
-function checkFFmpeg (callback: (err: Error) => void) {
+function checkFFmpeg () {
const Ffmpeg = require('fluent-ffmpeg')
-
- Ffmpeg.getAvailableCodecs(function (err, codecs) {
- if (err) return callback(err)
- if (CONFIG.TRANSCODING.ENABLED === false) return callback(null)
-
- const canEncode = [ 'libx264' ]
- canEncode.forEach(function (codec) {
- if (codecs[codec] === undefined) {
- return callback(new Error('Unknown codec ' + codec + ' in FFmpeg.'))
- }
-
- if (codecs[codec].canEncode !== true) {
- return callback(new Error('Unavailable encode codec ' + codec + ' in FFmpeg'))
- }
+ const getAvailableCodecsPromise = promisify0(Ffmpeg.getAvailableCodecs)
+
+ getAvailableCodecsPromise()
+ .then(codecs => {
+ if (CONFIG.TRANSCODING.ENABLED === false) return undefined
+
+ const canEncode = [ 'libx264' ]
+ canEncode.forEach(function (codec) {
+ if (codecs[codec] === undefined) {
+ throw new Error('Unknown codec ' + codec + ' in FFmpeg.')
+ }
+
+ if (codecs[codec].canEncode !== true) {
+ throw new Error('Unavailable encode codec ' + codec + ' in FFmpeg')
+ }
+ })
})
-
- return callback(null)
- })
}
-function clientsExist (callback: (err: Error, clientsExist?: boolean) => void) {
- db.OAuthClient.countTotal(function (err, totalClients) {
- if (err) return callback(err)
-
- return callback(null, totalClients !== 0)
+function clientsExist () {
+ return db.OAuthClient.countTotal().then(totalClients => {
+ return totalClients !== 0
})
}
-function usersExist (callback: (err: Error, usersExist?: boolean) => void) {
- db.User.countTotal(function (err, totalUsers) {
- if (err) return callback(err)
-
- return callback(null, totalUsers !== 0)
+function usersExist () {
+ return db.User.countTotal().then(totalUsers => {
+ return totalUsers !== 0
})
}
-import * as fs from 'fs'
import { join } from 'path'
+import { flattenDepth } from 'lodash'
import * as Sequelize from 'sequelize'
-import { each } from 'async'
+import * as Promise from 'bluebird'
import { CONFIG } from './constants'
// Do not use barrel, we need to load database first
import { logger } from '../helpers/logger'
-import { isTestInstance } from '../helpers/core-utils'
+import { isTestInstance, readdirPromise } from '../helpers/core-utils'
import {
ApplicationModel,
AuthorModel,
const database: {
sequelize?: Sequelize.Sequelize,
- init?: (silent: any, callback: any) => void,
+ init?: (silent: boolean) => Promise<void>,
Application?: ApplicationModel,
Author?: AuthorModel,
database.sequelize = sequelize
-database.init = function (silent: boolean, callback: (err: Error) => void) {
+database.init = function (silent: boolean) {
const modelDirectory = join(__dirname, '..', 'models')
- getModelFiles(modelDirectory, function (err, filePaths) {
- if (err) throw err
-
- filePaths.forEach(function (filePath) {
+ return getModelFiles(modelDirectory).then(filePaths => {
+ filePaths.forEach(filePath => {
const model = sequelize.import(filePath)
database[model['name']] = model
})
- Object.keys(database).forEach(function (modelName) {
+ Object.keys(database).forEach(modelName => {
if ('associate' in database[modelName]) {
database[modelName].associate(database)
}
if (!silent) logger.info('Database %s is ready.', dbname)
- return callback(null)
+ return undefined
})
}
// ---------------------------------------------------------------------------
-function getModelFiles (modelDirectory: string, callback: (err: Error, filePaths: string[]) => void) {
- fs.readdir(modelDirectory, function (err, files) {
- if (err) throw err
-
- const directories = files.filter(function (directory) {
- // Find directories
- if (
- directory.endsWith('.js.map') ||
- directory === 'index.js' || directory === 'index.ts' ||
- directory === 'utils.js' || directory === 'utils.ts'
- ) return false
+function getModelFiles (modelDirectory: string) {
+ return readdirPromise(modelDirectory)
+ .then(files => {
+ const directories: string[] = files.filter(function (directory) {
+ // Find directories
+ if (
+ directory.endsWith('.js.map') ||
+ directory === 'index.js' || directory === 'index.ts' ||
+ directory === 'utils.js' || directory === 'utils.ts'
+ ) return false
+
+ return true
+ })
- return true
+ return directories
})
-
- let modelFilePaths: string[] = []
-
- // For each directory we read it and append model in the modelFilePaths array
- each(directories, function (directory: string, eachCallback: ErrorCallback<Error>) {
- const modelDirectoryPath = join(modelDirectory, directory)
-
- fs.readdir(modelDirectoryPath, function (err, files) {
- if (err) return eachCallback(err)
-
- const filteredFiles = files.filter(file => {
- if (
- file === 'index.js' || file === 'index.ts' ||
- file === 'utils.js' || file === 'utils.ts' ||
- file.endsWith('-interface.js') || file.endsWith('-interface.ts') ||
- file.endsWith('.js.map')
- ) return false
-
- return true
- }).map(file => {
- return join(modelDirectoryPath, file)
+ .then(directories => {
+ const tasks = []
+
+ // For each directory we read it and append model in the modelFilePaths array
+ directories.forEach(directory => {
+ const modelDirectoryPath = join(modelDirectory, directory)
+
+ const promise = readdirPromise(modelDirectoryPath).then(files => {
+ const filteredFiles = files.filter(file => {
+ if (
+ file === 'index.js' || file === 'index.ts' ||
+ file === 'utils.js' || file === 'utils.ts' ||
+ file.endsWith('-interface.js') || file.endsWith('-interface.ts') ||
+ file.endsWith('.js.map')
+ ) return false
+
+ return true
+ }).map(file => join(modelDirectoryPath, file))
+
+ return filteredFiles
})
- modelFilePaths = modelFilePaths.concat(filteredFiles)
-
- return eachCallback(null)
+ tasks.push(promise)
})
- }, function (err: Error) {
- return callback(err, modelFilePaths)
+
+ return Promise.all(tasks)
+ })
+ .then((filteredFiles: string[][]) => {
+ return flattenDepth<string>(filteredFiles, 1)
})
- })
}
import { join } from 'path'
import * as config from 'config'
-import { each, series } from 'async'
-import * as mkdirp from 'mkdirp'
import * as passwordGenerator from 'password-generator'
+import * as Promise from 'bluebird'
import { database as db } from './database'
import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION } from './constants'
import { clientsExist, usersExist } from './checker'
-import { logger, createCertsIfNotExist, root } from '../helpers'
-
-function installApplication (callback: (err: Error) => void) {
- series([
- function createDatabase (callbackAsync) {
- db.sequelize.sync().asCallback(callbackAsync)
- // db.sequelize.sync({ force: true }).asCallback(callbackAsync)
- },
-
- function createDirectories (callbackAsync) {
- createDirectoriesIfNotExist(callbackAsync)
- },
-
- function createCertificates (callbackAsync) {
- createCertsIfNotExist(callbackAsync)
- },
-
- function createOAuthClient (callbackAsync) {
- createOAuthClientIfNotExist(callbackAsync)
- },
-
- function createOAuthUser (callbackAsync) {
- createOAuthAdminIfNotExist(callbackAsync)
- }
- ], callback)
+import { logger, createCertsIfNotExist, root, mkdirpPromise } from '../helpers'
+
+function installApplication () {
+ return db.sequelize.sync()
+ .then(() => createDirectoriesIfNotExist())
+ .then(() => createCertsIfNotExist())
+ .then(() => createOAuthClientIfNotExist())
+ .then(() => createOAuthAdminIfNotExist())
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-function createDirectoriesIfNotExist (callback: (err: Error) => void) {
+function createDirectoriesIfNotExist () {
const storages = config.get('storage')
- each(Object.keys(storages), function (key, callbackEach) {
+ const tasks = []
+ Object.keys(storages).forEach(key => {
const dir = storages[key]
- mkdirp(join(root(), dir), callbackEach)
- }, callback)
-}
+ tasks.push(mkdirpPromise(join(root(), dir)))
+ })
-function createOAuthClientIfNotExist (callback: (err: Error) => void) {
- clientsExist(function (err, exist) {
- if (err) return callback(err)
+ return Promise.all(tasks)
+}
+function createOAuthClientIfNotExist () {
+ return clientsExist().then(exist => {
// Nothing to do, clients already exist
- if (exist === true) return callback(null)
+ if (exist === true) return undefined
logger.info('Creating a default OAuth Client.')
redirectUris: null
})
- client.save().asCallback(function (err, createdClient) {
- if (err) return callback(err)
-
+ return client.save().then(createdClient => {
logger.info('Client id: ' + createdClient.clientId)
logger.info('Client secret: ' + createdClient.clientSecret)
- return callback(null)
+ return undefined
})
})
}
-function createOAuthAdminIfNotExist (callback: (err: Error) => void) {
- usersExist(function (err, exist) {
- if (err) return callback(err)
-
+function createOAuthAdminIfNotExist () {
+ return usersExist().then(exist => {
// Nothing to do, users already exist
- if (exist === true) return callback(null)
+ if (exist === true) return undefined
logger.info('Creating the administrator.')
role
}
- db.User.create(userData, createOptions).asCallback(function (err, createdUser) {
- if (err) return callback(err)
-
+ return db.User.create(userData, createOptions).then(createdUser => {
logger.info('Username: ' + username)
logger.info('User password: ' + password)
logger.info('Creating Application table.')
- db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION }).asCallback(callback)
+ return db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
})
})
}
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.STRING(400),
defaultValue: ''
}
- waterfall([
-
- function addEmailColumn (callback) {
- q.addColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function updateWithFakeEmails (callback) {
+ return q.addColumn('Pods', 'email', data)
+ .then(() => {
const query = 'UPDATE "Pods" SET "email" = \'dummy@example.com\''
- utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function nullOnDefault (callback) {
+ return utils.sequelize.query(query, { transaction: utils.transaction })
+ })
+ .then(() => {
data.defaultValue = null
- q.changeColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(callback)
- }
- ], finalCallback)
+ return q.changeColumn('Pods', 'email', data)
+ })
}
function down (options, callback) {
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.STRING(400),
allowNull: false,
defaultValue: ''
}
-
- waterfall([
-
- function addEmailColumn (callback) {
- q.addColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function updateWithFakeEmails (callback) {
+ return q.addColumn('Users', 'email', data)
+ .then(() => {
const query = 'UPDATE "Users" SET "email" = CONCAT("username", \'@example.com\')'
- utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function nullOnDefault (callback) {
+ return utils.sequelize.query(query, { transaction: utils.transaction })
+ })
+ .then(() => {
data.defaultValue = null
- q.changeColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(callback)
- }
- ], finalCallback)
+ return q.changeColumn('Users', 'email', data)
+ })
}
function down (options, callback) {
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: 0
}
- q.addColumn('Videos', 'views', data, { transaction: utils.transaction }).asCallback(finalCallback)
+ return q.addColumn('Videos', 'views', data)
}
function down (options, callback) {
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: 0
}
- q.addColumn('Videos', 'likes', data, { transaction: utils.transaction }).asCallback(finalCallback)
+ return q.addColumn('Videos', 'likes', data)
}
function down (options, callback) {
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: 0
}
- q.addColumn('Videos', 'dislikes', data, { transaction: utils.transaction }).asCallback(finalCallback)
+ return q.addColumn('Videos', 'dislikes', data)
}
function down (options, callback) {
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: 0
}
- waterfall([
-
- function addCategoryColumn (callback) {
- q.addColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function nullOnDefault (callback) {
+ return q.addColumn('Videos', 'category', data)
+ .then(() => {
data.defaultValue = null
- q.changeColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(callback)
- }
- ], finalCallback)
+ return q.changeColumn('Videos', 'category', data)
+ })
}
function down (options, callback) {
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: 0
}
- waterfall([
-
- function addLicenceColumn (callback) {
- q.addColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function nullOnDefault (callback) {
+ return q.addColumn('Videos', 'licence', data)
+ .then(() => {
data.defaultValue = null
-
- q.changeColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(callback)
- }
- ], finalCallback)
+ return q.changeColumn('Videos', 'licence', data)
+ })
}
function down (options, callback) {
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.BOOLEAN,
defaultValue: false
}
- waterfall([
-
- function addNSFWColumn (callback) {
- q.addColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(function (err) {
- return callback(err)
- })
- },
-
- function nullOnDefault (callback) {
+ return q.addColumn('Videos', 'nsfw', data)
+ .then(() => {
data.defaultValue = null
- q.changeColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(callback)
- }
- ], finalCallback)
+ return q.changeColumn('Videos', 'nsfw', data)
+ })
}
function down (options, callback) {
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.BOOLEAN,
defaultValue: false
}
- q.addColumn('Users', 'displayNSFW', data, { transaction: utils.transaction }).asCallback(finalCallback)
+ return q.addColumn('Users', 'displayNSFW', data)
}
function down (options, callback) {
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
const q = utils.queryInterface
- const Sequelize = utils.Sequelize
const data = {
type: Sequelize.INTEGER,
defaultValue: null
}
- q.addColumn('Videos', 'language', data, { transaction: utils.transaction }).asCallback(finalCallback)
+ return q.addColumn('Videos', 'language', data)
}
function down (options, callback) {
-import { waterfall, eachSeries } from 'async'
-import * as fs from 'fs'
import * as path from 'path'
-import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { database as db } from './database'
import { LAST_MIGRATION_VERSION } from './constants'
-import { logger } from '../helpers'
-
-function migrate (finalCallback: (err: Error) => void) {
- waterfall([
-
- function checkApplicationTableExists (callback) {
- db.sequelize.getQueryInterface().showAllTables().asCallback(function (err, tables) {
- if (err) return callback(err)
-
- // No tables, we don't need to migrate anything
- // The installer will do that
- if (tables.length === 0) return finalCallback(null)
-
- return callback(null)
- })
- },
-
- function loadMigrationVersion (callback) {
- db.Application.loadMigrationVersion(callback)
- },
-
- function createMigrationRowIfNotExists (actualVersion, callback) {
+import { logger, readdirPromise } from '../helpers'
+
+function migrate () {
+ const p = db.sequelize.getQueryInterface().showAllTables()
+ .then(tables => {
+ // No tables, we don't need to migrate anything
+ // The installer will do that
+ if (tables.length === 0) throw null
+ })
+ .then(() => {
+ return db.Application.loadMigrationVersion()
+ })
+ .then(actualVersion => {
if (actualVersion === null) {
- db.Application.create({
- migrationVersion: 0
- }, function (err) {
- return callback(err, 0)
- })
+ return db.Application.create({ migrationVersion: 0 }).then(() => 0)
}
- return callback(null, actualVersion)
- },
-
- function abortMigrationIfNotNeeded (actualVersion, callback) {
- // No need migrations
- if (actualVersion >= LAST_MIGRATION_VERSION) return finalCallback(null)
-
- return callback(null, actualVersion)
- },
+ return actualVersion
+ })
+ .then(actualVersion => {
+ // No need migrations, abort
+ if (actualVersion >= LAST_MIGRATION_VERSION) throw null
- function getMigrations (actualVersion, callback) {
+ return actualVersion
+ })
+ .then(actualVersion => {
// If there are a new migration scripts
logger.info('Begin migrations.')
- getMigrationScripts(function (err, migrationScripts) {
- return callback(err, actualVersion, migrationScripts)
+ return getMigrationScripts().then(migrationScripts => ({ actualVersion, migrationScripts }))
+ })
+ .then(({ actualVersion, migrationScripts }) => {
+ return Promise.mapSeries(migrationScripts, entity => {
+ return executeMigration(actualVersion, entity)
})
- },
+ })
+ .then(() => {
+ logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
+ })
+ .catch(err => {
+ if (err === null) return undefined
- function doMigrations (actualVersion, migrationScripts, callback) {
- eachSeries(migrationScripts, function (entity: any, callbackEach) {
- executeMigration(actualVersion, entity, callbackEach)
- }, function (err) {
- if (err) return callback(err)
+ throw err
+ })
- logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
- return callback(null)
- })
- }
- ], finalCallback)
+ return p
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-type GetMigrationScriptsCallback = (err: Error, filesToMigrate?: { version: string, script: string }[]) => void
-function getMigrationScripts (callback: GetMigrationScriptsCallback) {
- fs.readdir(path.join(__dirname, 'migrations'), function (err, files) {
- if (err) return callback(err)
-
- const filesToMigrate = []
+function getMigrationScripts () {
+ return readdirPromise(path.join(__dirname, 'migrations')).then(files => {
+ const filesToMigrate: {
+ version: string,
+ script: string
+ }[] = []
files.forEach(function (file) {
// Filename is something like 'version-blabla.js'
})
})
- return callback(err, filesToMigrate)
+ return filesToMigrate
})
}
-function executeMigration (actualVersion: number, entity: { version: string, script: string }, callback: (err: Error) => void) {
+function executeMigration (actualVersion: number, entity: { version: string, script: string }) {
const versionScript = parseInt(entity.version, 10)
// Do not execute old migration scripts
- if (versionScript <= actualVersion) return callback(null)
+ if (versionScript <= actualVersion) return undefined
// Load the migration module and run it
const migrationScriptName = entity.script
const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
- db.sequelize.transaction().asCallback(function (err, t) {
- if (err) return callback(err)
-
+ return db.sequelize.transaction(t => {
const options = {
transaction: t,
queryInterface: db.sequelize.getQueryInterface(),
- sequelize: db.sequelize,
- Sequelize: Sequelize
+ sequelize: db.sequelize
}
- migrationScript.up(options, function (err) {
- if (err) {
- t.rollback()
- return callback(err)
- }
-
- // Update the new migration version
- db.Application.updateMigrationVersion(versionScript, t, function (err) {
- if (err) {
- t.rollback()
- return callback(err)
- }
- t.commit().asCallback(callback)
+ migrationScript.up(options)
+ .then(() => {
+ // Update the new migration version
+ db.Application.updateMigrationVersion(versionScript, t)
})
- })
})
}
-import { each, eachLimit, eachSeries, series, waterfall } from 'async'
import * as request from 'request'
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { database as db } from '../initializers/database'
import {
logger,
getMyPublicCert,
makeSecureRequest,
- makeRetryRequest,
- createEmptyCallback
+ makeRetryRequest
} from '../helpers'
import {
RequestScheduler,
requestVideoEventScheduler.activate()
}
-function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction, callback: (err: Error) => void) {
+function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.ADD,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoData,
transaction
}
- createRequest(options, callback)
+ return createRequest(options)
}
-function updateVideoToFriends (videoData: Object, transaction: Sequelize.Transaction, callback: (err: Error) => void) {
+function updateVideoToFriends (videoData: Object, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.UPDATE,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoData,
transaction
}
- createRequest(options, callback)
+ return createRequest(options)
}
function removeVideoToFriends (videoParams: Object) {
data: videoParams,
transaction: null
}
- createRequest(options)
+ return createRequest(options)
}
-function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance) {
+function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.REPORT_ABUSE,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: reportData,
toIds: [ video.Author.podId ],
- transaction: null
+ transaction
}
- createRequest(options)
+ return createRequest(options)
}
-function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction, callback?: (err: Error) => void) {
+function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction) {
const options = {
videoId: qaduParam.videoId,
type: qaduParam.type,
transaction
}
- return createVideoQaduRequest(options, callback)
+ return createVideoQaduRequest(options)
}
-function quickAndDirtyUpdatesVideoToFriends (
- qadusParams: QaduParam[],
- transaction: Sequelize.Transaction,
- finalCallback: (err: Error) => void
-) {
+function quickAndDirtyUpdatesVideoToFriends (qadusParams: QaduParam[], transaction: Sequelize.Transaction) {
const tasks = []
qadusParams.forEach(function (qaduParams) {
- const fun = function (callback) {
- quickAndDirtyUpdateVideoToFriends(qaduParams, transaction, callback)
- }
-
- tasks.push(fun)
+ tasks.push(quickAndDirtyUpdateVideoToFriends(qaduParams, transaction))
})
- series(tasks, finalCallback)
+ return Promise.all(tasks)
}
-function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction, callback?: (err: Error) => void) {
+function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction) {
const options = {
videoId: eventParam.videoId,
type: eventParam.type,
transaction
}
- createVideoEventRequest(options, callback)
+ return createVideoEventRequest(options)
}
-function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction, finalCallback: (err: Error) => void) {
+function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction) {
const tasks = []
eventsParams.forEach(function (eventParams) {
- const fun = function (callback) {
- addEventToRemoteVideo(eventParams, transaction, callback)
- }
-
- tasks.push(fun)
+ tasks.push(addEventToRemoteVideo(eventParams, transaction))
})
- series(tasks, finalCallback)
+ return Promise.all(tasks)
}
-function hasFriends (callback: (err: Error, hasFriends?: boolean) => void) {
- db.Pod.countAll(function (err, count) {
- if (err) return callback(err)
-
- const hasFriends = (count !== 0)
- callback(null, hasFriends)
- })
+function hasFriends () {
+ return db.Pod.countAll().then(count => count !== 0)
}
-function makeFriends (hosts: string[], callback: (err: Error) => void) {
+function makeFriends (hosts: string[]) {
const podsScore = {}
logger.info('Make friends!')
- getMyPublicCert(function (err, cert) {
- if (err) {
- logger.error('Cannot read public cert.')
- return callback(err)
- }
-
- eachSeries(hosts, function (host, callbackEach) {
- computeForeignPodsList(host, podsScore, callbackEach)
- }, function (err: Error) {
- if (err) return callback(err)
-
+ return getMyPublicCert()
+ .then(cert => {
+ return Promise.mapSeries(hosts, host => {
+ return computeForeignPodsList(host, podsScore)
+ }).then(() => cert)
+ })
+ .then(cert => {
logger.debug('Pods scores computed.', { podsScore: podsScore })
const podsList = computeWinningPods(hosts, podsScore)
logger.debug('Pods that we keep.', { podsToKeep: podsList })
- makeRequestsToWinningPods(cert, podsList, callback)
+ return makeRequestsToWinningPods(cert, podsList)
})
- })
}
-function quitFriends (callback: (err: Error) => void) {
+function quitFriends () {
// Stop pool requests
requestScheduler.deactivate()
- waterfall([
- function flushRequests (callbackAsync) {
- requestScheduler.flush(err => callbackAsync(err))
- },
-
- function flushVideoQaduRequests (callbackAsync) {
- requestVideoQaduScheduler.flush(err => callbackAsync(err))
- },
-
- function getPodsList (callbackAsync) {
- return db.Pod.list(callbackAsync)
- },
-
- function announceIQuitMyFriends (pods, callbackAsync) {
+ return requestScheduler.flush()
+ .then(() => {
+ return requestVideoQaduScheduler.flush()
+ })
+ .then(() => {
+ return db.Pod.list()
+ })
+ .then(pods => {
const requestParams = {
method: 'POST' as 'POST',
path: '/api/' + API_VERSION + '/remote/pods/remove',
// Announce we quit them
// We don't care if the request fails
// The other pod will exclude us automatically after a while
- eachLimit(pods, REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
+ return Promise.map(pods, pod => {
requestParams.toPod = pod
- makeSecureRequest(requestParams, callbackEach)
- }, function (err) {
- if (err) {
- logger.error('Some errors while quitting friends.', { err: err })
- // Don't stop the process
- }
-
- return callbackAsync(null, pods)
+ return makeSecureRequest(requestParams)
+ }, { concurrency: REQUESTS_IN_PARALLEL })
+ .then(() => pods)
+ .catch(err => {
+ logger.error('Some errors while quitting friends.', { err: err })
+ // Don't stop the process
})
- },
-
- function removePodsFromDB (pods, callbackAsync) {
- each(pods, function (pod: any, callbackEach) {
- pod.destroy().asCallback(callbackEach)
- }, callbackAsync)
- }
- ], function (err: Error) {
- // Don't forget to re activate the scheduler, even if there was an error
- requestScheduler.activate()
-
- if (err) return callback(err)
+ })
+ .then(pods => {
+ const tasks = []
+ pods.forEach(pod => tasks.push(pod.destroy()))
- logger.info('Removed all remote videos.')
- return callback(null)
- })
+ return Promise.all(pods)
+ })
+ .then(() => {
+ logger.info('Removed all remote videos.')
+ // Don't forget to re activate the scheduler, even if there was an error
+ return requestScheduler.activate()
+ })
+ .finally(() => requestScheduler.activate())
}
function sendOwnedVideosToPod (podId: number) {
- db.Video.listOwnedAndPopulateAuthorAndTags(function (err, videosList) {
- if (err) {
- logger.error('Cannot get the list of videos we own.')
- return
- }
-
- videosList.forEach(function (video) {
- video.toAddRemoteJSON(function (err, remoteVideo) {
- if (err) {
- logger.error('Cannot convert video to remote.', { error: err })
- // Don't break the process
- return
- }
-
- const options = {
- type: 'add',
- endpoint: REQUEST_ENDPOINTS.VIDEOS,
- data: remoteVideo,
- toIds: [ podId ],
- transaction: null
- }
- createRequest(options)
+ db.Video.listOwnedAndPopulateAuthorAndTags()
+ .then(videosList => {
+ const tasks = []
+ videosList.forEach(video => {
+ const promise = video.toAddRemoteJSON()
+ .then(remoteVideo => {
+ const options = {
+ type: 'add',
+ endpoint: REQUEST_ENDPOINTS.VIDEOS,
+ data: remoteVideo,
+ toIds: [ podId ],
+ transaction: null
+ }
+ return createRequest(options)
+ })
+ .catch(err => {
+ logger.error('Cannot convert video to remote.', { error: err })
+ // Don't break the process
+ return undefined
+ })
+
+ tasks.push(promise)
})
+
+ return Promise.all(tasks)
})
- })
}
function getRequestScheduler () {
// ---------------------------------------------------------------------------
-function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }, callback: (err: Error) => void) {
- getForeignPodsList(host, function (err, res) {
- if (err) return callback(err)
-
+function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }) {
+ // TODO: type res
+ return getForeignPodsList(host).then((res: any) => {
const foreignPodsList = res.data
// Let's give 1 point to the pod we ask the friends list
foreignPodsList.push({ host })
- foreignPodsList.forEach(function (foreignPod) {
+ foreignPodsList.forEach(foreignPod => {
const foreignPodHost = foreignPod.host
if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
else podsScore[foreignPodHost] = 1
})
- return callback(null)
+ return undefined
})
}
const podsList = []
const baseScore = hosts.length / 2
- Object.keys(podsScore).forEach(function (podHost) {
+ Object.keys(podsScore).forEach(podHost => {
// If the pod is not me and with a good score we add it
if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
podsList.push({ host: podHost })
return podsList
}
-function getForeignPodsList (host: string, callback: (err: Error, foreignPodsList?: any) => void) {
- const path = '/api/' + API_VERSION + '/pods'
+function getForeignPodsList (host: string) {
+ return new Promise((res, rej) => {
+ const path = '/api/' + API_VERSION + '/pods'
- request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
- if (err) return callback(err)
+ request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
+ if (err) return rej(err)
- try {
- const json = JSON.parse(body)
- return callback(null, json)
- } catch (err) {
- return callback(err)
- }
+ try {
+ const json = JSON.parse(body)
+ return res(json)
+ } catch (err) {
+ return rej(err)
+ }
+ })
})
}
-function makeRequestsToWinningPods (cert: string, podsList: PodInstance[], callback: (err: Error) => void) {
+function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
// Stop pool requests
requestScheduler.deactivate()
// Flush pool requests
requestScheduler.forceSend()
- eachLimit(podsList, REQUESTS_IN_PARALLEL, function (pod: PodInstance, callbackEach) {
+ return Promise.map(podsList, pod => {
const params = {
url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/pods/',
method: 'POST' as 'POST',
}
}
- makeRetryRequest(params, function (err, res, body: { cert: string, email: string }) {
- if (err) {
- logger.error('Error with adding %s pod.', pod.host, { error: err })
+ return makeRetryRequest(params)
+ .then(({ response, body }) => {
+ body = body as { cert: string, email: string }
+
+ if (response.statusCode === 200) {
+ const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
+ return podObj.save()
+ .then(podCreated => {
+
+ // Add our videos to the request scheduler
+ sendOwnedVideosToPod(podCreated.id)
+ })
+ .catch(err => {
+ logger.error('Cannot add friend %s pod.', pod.host, { error: err })
+ })
+ } else {
+ logger.error('Status not 200 for %s pod.', pod.host)
+ }
+ })
+ .catch(err => {
+ logger.error('Error with adding %s pod.', pod.host, { error: err.stack })
// Don't break the process
- return callbackEach()
- }
-
- if (res.statusCode === 200) {
- const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
- podObj.save().asCallback(function (err, podCreated) {
- if (err) {
- logger.error('Cannot add friend %s pod.', pod.host, { error: err })
- return callbackEach()
- }
-
- // Add our videos to the request scheduler
- sendOwnedVideosToPod(podCreated.id)
-
- return callbackEach()
- })
- } else {
- logger.error('Status not 200 for %s pod.', pod.host)
- return callbackEach()
- }
- })
- }, function endRequests () {
+ })
+ }, { concurrency: REQUESTS_IN_PARALLEL })
+ .then(() => logger.debug('makeRequestsToWinningPods finished.'))
+ .finally(() => {
// Final callback, we've ended all the requests
// Now we made new friends, we can re activate the pool of requests
requestScheduler.activate()
-
- logger.debug('makeRequestsToWinningPods finished.')
- return callback(null)
})
}
toIds?: number[]
transaction: Sequelize.Transaction
}
-function createRequest (options: CreateRequestOptions, callback?: (err: Error) => void) {
- if (!callback) callback = function () { /* empty */ }
-
- if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions, callback)
+function createRequest (options: CreateRequestOptions) {
+ if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions)
// If the "toIds" pods is not specified, we send the request to all our friends
- db.Pod.listAllIds(options.transaction, function (err, podIds) {
- if (err) {
- logger.error('Cannot get pod ids', { error: err })
- return
- }
-
+ return db.Pod.listAllIds(options.transaction).then(podIds => {
const newOptions = Object.assign(options, { toIds: podIds })
- return requestScheduler.createRequest(newOptions, callback)
+ return requestScheduler.createRequest(newOptions)
})
}
-function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions, callback: (err: Error) => void) {
- if (!callback) callback = createEmptyCallback()
-
- requestVideoQaduScheduler.createRequest(options, callback)
+function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions) {
+ return requestVideoQaduScheduler.createRequest(options)
}
-function createVideoEventRequest (options: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
- if (!callback) callback = createEmptyCallback()
-
- requestVideoEventScheduler.createRequest(options, callback)
+function createVideoEventRequest (options: RequestVideoEventSchedulerOptions) {
+ return requestVideoEventScheduler.createRequest(options)
}
function isMe (host: string) {
import * as videoTranscoder from './video-transcoder'
-import { VideoInstance } from '../../../models'
-
export interface JobHandler<T> {
- process (data: object, callback: (err: Error, videoInstance?: T) => void)
- onError (err: Error, jobId: number, video: T, callback: (err: Error) => void)
- onSuccess (data: any, jobId: number, video: T, callback: (err: Error) => void)
+ process (data: object): T
+ onError (err: Error, jobId: number)
+ onSuccess (jobId: number, jobResult: T)
}
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
import { addVideoToFriends } from '../../../lib'
import { VideoInstance } from '../../../models'
-function process (data: { id: string }, callback: (err: Error, videoInstance?: VideoInstance) => void) {
- db.Video.loadAndPopulateAuthorAndPodAndTags(data.id, function (err, video) {
- if (err) return callback(err)
-
- video.transcodeVideofile(function (err) {
- return callback(err, video)
- })
+function process (data: { id: string }) {
+ return db.Video.loadAndPopulateAuthorAndPodAndTags(data.id).then(video => {
+ return video.transcodeVideofile().then(() => video)
})
}
-function onError (err: Error, jobId: number, video: VideoInstance, callback: (err: Error) => void) {
+function onError (err: Error, jobId: number) {
logger.error('Error when transcoding video file in job %d.', jobId, { error: err })
- return callback(null)
+ return Promise.resolve()
}
-function onSuccess (data: any, jobId: number, video: VideoInstance, callback: (err: Error) => void) {
+function onSuccess (jobId: number, video: VideoInstance) {
logger.info('Job %d is a success.', jobId)
- video.toAddRemoteJSON(function (err, remoteVideo) {
- if (err) return callback(err)
-
+ video.toAddRemoteJSON().then(remoteVideo => {
// Now we'll add the video's meta data to our friends
- addVideoToFriends(remoteVideo, null, callback)
+ return addVideoToFriends(remoteVideo, null)
})
}
// Finish processing jobs from a previous start
const state = JOB_STATES.PROCESSING
- db.Job.listWithLimit(limit, state, (err, jobs) => {
- this.enqueueJobs(err, jobsQueue, jobs)
-
- forever(
- next => {
- if (jobsQueue.length() !== 0) {
- // Finish processing the queue first
- return setTimeout(next, JOBS_FETCHING_INTERVAL)
- }
-
- const state = JOB_STATES.PENDING
- db.Job.listWithLimit(limit, state, (err, jobs) => {
- if (err) {
- logger.error('Cannot list pending jobs.', { error: err })
- } else {
- jobs.forEach(job => {
- jobsQueue.push(job)
- })
+ db.Job.listWithLimit(limit, state)
+ .then(jobs => {
+ this.enqueueJobs(jobsQueue, jobs)
+
+ forever(
+ next => {
+ if (jobsQueue.length() !== 0) {
+ // Finish processing the queue first
+ return setTimeout(next, JOBS_FETCHING_INTERVAL)
}
- // Optimization: we could use "drain" from queue object
- return setTimeout(next, JOBS_FETCHING_INTERVAL)
- })
- },
+ const state = JOB_STATES.PENDING
+ db.Job.listWithLimit(limit, state)
+ .then(jobs => {
+ this.enqueueJobs(jobsQueue, jobs)
- err => { logger.error('Error in job scheduler queue.', { error: err }) }
- )
- })
+ // Optimization: we could use "drain" from queue object
+ return setTimeout(next, JOBS_FETCHING_INTERVAL)
+ })
+ .catch(err => logger.error('Cannot list pending jobs.', { error: err }))
+ },
+
+ err => logger.error('Error in job scheduler queue.', { error: err })
+ )
+ })
+ .catch(err => logger.error('Cannot list pending jobs.', { error: err }))
}
- createJob (transaction: Sequelize.Transaction, handlerName: string, handlerInputData: object, callback: (err: Error) => void) {
+ createJob (transaction: Sequelize.Transaction, handlerName: string, handlerInputData: object) {
const createQuery = {
state: JOB_STATES.PENDING,
handlerName,
}
const options = { transaction }
- db.Job.create(createQuery, options).asCallback(callback)
+ return db.Job.create(createQuery, options)
}
- private enqueueJobs (err: Error, jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
- if (err) {
- logger.error('Cannot list pending jobs.', { error: err })
- } else {
- jobs.forEach(job => {
- jobsQueue.push(job)
- })
- }
+ private enqueueJobs (jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
+ jobs.forEach(job => jobsQueue.push(job))
}
private processJob (job: JobInstance, callback: (err: Error) => void) {
const jobHandler = jobHandlers[job.handlerName]
+ if (jobHandler === undefined) {
+ logger.error('Unknown job handler for job %s.', job.handlerName)
+ return callback(null)
+ }
logger.info('Processing job %d with handler %s.', job.id, job.handlerName)
job.state = JOB_STATES.PROCESSING
- job.save().asCallback(err => {
- if (err) return this.cannotSaveJobError(err, callback)
-
- if (jobHandler === undefined) {
- logger.error('Unknown job handler for job %s.', job.handlerName)
- return callback(null)
- }
+ return job.save()
+ .then(() => {
+ return jobHandler.process(job.handlerInputData)
+ })
+ .then(
+ result => {
+ return this.onJobSuccess(jobHandler, job, result)
+ },
- return jobHandler.process(job.handlerInputData, (err, result) => {
- if (err) {
+ err => {
logger.error('Error in job handler %s.', job.handlerName, { error: err })
- return this.onJobError(jobHandler, job, result, callback)
+ return this.onJobError(jobHandler, job, err)
}
-
- return this.onJobSuccess(jobHandler, job, result, callback)
+ )
+ .then(() => callback(null))
+ .catch(err => {
+ this.cannotSaveJobError(err)
+ return callback(err)
})
- })
}
- private onJobError (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any, callback: (err: Error) => void) {
+ private onJobError (jobHandler: JobHandler<any>, job: JobInstance, err: Error) {
job.state = JOB_STATES.ERROR
- job.save().asCallback(err => {
- if (err) return this.cannotSaveJobError(err, callback)
-
- return jobHandler.onError(err, job.id, jobResult, callback)
- })
+ return job.save()
+ .then(() => jobHandler.onError(err, job.id))
+ .catch(err => this.cannotSaveJobError(err))
}
- private onJobSuccess (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any, callback: (err: Error) => void) {
+ private onJobSuccess (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any) {
job.state = JOB_STATES.SUCCESS
- job.save().asCallback(err => {
- if (err) return this.cannotSaveJobError(err, callback)
-
- return jobHandler.onSuccess(err, job.id, jobResult, callback)
- })
+ return job.save()
+ .then(() => jobHandler.onSuccess(job.id, jobResult))
+ .catch(err => this.cannotSaveJobError(err))
}
- private cannotSaveJobError (err: Error, callback: (err: Error) => void) {
+ private cannotSaveJobError (err: Error) {
logger.error('Cannot save new job state.', { error: err })
- return callback(err)
}
}
return db.User.getByUsername(username).then(function (user) {
if (!user) return null
- // We need to return a promise
- return new Promise(function (resolve, reject) {
- return user.isPasswordMatch(password, function (err, isPasswordMatch) {
- if (err) return reject(err)
+ return user.isPasswordMatch(password).then(passwordMatch => {
+ if (passwordMatch === false) return null
- if (isPasswordMatch === true) {
- return resolve(user)
- }
-
- return resolve(null)
- })
+ return user
})
})
}
tokenCreated.user = user
return tokenCreated
- }).catch(function (err) {
- throw err
})
}
-import * as eachLimit from 'async/eachLimit'
+import { isEmpty } from 'lodash'
+import * as Promise from 'bluebird'
import { database as db } from '../../initializers/database'
import { logger, makeSecureRequest } from '../../helpers'
-import { PodInstance } from '../../models'
+import { AbstractRequestClass, AbstractRequestToPodClass, PodInstance } from '../../models'
import {
API_VERSION,
REQUESTS_IN_PARALLEL,
REQUESTS_INTERVAL
} from '../../initializers'
-abstract class AbstractRequestScheduler {
+abstract class AbstractRequestScheduler <T> {
requestInterval: number
limitPods: number
limitPerPod: number
this.requestInterval = REQUESTS_INTERVAL
}
- abstract getRequestModel ()
- abstract getRequestToPodModel ()
- abstract buildRequestObjects (requests: any)
+ abstract getRequestModel (): AbstractRequestClass<T>
+ abstract getRequestToPodModel (): AbstractRequestToPodClass
+ abstract buildRequestObjects (requestsGrouped: T): {}
activate () {
logger.info('Requests scheduler activated.')
return REQUESTS_INTERVAL - (Date.now() - this.lastRequestTimestamp)
}
- remainingRequestsCount (callback: (err: Error, total: number) => void) {
- return this.getRequestModel().countTotalRequests(callback)
+ remainingRequestsCount () {
+ return this.getRequestModel().countTotalRequests()
}
- flush (callback: (err: Error) => void) {
- this.getRequestModel().removeAll(callback)
+ flush () {
+ return this.getRequestModel().removeAll()
}
// ---------------------------------------------------------------------------
// Make a requests to friends of a certain type
- protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object, callback) {
- if (!callback) callback = function () { /* empty */ }
-
+ protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object) {
const params = {
toPod: toPod,
sign: true, // Prove our identity
// Make multiple retry requests to all of pods
// The function fire some useful callbacks
- makeSecureRequest(params, (err, res) => {
- if (err || (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 204)) {
- err = err ? err.message : 'Status code not 20x : ' + res.statusCode
+ return makeSecureRequest(params)
+ .then(({ response, body }) => {
+ if (response.statusCode !== 200 && response.statusCode !== 201 && response.statusCode !== 204) {
+ throw new Error('Status code not 20x : ' + response.statusCode)
+ }
+ })
+ .catch(err => {
logger.error('Error sending secure request to %s pod.', toPod.host, { error: err })
- return callback(err)
- }
-
- return callback(null)
- })
+ throw err
+ })
}
// Make all the requests of the scheduler
protected makeRequests () {
- this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod, (err, requests) => {
- if (err) {
- logger.error('Cannot get the list of "%s".', this.description, { err: err })
- return // Abort
- }
-
- // If there are no requests, abort
- if (requests.length === 0) {
- logger.info('No "%s" to make.', this.description)
- return
- }
-
- // We want to group requests by destinations pod and endpoint
- const requestsToMakeGrouped = this.buildRequestObjects(requests)
-
- logger.info('Making "%s" to friends.', this.description)
-
- const goodPods = []
- const badPods = []
-
- eachLimit(Object.keys(requestsToMakeGrouped), REQUESTS_IN_PARALLEL, (hashKey, callbackEach) => {
- const requestToMake = requestsToMakeGrouped[hashKey]
- const toPod = requestToMake.toPod
-
- this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas, (err) => {
- if (err) {
- badPods.push(requestToMake.toPod.id)
- return callbackEach()
- }
-
- logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
- goodPods.push(requestToMake.toPod.id)
-
- // Remove the pod id of these request ids
- this.getRequestToPodModel().removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id, callbackEach)
+ return this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod)
+ .then((requestsGrouped: T) => {
+ // We want to group requests by destinations pod and endpoint
+ const requestsToMake = this.buildRequestObjects(requestsGrouped)
+
+ // If there are no requests, abort
+ if (isEmpty(requestsToMake) === true) {
+ logger.info('No "%s" to make.', this.description)
+ return { goodPods: [], badPods: [] }
+ }
+
+ logger.info('Making "%s" to friends.', this.description)
+
+ const goodPods = []
+ const badPods = []
+
+ return Promise.map(Object.keys(requestsToMake), hashKey => {
+ const requestToMake = requestsToMake[hashKey]
+ const toPod: PodInstance = requestToMake.toPod
+
+ return this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas)
+ .then(() => {
+ logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
+ goodPods.push(requestToMake.toPod.id)
+
+ this.afterRequestHook()
+
+ // Remove the pod id of these request ids
+ return this.getRequestToPodModel().removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id)
+ })
+ .catch(err => {
+ badPods.push(requestToMake.toPod.id)
+ logger.info('Cannot make request to %s.', toPod.host, { error: err })
+ })
+ }, { concurrency: REQUESTS_IN_PARALLEL }).then(() => ({ goodPods, badPods }))
+ })
+ .then(({ goodPods, badPods }) => {
+ this.afterRequestsHook()
- this.afterRequestHook()
- })
- }, () => {
// All the requests were made, we update the pods score
- db.Pod.updatePodsScore(goodPods, badPods)
-
- this.afterRequestsHook()
+ return db.Pod.updatePodsScore(goodPods, badPods)
})
- })
+ .catch(err => logger.error('Cannot get the list of "%s".', this.description, { error: err.stack }))
}
protected afterRequestHook () {
import { database as db } from '../../initializers/database'
import { AbstractRequestScheduler } from './abstract-request-scheduler'
import { logger } from '../../helpers'
-import {
- REQUESTS_LIMIT_PODS,
- REQUESTS_LIMIT_PER_POD
-} from '../../initializers'
+import { REQUESTS_LIMIT_PODS, REQUESTS_LIMIT_PER_POD } from '../../initializers'
+import { RequestsGrouped } from '../../models'
import { RequestEndpoint } from '../../../shared'
export type RequestSchedulerOptions = {
transaction: Sequelize.Transaction
}
-class RequestScheduler extends AbstractRequestScheduler {
+class RequestScheduler extends AbstractRequestScheduler<RequestsGrouped> {
constructor () {
super()
return db.RequestToPod
}
- buildRequestObjects (requests: { [ toPodId: number ]: any }) {
+ buildRequestObjects (requestsGrouped: RequestsGrouped) {
const requestsToMakeGrouped = {}
- Object.keys(requests).forEach(toPodId => {
- requests[toPodId].forEach(data => {
+ Object.keys(requestsGrouped).forEach(toPodId => {
+ requestsGrouped[toPodId].forEach(data => {
const request = data.request
const pod = data.pod
const hashKey = toPodId + request.endpoint
return requestsToMakeGrouped
}
- createRequest ({ type, endpoint, data, toIds, transaction }: RequestSchedulerOptions, callback: (err: Error) => void) {
+ createRequest ({ type, endpoint, data, toIds, transaction }: RequestSchedulerOptions) {
// TODO: check the setPods works
const podIds = []
// If there are no destination pods abort
- if (toIds.length === 0) return callback(null)
+ if (toIds.length === 0) return undefined
toIds.forEach(toPod => {
podIds.push(toPod)
transaction
}
- return db.Request.create(createQuery, dbRequestOptions).asCallback((err, request) => {
- if (err) return callback(err)
-
- return request.setPods(podIds, dbRequestOptions).asCallback(callback)
- })
+ return db.Request.create(createQuery, dbRequestOptions)
+ .then(request => {
+ return request.setPods(podIds, dbRequestOptions)
+ })
}
// ---------------------------------------------------------------------------
afterRequestsHook () {
// Flush requests with no pod
- this.getRequestModel().removeWithEmptyTo(err => {
- if (err) logger.error('Error when removing requests with no pods.', { error: err })
- })
+ this.getRequestModel().removeWithEmptyTo()
+ .catch(err => logger.error('Error when removing requests with no pods.', { error: err }))
}
}
REQUESTS_VIDEO_EVENT_LIMIT_PER_POD,
REQUEST_VIDEO_EVENT_ENDPOINT
} from '../../initializers'
+import { RequestsVideoEventGrouped } from '../../models'
import { RequestVideoEventType } from '../../../shared'
export type RequestVideoEventSchedulerOptions = {
transaction?: Sequelize.Transaction
}
-class RequestVideoEventScheduler extends AbstractRequestScheduler {
+class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoEventGrouped> {
constructor () {
super()
return db.RequestVideoEvent
}
- buildRequestObjects (eventsToProcess: { [ toPodId: number ]: any }[]) {
+ buildRequestObjects (eventRequests: RequestsVideoEventGrouped) {
const requestsToMakeGrouped = {}
/* Example:
// We group video events per video and per pod
// We add the counts of the same event types
- Object.keys(eventsToProcess).forEach(toPodId => {
- eventsToProcess[toPodId].forEach(eventToProcess => {
+ Object.keys(eventRequests).forEach(toPodId => {
+ eventRequests[toPodId].forEach(eventToProcess => {
if (!eventsPerVideoPerPod[toPodId]) eventsPerVideoPerPod[toPodId] = {}
if (!requestsToMakeGrouped[toPodId]) {
return requestsToMakeGrouped
}
- createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
+ createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions) {
if (count === undefined) count = 1
const dbRequestOptions: Sequelize.CreateOptions = {}
videoId
}
- return db.RequestVideoEvent.create(createQuery, dbRequestOptions).asCallback(callback)
+ return db.RequestVideoEvent.create(createQuery, dbRequestOptions)
}
}
REQUEST_VIDEO_QADU_ENDPOINT,
REQUEST_VIDEO_QADU_TYPES
} from '../../initializers'
+import { RequestsVideoQaduGrouped } from '../../models'
import { RequestVideoQaduType } from '../../../shared'
export type RequestVideoQaduSchedulerOptions = {
transaction?: Sequelize.Transaction
}
-class RequestVideoQaduScheduler extends AbstractRequestScheduler {
+class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQaduGrouped> {
constructor () {
super()
return db.RequestVideoQadu
}
- buildRequestObjects (requests: { [ toPodId: number ]: any }[]) {
+ buildRequestObjects (requests: RequestsVideoQaduGrouped) {
const requestsToMakeGrouped = {}
Object.keys(requests).forEach(toPodId => {
return requestsToMakeGrouped
}
- createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions, callback: (err: Error) => void) {
+ createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions) {
const dbRequestOptions: Sequelize.BulkCreateOptions = {}
if (transaction) dbRequestOptions.transaction = transaction
// Send the update to all our friends
- db.Pod.listAllIds(transaction, function (err, podIds) {
- if (err) return callback(err)
-
+ return db.Pod.listAllIds(transaction).then(podIds => {
const queries = []
podIds.forEach(podId => {
queries.push({ type, videoId, podId })
})
- return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions).asCallback(callback)
+ return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions)
})
}
}
function checkSignature (req: express.Request, res: express.Response, next: express.NextFunction) {
const host = req.body.signature.host
- db.Pod.loadByHost(host, function (err, pod) {
- if (err) {
- logger.error('Cannot get signed host in body.', { error: err })
- return res.sendStatus(500)
- }
+ db.Pod.loadByHost(host)
+ .then(pod => {
+ if (pod === null) {
+ logger.error('Unknown pod %s.', host)
+ return res.sendStatus(403)
+ }
- if (pod === null) {
- logger.error('Unknown pod %s.', host)
- return res.sendStatus(403)
- }
+ logger.debug('Checking signature from %s.', host)
- logger.debug('Checking signature from %s.', host)
+ let signatureShouldBe
+ // If there is data in the body the sender used it for its signature
+ // If there is no data we just use its host as signature
+ if (req.body.data) {
+ signatureShouldBe = req.body.data
+ } else {
+ signatureShouldBe = host
+ }
- let signatureShouldBe
- // If there is data in the body the sender used it for its signature
- // If there is no data we just use its host as signature
- if (req.body.data) {
- signatureShouldBe = req.body.data
- } else {
- signatureShouldBe = host
- }
+ const signatureOk = peertubeCryptoCheckSignature(pod.publicKey, signatureShouldBe, req.body.signature.signature)
- const signatureOk = peertubeCryptoCheckSignature(pod.publicKey, signatureShouldBe, req.body.signature.signature)
+ if (signatureOk === true) {
+ res.locals.secure = {
+ pod
+ }
- if (signatureOk === true) {
- res.locals.secure = {
- pod
+ return next()
}
- return next()
- }
-
- logger.error('Signature is not okay in body for %s.', req.body.signature.host)
- return res.sendStatus(403)
- })
+ logger.error('Signature is not okay in body for %s.', req.body.signature.host)
+ return res.sendStatus(403)
+ })
+ .catch(err => {
+ logger.error('Cannot get signed host in body.', { error: err })
+ return res.sendStatus(500)
+ })
}
// ---------------------------------------------------------------------------
logger.debug('Checking makeFriends parameters', { parameters: req.body })
checkErrors(req, res, function () {
- hasFriends(function (err, heHasFriends) {
- if (err) {
+ hasFriends()
+ .then(heHasFriends => {
+ if (heHasFriends === true) {
+ // We need to quit our friends before make new ones
+ return res.sendStatus(409)
+ }
+
+ return next()
+ })
+ .catch(err => {
logger.error('Cannot know if we have friends.', { error: err })
res.sendStatus(500)
- }
-
- if (heHasFriends === true) {
- // We need to quit our friends before make new ones
- return res.sendStatus(409)
- }
-
- return next()
- })
+ })
})
}
logger.debug('Checking podsAdd parameters', { parameters: req.body })
checkErrors(req, res, function () {
- db.Pod.loadByHost(req.body.host, function (err, pod) {
- if (err) {
+ db.Pod.loadByHost(req.body.host)
+ .then(pod => {
+ // Pod with this host already exists
+ if (pod) {
+ return res.sendStatus(409)
+ }
+
+ return next()
+ })
+ .catch(err => {
logger.error('Cannot load pod by host.', { error: err })
res.sendStatus(500)
- }
-
- // Pod with this host already exists
- if (pod) {
- return res.sendStatus(409)
- }
-
- return next()
- })
+ })
})
}
logger.debug('Checking usersAdd parameters', { parameters: req.body })
checkErrors(req, res, function () {
- db.User.loadByUsernameOrEmail(req.body.username, req.body.email, function (err, user) {
- if (err) {
+ db.User.loadByUsernameOrEmail(req.body.username, req.body.email)
+ .then(user => {
+ if (user) return res.status(409).send('User already exists.')
+
+ next()
+ })
+ .catch(err => {
logger.error('Error in usersAdd request validator.', { error: err })
return res.sendStatus(500)
- }
-
- if (user) return res.status(409).send('User already exists.')
-
- next()
- })
+ })
})
}
logger.debug('Checking usersRemove parameters', { parameters: req.params })
checkErrors(req, res, function () {
- db.User.loadById(req.params.id, function (err, user) {
- if (err) {
- logger.error('Error in usersRemove request validator.', { error: err })
- return res.sendStatus(500)
- }
-
- if (!user) return res.status(404).send('User not found')
+ db.User.loadById(req.params.id)
+ .then(user => {
+ if (!user) return res.status(404).send('User not found')
- if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
+ if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
- next()
- })
+ next()
+ })
+ .catch(err => {
+ logger.error('Error in usersRemove request validator.', { error: err })
+ return res.sendStatus(500)
+ })
})
}
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
checkErrors(req, res, function () {
- db.Video.load(req.params.videoId, function (err, video) {
- if (err) {
+ db.Video.load(req.params.videoId)
+ .then(video => {
+ if (!video) return res.status(404).send('Video not found')
+
+ next()
+ })
+ .catch(err => {
logger.error('Error in user request validator.', { error: err })
return res.sendStatus(500)
- }
-
- if (!video) return res.status(404).send('Video not found')
-
- next()
- })
+ })
})
}
import 'express-validator'
-import * as multer from 'multer'
import * as express from 'express'
import { database as db } from '../../initializers/database'
checkErrors(req, res, function () {
const videoFile = req.files.videofile[0]
- db.Video.getDurationFromFile(videoFile.path, function (err, duration) {
- if (err) {
- return res.status(400).send('Cannot retrieve metadata of the file.')
- }
-
- if (!isVideoDurationValid(duration)) {
- return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
- }
+ db.Video.getDurationFromFile(videoFile.path)
+ .then(duration => {
+ if (!isVideoDurationValid('' + duration)) {
+ return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
+ }
- videoFile['duration'] = duration
- next()
- })
+ videoFile['duration'] = duration
+ next()
+ })
+ .catch(err => {
+ logger.error('Error in getting duration from file.', { error: err })
+ res.status(400).send('Cannot retrieve metadata of the file.')
+ })
})
}
// ---------------------------------------------------------------------------
function checkVideoExists (id: string, res: express.Response, callback: () => void) {
- db.Video.loadAndPopulateAuthorAndPodAndTags(id, function (err, video) {
- if (err) {
- logger.error('Error in video request validator.', { error: err })
- return res.sendStatus(500)
- }
-
+ db.Video.loadAndPopulateAuthorAndPodAndTags(id).then(video => {
if (!video) return res.status(404).send('Video not found')
res.locals.video = video
callback()
})
+ .catch(err => {
+ logger.error('Error in video request validator.', { error: err })
+ return res.sendStatus(500)
+ })
}
function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) {
// Retrieve the user who did the request
- db.User.loadById(userId, function (err, user) {
- if (err) {
- logger.error('Error in video request validator.', { error: err })
- return res.sendStatus(500)
- }
-
- // Check if the user can delete the video
- // The user can delete it if s/he is an admin
- // Or if s/he is the video's author
- if (user.isAdmin() === false) {
- if (res.locals.video.isOwned() === false) {
- return res.status(403).send('Cannot remove video of another pod')
- }
-
- if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
- return res.status(403).send('Cannot remove video of another user')
+ db.User.loadById(userId)
+ .then(user => {
+ // Check if the user can delete the video
+ // The user can delete it if s/he is an admin
+ // Or if s/he is the video's author
+ if (user.isAdmin() === false) {
+ if (res.locals.video.isOwned() === false) {
+ return res.status(403).send('Cannot remove video of another pod')
+ }
+
+ if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
+ return res.status(403).send('Cannot remove video of another user')
+ }
}
- }
- // If we reach this comment, we can delete the video
- callback()
- })
+ // If we reach this comment, we can delete the video
+ callback()
+ })
+ .catch(err => {
+ logger.error('Error in video request validator.', { error: err })
+ return res.sendStatus(500)
+ })
}
function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) {
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
export namespace ApplicationMethods {
- export type LoadMigrationVersionCallback = (err: Error, version: number) => void
- export type LoadMigrationVersion = (callback: LoadMigrationVersionCallback) => void
+ export type LoadMigrationVersion = () => Promise<number>
- export type UpdateMigrationVersionCallback = (err: Error, applicationInstance: ApplicationAttributes) => void
- export type UpdateMigrationVersion = (newVersion: number, transaction: Sequelize.Transaction, callback: UpdateMigrationVersionCallback) => void
+ export type UpdateMigrationVersion = (
+ newVersion: number,
+ transaction: Sequelize.Transaction
+ ) => Promise<[ number, ApplicationInstance[] ]>
}
export interface ApplicationClass {
import { addMethodsToModel } from '../utils'
import {
- ApplicationClass,
ApplicationAttributes,
ApplicationInstance,
// ---------------------------------------------------------------------------
-loadMigrationVersion = function (callback: ApplicationMethods.LoadMigrationVersionCallback) {
+loadMigrationVersion = function () {
const query = {
attributes: [ 'migrationVersion' ]
}
- return Application.findOne(query).asCallback(function (err, data) {
- const version = data ? data.migrationVersion : null
-
- return callback(err, version)
- })
+ return Application.findOne(query).then(data => data ? data.migrationVersion : null)
}
-updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction, callback: ApplicationMethods.UpdateMigrationVersionCallback) {
+updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction) {
const options: Sequelize.UpdateOptions = {
where: {},
transaction: transaction
}
- return Application.update({ migrationVersion: newVersion }, options).asCallback(callback)
+ return Application.update({ migrationVersion: newVersion }, options)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { JobState } from '../../../shared/models/job.model'
export namespace JobMethods {
- export type ListWithLimitCallback = (err: Error, jobInstances: JobInstance[]) => void
- export type ListWithLimit = (limit: number, state: JobState, callback: ListWithLimitCallback) => void
+ export type ListWithLimit = (limit: number, state: JobState) => Promise<JobInstance[]>
}
export interface JobClass {
import { addMethodsToModel } from '../utils'
import {
- JobClass,
JobInstance,
JobAttributes,
// ---------------------------------------------------------------------------
-listWithLimit = function (limit: number, state: JobState, callback: JobMethods.ListWithLimitCallback) {
+listWithLimit = function (limit: number, state: JobState) {
const query = {
order: [
[ 'id', 'ASC' ]
}
}
- return Job.findAll(query).asCallback(callback)
+ return Job.findAll(query)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
export namespace OAuthClientMethods {
- export type CountTotalCallback = (err: Error, total: number) => void
- export type CountTotal = (callback: CountTotalCallback) => void
+ export type CountTotal = () => Promise<number>
- export type LoadFirstClientCallback = (err: Error, client: OAuthClientInstance) => void
- export type LoadFirstClient = (callback: LoadFirstClientCallback) => void
+ export type LoadFirstClient = () => Promise<OAuthClientInstance>
- export type GetByIdAndSecret = (clientId, clientSecret) => void
+ export type GetByIdAndSecret = (clientId: string, clientSecret: string) => Promise<OAuthClientInstance>
}
export interface OAuthClientClass {
import { addMethodsToModel } from '../utils'
import {
- OAuthClientClass,
OAuthClientInstance,
OAuthClientAttributes,
})
}
-countTotal = function (callback: OAuthClientMethods.CountTotalCallback) {
- return OAuthClient.count().asCallback(callback)
+countTotal = function () {
+ return OAuthClient.count()
}
-loadFirstClient = function (callback: OAuthClientMethods.LoadFirstClientCallback) {
- return OAuthClient.findOne().asCallback(callback)
+loadFirstClient = function () {
+ return OAuthClient.findOne()
}
getByIdAndSecret = function (clientId: string, clientSecret: string) {
import * as Sequelize from 'sequelize'
-import * as Bluebird from 'bluebird'
+import * as Promise from 'bluebird'
import { UserModel } from '../user'
}
export namespace OAuthTokenMethods {
- export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Bluebird<OAuthTokenInfo>
- export type GetByTokenAndPopulateUser = (bearerToken: string) => Bluebird<OAuthTokenInstance>
- export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Bluebird<OAuthTokenInstance>
+ export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Promise<OAuthTokenInfo>
+ export type GetByTokenAndPopulateUser = (bearerToken: string) => Promise<OAuthTokenInstance>
+ export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Promise<OAuthTokenInstance>
- export type RemoveByUserIdCallback = (err: Error) => void
- export type RemoveByUserId = (userId, callback) => void
+ export type RemoveByUserId = (userId) => Promise<number>
}
export interface OAuthTokenClass {
import { addMethodsToModel } from '../utils'
import {
- OAuthTokenClass,
OAuthTokenInstance,
OAuthTokenAttributes,
})
}
-removeByUserId = function (userId, callback) {
+removeByUserId = function (userId: number) {
const query = {
where: {
userId: userId
}
}
- return OAuthToken.destroy(query).asCallback(callback)
+ return OAuthToken.destroy(query)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
// Don't use barrel, import just what we need
import { Pod as FormatedPod } from '../../../shared/models/pod.model'
export namespace PodMethods {
export type ToFormatedJSON = (this: PodInstance) => FormatedPod
- export type CountAllCallback = (err: Error, total: number) => void
- export type CountAll = (callback) => void
+ export type CountAll = () => Promise<number>
- export type IncrementScoresCallback = (err: Error) => void
- export type IncrementScores = (ids: number[], value: number, callback?: IncrementScoresCallback) => void
+ export type IncrementScores = (ids: number[], value: number) => Promise<[ number, PodInstance[] ]>
- export type ListCallback = (err: Error, podInstances?: PodInstance[]) => void
- export type List = (callback: ListCallback) => void
+ export type List = () => Promise<PodInstance[]>
- export type ListAllIdsCallback = (err: Error, ids?: number[]) => void
- export type ListAllIds = (transaction: Sequelize.Transaction, callback: ListAllIdsCallback) => void
+ export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
- export type ListRandomPodIdsWithRequestCallback = (err: Error, podInstanceIds?: number[]) => void
- export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: ListRandomPodIdsWithRequestCallback) => void
+ export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
- export type ListBadPodsCallback = (err: Error, podInstances?: PodInstance[]) => void
- export type ListBadPods = (callback: ListBadPodsCallback) => void
+ export type ListBadPods = () => Promise<PodInstance[]>
- export type LoadCallback = (err: Error, podInstance: PodInstance) => void
- export type Load = (id: number, callback: LoadCallback) => void
+ export type Load = (id: number) => Promise<PodInstance>
- export type LoadByHostCallback = (err: Error, podInstance: PodInstance) => void
- export type LoadByHost = (host: string, callback: LoadByHostCallback) => void
+ export type LoadByHost = (host: string) => Promise<PodInstance>
- export type RemoveAllCallback = (err: Error) => void
- export type RemoveAll = (callback: RemoveAllCallback) => void
+ export type RemoveAll = () => Promise<number>
export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
}
-import { each, waterfall } from 'async'
import { map } from 'lodash'
import * as Sequelize from 'sequelize'
import { addMethodsToModel } from '../utils'
import {
- PodClass,
PodInstance,
PodAttributes,
})
}
-countAll = function (callback: PodMethods.CountAllCallback) {
- return Pod.count().asCallback(callback)
+countAll = function () {
+ return Pod.count()
}
-incrementScores = function (ids: number[], value: number, callback?: PodMethods.IncrementScoresCallback) {
- if (!callback) callback = function () { /* empty */ }
-
+incrementScores = function (ids: number[], value: number) {
const update = {
score: Sequelize.literal('score +' + value)
}
validate: false
}
- return Pod.update(update, options).asCallback(callback)
+ return Pod.update(update, options)
}
-list = function (callback: PodMethods.ListCallback) {
- return Pod.findAll().asCallback(callback)
+list = function () {
+ return Pod.findAll()
}
-listAllIds = function (transaction: Sequelize.Transaction, callback: PodMethods.ListAllIdsCallback) {
- const query: any = {
- attributes: [ 'id' ]
+listAllIds = function (transaction: Sequelize.Transaction) {
+ const query: Sequelize.FindOptions = {
+ attributes: [ 'id' ],
+ transaction
}
- if (transaction !== null) query.transaction = transaction
-
- return Pod.findAll(query).asCallback(function (err: Error, pods) {
- if (err) return callback(err)
-
- return callback(null, map(pods, 'id'))
+ return Pod.findAll(query).then(pods => {
+ return map(pods, 'id')
})
}
-listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: PodMethods.ListRandomPodIdsWithRequestCallback) {
- Pod.count().asCallback(function (err, count) {
- if (err) return callback(err)
-
+listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
+ return Pod.count().then(count => {
// Optimization...
- if (count === 0) return callback(null, [])
+ if (count === 0) return []
let start = Math.floor(Math.random() * count) - limit
if (start < 0) start = 0
}
}
- return Pod.findAll(query).asCallback(function (err, pods) {
- if (err) return callback(err)
-
- return callback(null, map(pods, 'id'))
+ return Pod.findAll(query).then(pods => {
+ return map(pods, 'id')
})
})
}
-listBadPods = function (callback: PodMethods.ListBadPodsCallback) {
+listBadPods = function () {
const query = {
where: {
score: { $lte: 0 }
}
}
- return Pod.findAll(query).asCallback(callback)
+ return Pod.findAll(query)
}
-load = function (id: number, callback: PodMethods.LoadCallback) {
- return Pod.findById(id).asCallback(callback)
+load = function (id: number) {
+ return Pod.findById(id)
}
-loadByHost = function (host: string, callback: PodMethods.LoadByHostCallback) {
+loadByHost = function (host: string) {
const query = {
where: {
host: host
}
}
- return Pod.findOne(query).asCallback(callback)
+ return Pod.findOne(query)
}
-removeAll = function (callback: PodMethods.RemoveAllCallback) {
- return Pod.destroy().asCallback(callback)
+removeAll = function () {
+ return Pod.destroy()
}
updatePodsScore = function (goodPods: number[], badPods: number[]) {
logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
if (goodPods.length !== 0) {
- incrementScores(goodPods, PODS_SCORE.BONUS, function (err) {
- if (err) logger.error('Cannot increment scores of good pods.', { error: err })
+ incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
+ logger.error('Cannot increment scores of good pods.', { error: err })
})
}
if (badPods.length !== 0) {
- incrementScores(badPods, PODS_SCORE.MALUS, function (err) {
- if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
- removeBadPods()
- })
+ incrementScores(badPods, PODS_SCORE.MALUS)
+ .then(() => removeBadPods())
+ .catch(err => {
+ if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
+ })
}
}
// Remove pods with a score of 0 (too many requests where they were unreachable)
function removeBadPods () {
- waterfall([
- function findBadPods (callback) {
- listBadPods(function (err, pods) {
- if (err) {
- logger.error('Cannot find bad pods.', { error: err })
- return callback(err)
- }
-
- return callback(null, pods)
- })
- },
-
- function removeTheseBadPods (pods, callback) {
- each(pods, function (pod: any, callbackEach) {
- pod.destroy().asCallback(callbackEach)
- }, function (err) {
- return callback(err, pods.length)
- })
- }
- ], function (err, numberOfPodsRemoved) {
- if (err) {
+ return listBadPods()
+ .then(pods => {
+ const podsRemovePromises = pods.map(pod => pod.destroy())
+ return Promise.all(podsRemovePromises).then(() => pods.length)
+ })
+ .then(numberOfPodsRemoved => {
+ if (numberOfPodsRemoved) {
+ logger.info('Removed %d pods.', numberOfPodsRemoved)
+ } else {
+ logger.info('No need to remove bad pods.')
+ }
+ })
+ .catch(err => {
logger.error('Cannot remove bad pods.', { error: err })
- } else if (numberOfPodsRemoved) {
- logger.info('Removed %d pods.', numberOfPodsRemoved)
- } else {
- logger.info('No need to remove bad pods.')
- }
- })
+ })
}
--- /dev/null
+import * as Promise from 'bluebird'
+
+export interface AbstractRequestClass <T> {
+ countTotalRequests: () => Promise<number>
+ listWithLimitAndRandom: (limitPods: number, limitRequestsPerPod: number) => Promise<T>
+ removeWithEmptyTo: () => Promise<number>
+ removeAll: () => Promise<void>
+}
+
+export interface AbstractRequestToPodClass {
+ removeByRequestIdsAndPod: (ids: number[], podId: number) => Promise<number>
+}
+export * from './abstract-request-interface'
export * from './request-interface'
export * from './request-to-pod-interface'
export * from './request-video-event-interface'
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+import { AbstractRequestClass } from './abstract-request-interface'
import { PodInstance, PodAttributes } from '../pod'
import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
}
export namespace RequestMethods {
- export type CountTotalRequestsCallback = (err: Error, total: number) => void
- export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+ export type CountTotalRequests = () => Promise<number>
- export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsGrouped) => void
- export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback: ListWithLimitAndRandomCallback) => void
+ export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
- export type RemoveWithEmptyToCallback = (err: Error) => void
- export type RemoveWithEmptyTo = (callback: RemoveWithEmptyToCallback) => void
+ export type RemoveWithEmptyTo = () => Promise<number>
- export type RemoveAllCallback = (err: Error) => void
- export type RemoveAll = (callback: RemoveAllCallback) => void
+ export type RemoveAll = () => Promise<void>
}
-export interface RequestClass {
+export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
countTotalRequests: RequestMethods.CountTotalRequests
listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+import { AbstractRequestToPodClass } from './abstract-request-interface'
export namespace RequestToPodMethods {
- export type RemoveByRequestIdsAndPodCallback = (err: Error) => void
- export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number, callback?: RemoveByRequestIdsAndPodCallback) => void
+ export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
}
-export interface RequestToPodClass {
+export interface RequestToPodClass extends AbstractRequestToPodClass {
removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
}
import { addMethodsToModel } from '../utils'
import {
- RequestToPodClass,
RequestToPodInstance,
RequestToPodAttributes,
// ---------------------------------------------------------------------------
-removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callback?: RequestToPodMethods.RemoveByRequestIdsAndPodCallback) {
- if (!callback) callback = function () { /* empty */ }
-
+removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
const query = {
where: {
requestId: {
}
}
- RequestToPod.destroy(query).asCallback(callback)
+ return RequestToPod.destroy(query)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
import { VideoInstance } from '../video'
import { PodInstance } from '../pod'
}
export namespace RequestVideoEventMethods {
- export type CountTotalRequestsCallback = (err: Error, total: number) => void
- export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+ export type CountTotalRequests = () => Promise<number>
- export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoEventGrouped) => void
- export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
+ export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
- export type RemoveByRequestIdsAndPodCallback = () => void
- export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
+ export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
- export type RemoveAllCallback = () => void
- export type RemoveAll = (callback: RemoveAllCallback) => void
+ export type RemoveAll = () => Promise<void>
}
-export interface RequestVideoEventClass {
+export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
countTotalRequests: RequestVideoEventMethods.CountTotalRequests
listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
count: number
}
-export interface RequestVideoEventInstance extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
+export interface RequestVideoEventInstance
+ extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
id: number
Video: VideoInstance
}
-export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
+export interface RequestVideoEventModel
+ extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
import { isVideoEventCountValid } from '../../helpers'
import { addMethodsToModel } from '../utils'
import {
- RequestVideoEventClass,
RequestVideoEventInstance,
RequestVideoEventAttributes,
})
}
-countTotalRequests = function (callback: RequestVideoEventMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
const query = {}
- return RequestVideoEvent.count(query).asCallback(callback)
+ return RequestVideoEvent.count(query)
}
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoEventMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
// We make a join between videos and authors to find the podId of our video event requests
const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' +
'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
- Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins, function (err, podIds) {
- if (err) return callback(err)
-
+ return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
// We don't have friends that have requests
- if (podIds.length === 0) return callback(null, [])
+ if (podIds.length === 0) return []
const query = {
order: [
]
}
- RequestVideoEvent.findAll(query).asCallback(function (err, requests) {
- if (err) return callback(err)
-
+ return RequestVideoEvent.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
- return callback(err, requestsGrouped)
+ return requestsGrouped
})
})
}
-removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoEventMethods.RemoveByRequestIdsAndPodCallback) {
+removeByRequestIdsAndPod = function (ids: number[], podId: number) {
const query = {
where: {
id: {
]
}
- RequestVideoEvent.destroy(query).asCallback(callback)
+ return RequestVideoEvent.destroy(query)
}
-removeAll = function (callback: RequestVideoEventMethods.RemoveAllCallback) {
+removeAll = function () {
// Delete all requests
- RequestVideoEvent.truncate({ cascade: true }).asCallback(callback)
+ return RequestVideoEvent.truncate({ cascade: true })
}
// ---------------------------------------------------------------------------
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
import { VideoInstance } from '../video'
import { PodInstance } from '../pod'
}
export namespace RequestVideoQaduMethods {
- export type CountTotalRequestsCallback = (err: Error, total: number) => void
- export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+ export type CountTotalRequests = () => Promise<number>
- export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoQaduGrouped) => void
- export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
+ export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
- export type RemoveByRequestIdsAndPodCallback = () => void
- export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
+ export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
- export type RemoveAllCallback = () => void
- export type RemoveAll = (callback: RemoveAllCallback) => void
+ export type RemoveAll = () => Promise<void>
}
-export interface RequestVideoQaduClass {
+export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
type: RequestVideoQaduType
}
-export interface RequestVideoQaduInstance extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
+export interface RequestVideoQaduInstance
+ extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
id: number
Pod: PodInstance
Video: VideoInstance
}
-export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
+export interface RequestVideoQaduModel
+ extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
import { addMethodsToModel } from '../utils'
import {
- RequestVideoQaduClass,
RequestVideoQaduInstance,
RequestVideoQaduAttributes,
})
}
-countTotalRequests = function (callback: RequestVideoQaduMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
const query = {}
- return RequestVideoQadu.count(query).asCallback(callback)
+ return RequestVideoQadu.count(query)
}
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoQaduMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
const tableJoin = ''
- Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin, function (err, podIds) {
- if (err) return callback(err)
-
+ return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
// We don't have friends that have requests
- if (podIds.length === 0) return callback(null, [])
+ if (podIds.length === 0) return []
const query = {
include: [
]
}
- RequestVideoQadu.findAll(query).asCallback(function (err, requests) {
- if (err) return callback(err)
-
+ return RequestVideoQadu.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
- return callback(err, requestsGrouped)
+ return requestsGrouped
})
})
}
-removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoQaduMethods.RemoveByRequestIdsAndPodCallback) {
+removeByRequestIdsAndPod = function (ids: number[], podId: number) {
const query = {
where: {
id: {
}
}
- RequestVideoQadu.destroy(query).asCallback(callback)
+ return RequestVideoQadu.destroy(query)
}
-removeAll = function (callback: RequestVideoQaduMethods.RemoveAllCallback) {
+removeAll = function () {
// Delete all requests
- RequestVideoQadu.truncate({ cascade: true }).asCallback(callback)
+ return RequestVideoQadu.truncate({ cascade: true })
}
// ---------------------------------------------------------------------------
import { REQUEST_ENDPOINTS } from '../../initializers'
import { addMethodsToModel } from '../utils'
import {
- RequestClass,
RequestInstance,
RequestAttributes,
})
}
-countTotalRequests = function (callback: RequestMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
// We need to include Pod because there are no cascade delete when a pod is removed
// So we could count requests that do not have existing pod anymore
const query = {
include: [ Request['sequelize'].models.Pod ]
}
- return Request.count(query).asCallback(callback)
+ return Request.count(query)
}
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
const tableJoin = ''
- Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', '', function (err, podIds) {
- if (err) return callback(err)
-
+ return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
// We don't have friends that have requests
- if (podIds.length === 0) return callback(null, [])
+ if (podIds.length === 0) return []
// The first x requests of these pods
// It is very important to sort by id ASC to keep the requests order!
]
}
- Request.findAll(query).asCallback(function (err, requests) {
- if (err) return callback(err)
+ return Request.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
- return callback(err, requestsGrouped)
+ return requestsGrouped
})
})
}
-removeAll = function (callback: RequestMethods.RemoveAllCallback) {
+removeAll = function () {
// Delete all requests
- Request.truncate({ cascade: true }).asCallback(callback)
+ return Request.truncate({ cascade: true })
}
-removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallback) {
- if (!callback) callback = function () { /* empty */ }
-
+removeWithEmptyTo = function () {
const query = {
where: {
id: {
}
}
- Request.destroy(query).asCallback(callback)
+ return Request.destroy(query)
}
// ---------------------------------------------------------------------------
import * as Sequelize from 'sequelize'
-import * as Bluebird from 'bluebird'
+import * as Promise from 'bluebird'
// Don't use barrel, import just what we need
import { UserRole, User as FormatedUser } from '../../../shared/models/user.model'
+import { ResultList } from '../../../shared/models/result-list.model'
export namespace UserMethods {
- export type IsPasswordMatchCallback = (err: Error, same: boolean) => void
- export type IsPasswordMatch = (this: UserInstance, password: string, callback: IsPasswordMatchCallback) => void
+ export type IsPasswordMatch = (this: UserInstance, password: string) => Promise<boolean>
export type ToFormatedJSON = (this: UserInstance) => FormatedUser
export type IsAdmin = (this: UserInstance) => boolean
- export type CountTotalCallback = (err: Error, total: number) => void
- export type CountTotal = (callback: CountTotalCallback) => void
+ export type CountTotal = () => Promise<number>
- export type GetByUsername = (username: string) => Bluebird<UserInstance>
+ export type GetByUsername = (username: string) => Promise<UserInstance>
- export type ListCallback = (err: Error, userInstances: UserInstance[]) => void
- export type List = (callback: ListCallback) => void
+ export type List = () => Promise<UserInstance[]>
- export type ListForApiCallback = (err: Error, userInstances?: UserInstance[], total?: number) => void
- export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+ export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<UserInstance> >
- export type LoadByIdCallback = (err: Error, userInstance: UserInstance) => void
- export type LoadById = (id: number, callback: LoadByIdCallback) => void
+ export type LoadById = (id: number) => Promise<UserInstance>
- export type LoadByUsernameCallback = (err: Error, userInstance: UserInstance) => void
- export type LoadByUsername = (username: string, callback: LoadByUsernameCallback) => void
+ export type LoadByUsername = (username: string) => Promise<UserInstance>
- export type LoadByUsernameOrEmailCallback = (err: Error, userInstance: UserInstance) => void
- export type LoadByUsernameOrEmail = (username: string, email: string, callback: LoadByUsernameOrEmailCallback) => void
+ export type LoadByUsernameOrEmail = (username: string, email: string) => Promise<UserInstance>
}
export interface UserClass {
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { VideoRateType } from '../../../shared/models/user-video-rate.model'
export namespace UserVideoRateMethods {
- export type LoadCallback = (err: Error, userVideoRateInstance: UserVideoRateInstance) => void
- export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: LoadCallback) => void
+ export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
}
export interface UserVideoRateClass {
import { addMethodsToModel } from '../utils'
import {
- UserVideoRateClass,
UserVideoRateInstance,
UserVideoRateAttributes,
})
}
-load = function (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: UserVideoRateMethods.LoadCallback) {
+load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) {
const options: Sequelize.FindOptions = {
where: {
userId,
}
if (transaction) options.transaction = transaction
- return UserVideoRate.findOne(options).asCallback(callback)
+ return UserVideoRate.findOne(options)
}
import { addMethodsToModel } from '../utils'
import {
- UserClass,
UserInstance,
UserAttributes,
}
function beforeCreateOrUpdate (user: UserInstance) {
- return new Promise(function (resolve, reject) {
- cryptPassword(user.password, function (err, hash) {
- if (err) return reject(err)
-
- user.password = hash
-
- return resolve()
- })
+ return cryptPassword(user.password).then(hash => {
+ user.password = hash
+ return undefined
})
}
// ------------------------------ METHODS ------------------------------
-isPasswordMatch = function (this: UserInstance, password: string, callback: UserMethods.IsPasswordMatchCallback) {
- return comparePassword(password, this.password, callback)
+isPasswordMatch = function (this: UserInstance, password: string) {
+ return comparePassword(password, this.password)
}
toFormatedJSON = function (this: UserInstance) {
})
}
-countTotal = function (callback: UserMethods.CountTotalCallback) {
- return this.count().asCallback(callback)
+countTotal = function () {
+ return this.count()
}
getByUsername = function (username: string) {
return User.findOne(query)
}
-list = function (callback: UserMethods.ListCallback) {
- return User.find().asCallback(callback)
+list = function () {
+ return User.findAll()
}
-listForApi = function (start: number, count: number, sort: string, callback: UserMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
const query = {
offset: start,
limit: count,
order: [ getSort(sort) ]
}
- return User.findAndCountAll(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- return callback(null, result.rows, result.count)
+ return User.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
})
}
-loadById = function (id: number, callback: UserMethods.LoadByIdCallback) {
- return User.findById(id).asCallback(callback)
+loadById = function (id: number) {
+ return User.findById(id)
}
-loadByUsername = function (username: string, callback: UserMethods.LoadByUsernameCallback) {
+loadByUsername = function (username: string) {
const query = {
where: {
username: username
}
}
- return User.findOne(query).asCallback(callback)
+ return User.findOne(query)
}
-loadByUsernameOrEmail = function (username: string, email: string, callback: UserMethods.LoadByUsernameOrEmailCallback) {
+loadByUsernameOrEmail = function (username: string, email: string) {
const query = {
where: {
$or: [ { username }, { email } ]
}
}
- return User.findOne(query).asCallback(callback)
+ return User.findOne(query)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { PodInstance } from '../pod'
export namespace AuthorMethods {
- export type FindOrCreateAuthorCallback = (err: Error, authorInstance?: AuthorInstance) => void
- export type FindOrCreateAuthor = (name: string, podId: number, userId: number, transaction: Sequelize.Transaction, callback: FindOrCreateAuthorCallback) => void
+ export type FindOrCreateAuthor = (
+ name: string,
+ podId: number,
+ userId: number,
+ transaction: Sequelize.Transaction
+ ) => Promise<AuthorInstance>
}
export interface AuthorClass {
import { addMethodsToModel } from '../utils'
import {
- AuthorClass,
AuthorInstance,
AuthorAttributes,
})
}
-findOrCreateAuthor = function (
- name: string,
- podId: number,
- userId: number,
- transaction: Sequelize.Transaction,
- callback: AuthorMethods.FindOrCreateAuthorCallback
-) {
+findOrCreateAuthor = function (name: string, podId: number, userId: number, transaction: Sequelize.Transaction) {
const author = {
name,
podId,
userId
}
- const query: any = {
+ const query: Sequelize.FindOrInitializeOptions<AuthorAttributes> = {
where: author,
- defaults: author
+ defaults: author,
+ transaction
}
- if (transaction !== null) query.transaction = transaction
-
- Author.findOrCreate(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- // [ instance, wasCreated ]
- return callback(null, result[0])
- })
+ return Author.findOrCreate(query).then(([ authorInstance ]) => authorInstance)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
export namespace TagMethods {
- export type FindOrCreateTagsCallback = (err: Error, tagInstances: TagInstance[]) => void
- export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction, callback: FindOrCreateTagsCallback) => void
+ export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction) => Promise<TagInstance[]>
}
export interface TagClass {
-import { each } from 'async'
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { addMethodsToModel } from '../utils'
import {
- TagClass,
TagInstance,
TagAttributes,
})
}
-findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction, callback: TagMethods.FindOrCreateTagsCallback) {
- const tagInstances = []
-
- each<string, Error>(tags, function (tag, callbackEach) {
+findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction) {
+ const tasks: Promise<TagInstance>[] = []
+ tags.forEach(tag => {
const query: any = {
where: {
name: tag
if (transaction) query.transaction = transaction
- Tag.findOrCreate(query).asCallback(function (err, res) {
- if (err) return callbackEach(err)
-
- // res = [ tag, isCreated ]
- const tag = res[0]
- tagInstances.push(tag)
- return callbackEach()
- })
- }, function (err) {
- return callback(err, tagInstances)
+ const promise = Tag.findOrCreate(query).then(([ tagInstance ]) => tagInstance)
+ tasks.push(promise)
})
+
+ return Promise.all(tasks)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { PodInstance } from '../pod'
+import { ResultList } from '../../../shared'
// Don't use barrel, import just what we need
import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model'
export namespace VideoAbuseMethods {
export type ToFormatedJSON = (this: VideoAbuseInstance) => FormatedVideoAbuse
- export type ListForApiCallback = (err: Error, videoAbuseInstances?: VideoAbuseInstance[], total?: number) => void
- export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+ export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoAbuseInstance> >
}
export interface VideoAbuseClass {
import { addMethodsToModel, getSort } from '../utils'
import {
- VideoAbuseClass,
VideoAbuseInstance,
VideoAbuseAttributes,
})
}
-listForApi = function (start: number, count: number, sort: string, callback: VideoAbuseMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
const query = {
offset: start,
limit: count,
]
}
- return VideoAbuse.findAndCountAll(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- return callback(null, result.rows, result.count)
+ return VideoAbuse.findAndCountAll(query).then(({ rows, count }) => {
+ return { total: count, data: rows }
})
}
-
-
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+import { ResultList } from '../../../shared'
// Don't use barrel, import just what we need
import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model'
export namespace BlacklistedVideoMethods {
export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo
- export type CountTotalCallback = (err: Error, total: number) => void
- export type CountTotal = (callback: CountTotalCallback) => void
+ export type CountTotal = () => Promise<number>
- export type ListCallback = (err: Error, backlistedVideoInstances: BlacklistedVideoInstance[]) => void
- export type List = (callback: ListCallback) => void
+ export type List = () => Promise<BlacklistedVideoInstance[]>
- export type ListForApiCallback = (err: Error, blacklistedVIdeoInstances?: BlacklistedVideoInstance[], total?: number) => void
- export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+ export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<BlacklistedVideoInstance> >
- export type LoadByIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
- export type LoadById = (id: number, callback: LoadByIdCallback) => void
+ export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
- export type LoadByVideoIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
- export type LoadByVideoId = (id: string, callback: LoadByVideoIdCallback) => void
+ export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance>
}
export interface BlacklistedVideoClass {
videoId: string
}
-export interface BlacklistedVideoInstance extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
+export interface BlacklistedVideoInstance
+ extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
id: number
createdAt: Date
updatedAt: Date
toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
}
-export interface BlacklistedVideoModel extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
+export interface BlacklistedVideoModel
+ extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
import { addMethodsToModel, getSort } from '../utils'
import {
- BlacklistedVideoClass,
BlacklistedVideoInstance,
BlacklistedVideoAttributes,
})
}
-countTotal = function (callback: BlacklistedVideoMethods.CountTotalCallback) {
- return BlacklistedVideo.count().asCallback(callback)
+countTotal = function () {
+ return BlacklistedVideo.count()
}
-list = function (callback: BlacklistedVideoMethods.ListCallback) {
- return BlacklistedVideo.findAll().asCallback(callback)
+list = function () {
+ return BlacklistedVideo.findAll()
}
-listForApi = function (start: number, count: number, sort: string, callback: BlacklistedVideoMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
const query = {
offset: start,
limit: count,
order: [ getSort(sort) ]
}
- return BlacklistedVideo.findAndCountAll(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- return callback(null, result.rows, result.count)
+ return BlacklistedVideo.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
})
}
-loadById = function (id: number, callback: BlacklistedVideoMethods.LoadByIdCallback) {
- return BlacklistedVideo.findById(id).asCallback(callback)
+loadById = function (id: number) {
+ return BlacklistedVideo.findById(id)
}
-loadByVideoId = function (id: string, callback: BlacklistedVideoMethods.LoadByIdCallback) {
+loadByVideoId = function (id: string) {
const query = {
where: {
videoId: id
}
}
- return BlacklistedVideo.find(query).asCallback(callback)
+ return BlacklistedVideo.findOne(query)
}
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { AuthorInstance } from './author-interface'
-import { VideoTagInstance } from './video-tag-interface'
+import { TagAttributes, TagInstance } from './tag-interface'
// Don't use barrel, import just what we need
import { Video as FormatedVideo } from '../../../shared/models/video.model'
+import { ResultList } from '../../../shared/models/result-list.model'
export type FormatedAddRemoteVideo = {
name: string
export type IsOwned = (this: VideoInstance) => boolean
export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo
- export type ToAddRemoteJSONCallback = (err: Error, videoFormated?: FormatedAddRemoteVideo) => void
- export type ToAddRemoteJSON = (this: VideoInstance, callback: ToAddRemoteJSONCallback) => void
-
+ export type ToAddRemoteJSON = (this: VideoInstance) => Promise<FormatedAddRemoteVideo>
export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo
- export type TranscodeVideofileCallback = (err: Error) => void
- export type TranscodeVideofile = (this: VideoInstance, callback: TranscodeVideofileCallback) => void
-
- export type GenerateThumbnailFromDataCallback = (err: Error, thumbnailName?: string) => void
- export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string, callback: GenerateThumbnailFromDataCallback) => void
-
- export type GetDurationFromFileCallback = (err: Error, duration?: number) => void
- export type GetDurationFromFile = (videoPath, callback) => void
-
- export type ListCallback = (err: Error, videoInstances: VideoInstance[]) => void
- export type List = (callback: ListCallback) => void
-
- export type ListForApiCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
- export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
-
- export type LoadByHostAndRemoteIdCallback = (err: Error, videoInstance: VideoInstance) => void
- export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string, callback: LoadByHostAndRemoteIdCallback) => void
-
- export type ListOwnedAndPopulateAuthorAndTagsCallback = (err: Error, videoInstances: VideoInstance[]) => void
- export type ListOwnedAndPopulateAuthorAndTags = (callback: ListOwnedAndPopulateAuthorAndTagsCallback) => void
-
- export type ListOwnedByAuthorCallback = (err: Error, videoInstances: VideoInstance[]) => void
- export type ListOwnedByAuthor = (author: string, callback: ListOwnedByAuthorCallback) => void
-
- export type LoadCallback = (err: Error, videoInstance: VideoInstance) => void
- export type Load = (id: string, callback: LoadCallback) => void
-
- export type LoadAndPopulateAuthorCallback = (err: Error, videoInstance: VideoInstance) => void
- export type LoadAndPopulateAuthor = (id: string, callback: LoadAndPopulateAuthorCallback) => void
-
- export type LoadAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstance: VideoInstance) => void
- export type LoadAndPopulateAuthorAndPodAndTags = (id: string, callback: LoadAndPopulateAuthorAndPodAndTagsCallback) => void
-
- export type SearchAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
- export type SearchAndPopulateAuthorAndPodAndTags = (value: string, field: string, start: number, count: number, sort: string, callback: SearchAndPopulateAuthorAndPodAndTagsCallback) => void
+ export type TranscodeVideofile = (this: VideoInstance) => Promise<void>
+
+ // Return thumbnail name
+ export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string>
+ export type GetDurationFromFile = (videoPath: string) => Promise<number>
+
+ export type List = () => Promise<VideoInstance[]>
+ export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]>
+ export type ListOwnedByAuthor = (author: string) => Promise<VideoInstance[]>
+
+ export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> >
+ export type SearchAndPopulateAuthorAndPodAndTags = (
+ value: string,
+ field: string,
+ start: number,
+ count: number,
+ sort: string
+ ) => Promise< ResultList<VideoInstance> >
+
+ export type Load = (id: string) => Promise<VideoInstance>
+ export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance>
+ export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance>
+ export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance>
}
export interface VideoClass {
dislikes?: number
Author?: AuthorInstance
- Tags?: VideoTagInstance[]
+ Tags?: TagInstance[]
}
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
transcodeVideofile: VideoMethods.TranscodeVideofile
+
+ setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string>
}
export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
import * as Sequelize from 'sequelize'
-import { addMethodsToModel } from '../utils'
import {
- VideoTagClass,
VideoTagInstance,
- VideoTagAttributes,
-
- VideoTagMethods
+ VideoTagAttributes
} from './video-tag-interface'
let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
import * as safeBuffer from 'safe-buffer'
const Buffer = safeBuffer.Buffer
-import * as createTorrent from 'create-torrent'
import * as ffmpeg from 'fluent-ffmpeg'
-import * as fs from 'fs'
import * as magnetUtil from 'magnet-uri'
import { map, values } from 'lodash'
-import { parallel, series } from 'async'
import * as parseTorrent from 'parse-torrent'
import { join } from 'path'
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { database as db } from '../../initializers/database'
-import { VideoTagInstance } from './video-tag-interface'
+import { TagInstance } from './tag-interface'
import {
logger,
isVideoNameValid,
isVideoNSFWValid,
isVideoDescriptionValid,
isVideoInfoHashValid,
- isVideoDurationValid
+ isVideoDurationValid,
+ readFileBufferPromise,
+ unlinkPromise,
+ renamePromise,
+ writeFilePromise,
+ createTorrentPromise
} from '../../helpers'
import {
CONSTRAINTS_FIELDS,
import { addMethodsToModel, getSort } from '../utils'
import {
- VideoClass,
VideoInstance,
VideoAttributes,
toFormatedJSON,
toAddRemoteJSON,
toUpdateRemoteJSON,
- transcodeVideofile,
+ transcodeVideofile
]
addMethodsToModel(Video, classMethods, instanceMethods)
}
function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
- return new Promise(function (resolve, reject) {
+ if (video.isOwned()) {
+ const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
const tasks = []
- if (video.isOwned()) {
- const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
-
- tasks.push(
- function createVideoTorrent (callback) {
- createTorrentFromVideo(video, videoPath, callback)
- },
-
- function createVideoThumbnail (callback) {
- createThumbnail(video, videoPath, callback)
- },
-
- function createVideoPreview (callback) {
- createPreview(video, videoPath, callback)
- }
- )
-
- if (CONFIG.TRANSCODING.ENABLED === true) {
- tasks.push(
- function createVideoTranscoderJob (callback) {
- const dataInput = {
- id: video.id
- }
+ tasks.push(
+ createTorrentFromVideo(video, videoPath),
+ createThumbnail(video, videoPath),
+ createPreview(video, videoPath)
+ )
- JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback)
- }
- )
+ if (CONFIG.TRANSCODING.ENABLED === true) {
+ const dataInput = {
+ id: video.id
}
- return parallel(tasks, function (err) {
- if (err) return reject(err)
-
- return resolve()
- })
+ tasks.push(
+ JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput)
+ )
}
- return resolve()
- })
+ return Promise.all(tasks)
+ }
+
+ return Promise.resolve()
}
function afterDestroy (video: VideoInstance) {
- return new Promise(function (resolve, reject) {
- const tasks = []
-
- tasks.push(
- function (callback) {
- removeThumbnail(video, callback)
- }
- )
-
- if (video.isOwned()) {
- tasks.push(
- function removeVideoFile (callback) {
- removeFile(video, callback)
- },
+ const tasks = []
- function removeVideoTorrent (callback) {
- removeTorrent(video, callback)
- },
-
- function removeVideoPreview (callback) {
- removePreview(video, callback)
- },
-
- function notifyFriends (callback) {
- const params = {
- remoteId: video.id
- }
-
- removeVideoToFriends(params)
+ tasks.push(
+ removeThumbnail(video)
+ )
- return callback()
- }
- )
+ if (video.isOwned()) {
+ const removeVideoToFriendsParams = {
+ remoteId: video.id
}
- parallel(tasks, function (err) {
- if (err) return reject(err)
+ tasks.push(
+ removeFile(video),
+ removeTorrent(video),
+ removePreview(video),
+ removeVideoToFriends(removeVideoToFriendsParams)
+ )
+ }
- return resolve()
- })
- })
+ return Promise.all(tasks)
}
// ------------------------------ METHODS ------------------------------
views: this.views,
likes: this.likes,
dislikes: this.dislikes,
- tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+ tags: map<TagInstance, string>(this.Tags, 'name'),
thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
createdAt: this.createdAt,
updatedAt: this.updatedAt
return json
}
-toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRemoteJSONCallback) {
+toAddRemoteJSON = function (this: VideoInstance) {
// Get thumbnail data to send to the other pod
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
- fs.readFile(thumbnailPath, (err, thumbnailData) => {
- if (err) {
- logger.error('Cannot read the thumbnail of the video')
- return callback(err)
- }
+ return readFileBufferPromise(thumbnailPath).then(thumbnailData => {
const remoteVideo = {
name: this.name,
category: this.category,
author: this.Author.name,
duration: this.duration,
thumbnailData: thumbnailData.toString('binary'),
- tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+ tags: map<TagInstance, string>(this.Tags, 'name'),
createdAt: this.createdAt,
updatedAt: this.updatedAt,
extname: this.extname,
dislikes: this.dislikes
}
- return callback(null, remoteVideo)
+ return remoteVideo
})
}
remoteId: this.id,
author: this.Author.name,
duration: this.duration,
- tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+ tags: map<TagInstance, string>(this.Tags, 'name'),
createdAt: this.createdAt,
updatedAt: this.updatedAt,
extname: this.extname,
return json
}
-transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.TranscodeVideofileCallback) {
+transcodeVideofile = function (this: VideoInstance) {
const video = this
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const videoInputPath = join(videosDirectory, video.getVideoFilename())
const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
- ffmpeg(videoInputPath)
- .output(videoOutputPath)
- .videoCodec('libx264')
- .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
- .outputOption('-movflags faststart')
- .on('error', finalCallback)
- .on('end', function () {
- series([
- function removeOldFile (callback) {
- fs.unlink(videoInputPath, callback)
- },
-
- function moveNewFile (callback) {
- // Important to do this before getVideoFilename() to take in account the new file extension
- video.set('extname', newExtname)
-
- const newVideoPath = join(videosDirectory, video.getVideoFilename())
- fs.rename(videoOutputPath, newVideoPath, callback)
- },
-
- function torrent (callback) {
- const newVideoPath = join(videosDirectory, video.getVideoFilename())
- createTorrentFromVideo(video, newVideoPath, callback)
- },
-
- function videoExtension (callback) {
- video.save().asCallback(callback)
- }
-
- ], function (err: Error) {
- if (err) {
- // Autodesctruction...
- video.destroy().asCallback(function (err) {
- if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
+ return new Promise<void>((res, rej) => {
+ ffmpeg(videoInputPath)
+ .output(videoOutputPath)
+ .videoCodec('libx264')
+ .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
+ .outputOption('-movflags faststart')
+ .on('error', rej)
+ .on('end', () => {
+
+ return unlinkPromise(videoInputPath)
+ .then(() => {
+ // Important to do this before getVideoFilename() to take in account the new file extension
+ video.set('extname', newExtname)
+
+ const newVideoPath = join(videosDirectory, video.getVideoFilename())
+ return renamePromise(videoOutputPath, newVideoPath)
})
+ .then(() => {
+ const newVideoPath = join(videosDirectory, video.getVideoFilename())
+ return createTorrentFromVideo(video, newVideoPath)
+ })
+ .then(() => {
+ return video.save()
+ })
+ .then(() => {
+ return res()
+ })
+ .catch(err => {
+ // Autodesctruction...
+ video.destroy().asCallback(function (err) {
+ if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
+ })
- return finalCallback(err)
- }
-
- return finalCallback(null)
+ return rej(err)
+ })
})
- })
- .run()
+ .run()
+ })
}
// ------------------------------ STATICS ------------------------------
-generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string, callback: VideoMethods.GenerateThumbnailFromDataCallback) {
+generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string) {
// Creating the thumbnail for a remote video
const thumbnailName = video.getThumbnailName()
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
- fs.writeFile(thumbnailPath, Buffer.from(thumbnailData, 'binary'), function (err) {
- if (err) return callback(err)
-
- return callback(null, thumbnailName)
+ return writeFilePromise(thumbnailPath, Buffer.from(thumbnailData, 'binary')).then(() => {
+ return thumbnailName
})
}
-getDurationFromFile = function (videoPath: string, callback: VideoMethods.GetDurationFromFileCallback) {
- ffmpeg.ffprobe(videoPath, function (err, metadata) {
- if (err) return callback(err)
+getDurationFromFile = function (videoPath: string) {
+ return new Promise<number>((res, rej) => {
+ ffmpeg.ffprobe(videoPath, function (err, metadata) {
+ if (err) return rej(err)
- return callback(null, Math.floor(metadata.format.duration))
+ return res(Math.floor(metadata.format.duration))
+ })
})
}
-list = function (callback: VideoMethods.ListCallback) {
- return Video.findAll().asCallback(callback)
+list = function () {
+ return Video.findAll()
}
-listForApi = function (start: number, count: number, sort: string, callback: VideoMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
// Exclude Blakclisted videos from the list
const query = {
distinct: true,
where: createBaseVideosWhere()
}
- return Video.findAndCountAll(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- return callback(null, result.rows, result.count)
+ return Video.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
})
}
-loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback: VideoMethods.LoadByHostAndRemoteIdCallback) {
+loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
const query = {
where: {
remoteId: remoteId
]
}
- return Video.findOne(query).asCallback(callback)
+ return Video.findOne(query)
}
-listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAndPopulateAuthorAndTagsCallback) {
+listOwnedAndPopulateAuthorAndTags = function () {
// If remoteId is null this is *our* video
const query = {
where: {
include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
}
- return Video.findAll(query).asCallback(callback)
+ return Video.findAll(query)
}
-listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedByAuthorCallback) {
+listOwnedByAuthor = function (author: string) {
const query = {
where: {
remoteId: null
]
}
- return Video.findAll(query).asCallback(callback)
+ return Video.findAll(query)
}
-load = function (id: string, callback: VideoMethods.LoadCallback) {
- return Video.findById(id).asCallback(callback)
+load = function (id: string) {
+ return Video.findById(id)
}
-loadAndPopulateAuthor = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorCallback) {
+loadAndPopulateAuthor = function (id: string) {
const options = {
include: [ Video['sequelize'].models.Author ]
}
- return Video.findById(id, options).asCallback(callback)
+ return Video.findById(id, options)
}
-loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorAndPodAndTagsCallback) {
+loadAndPopulateAuthorAndPodAndTags = function (id: string) {
const options = {
include: [
{
]
}
- return Video.findById(id, options).asCallback(callback)
+ return Video.findById(id, options)
}
-searchAndPopulateAuthorAndPodAndTags = function (
- value: string,
- field: string,
- start: number,
- count: number,
- sort: string,
- callback: VideoMethods.SearchAndPopulateAuthorAndPodAndTagsCallback
-) {
+searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
const podInclude: any = {
model: Video['sequelize'].models.Pod,
required: false
} else if (field === 'tags') {
const escapedValue = Video['sequelize'].escape('%' + value + '%')
query.where.id.$in = Video['sequelize'].literal(
- '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')'
+ `(SELECT "VideoTags"."videoId"
+ FROM "Tags"
+ INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
+ WHERE name LIKE ${escapedValue}
+ )`
)
} else if (field === 'host') {
// FIXME: Include our pod? (not stored in the database)
// query.include.push([ Video['sequelize'].models.Tag ])
}
- return Video.findAndCountAll(query).asCallback(function (err, result) {
- if (err) return callback(err)
-
- return callback(null, result.rows, result.count)
+ return Video.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
})
}
}
}
-function removeThumbnail (video: VideoInstance, callback: (err: Error) => void) {
+function removeThumbnail (video: VideoInstance) {
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName())
- fs.unlink(thumbnailPath, callback)
+ return unlinkPromise(thumbnailPath)
}
-function removeFile (video: VideoInstance, callback: (err: Error) => void) {
+function removeFile (video: VideoInstance) {
const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
- fs.unlink(filePath, callback)
+ return unlinkPromise(filePath)
}
-function removeTorrent (video: VideoInstance, callback: (err: Error) => void) {
+function removeTorrent (video: VideoInstance) {
const torrenPath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
- fs.unlink(torrenPath, callback)
+ return unlinkPromise(torrenPath)
}
-function removePreview (video: VideoInstance, callback: (err: Error) => void) {
+function removePreview (video: VideoInstance) {
// Same name than video thumnail
- fs.unlink(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback)
+ return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName())
}
-function createTorrentFromVideo (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
+function createTorrentFromVideo (video: VideoInstance, videoPath: string) {
const options = {
announceList: [
[ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
]
}
- createTorrent(videoPath, options, function (err, torrent) {
- if (err) return callback(err)
-
- const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
- fs.writeFile(filePath, torrent, function (err) {
- if (err) return callback(err)
-
+ return createTorrentPromise(videoPath, options)
+ .then(torrent => {
+ const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
+ return writeFilePromise(filePath, torrent).then(() => torrent)
+ })
+ .then(torrent => {
const parsedTorrent = parseTorrent(torrent)
video.set('infoHash', parsedTorrent.infoHash)
- video.validate().asCallback(callback)
+ return video.validate()
})
- })
}
-function createPreview (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
- generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null, callback)
+function createPreview (video: VideoInstance, videoPath: string) {
+ return generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null)
}
-function createThumbnail (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
- generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE, callback)
+function createThumbnail (video: VideoInstance, videoPath: string) {
+ return generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE)
}
-type GenerateImageCallback = (err: Error, imageName: string) => void
-function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string, callback?: GenerateImageCallback) {
+function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string) {
const options: any = {
filename: imageName,
count: 1,
options.size = size
}
- ffmpeg(videoPath)
- .on('error', callback)
- .on('end', function () {
- callback(null, imageName)
- })
- .thumbnail(options)
+ return new Promise<string>((res, rej) => {
+ ffmpeg(videoPath)
+ .on('error', rej)
+ .on('end', function () {
+ return res(imageName)
+ })
+ .thumbnail(options)
+ })
}
-function removeFromBlacklist (video: VideoInstance, callback: (err: Error) => void) {
+function removeFromBlacklist (video: VideoInstance) {
// Find the blacklisted video
- db.BlacklistedVideo.loadByVideoId(video.id, function (err, video) {
- // If an error occured, stop here
- if (err) {
- logger.error('Error when fetching video from blacklist.', { error: err })
- return callback(err)
+ return db.BlacklistedVideo.loadByVideoId(video.id).then(video => {
+ // Not found the video, skip
+ if (!video) {
+ return null
}
// If we found the video, remove it from the blacklist
- if (video) {
- video.destroy().asCallback(callback)
- } else {
- // If haven't found it, simply ignore it and do nothing
- return callback(null)
- }
+ return video.destroy()
})
}
function (next) {
setTimeout(next, 22000)
},
+ // Check the pods 1, 2 expulsed pod 4
+ function (next) {
+ each([ 1, 2 ], function (i, callback) {
+ getFriendsList(i, function (err, res) {
+ if (err) throw err
+
+ // Pod 4 should not be our friend
+ const result = res.body.data
+ expect(result.length).to.equal(2)
+ for (const pod of result) {
+ expect(pod.host).not.equal(servers[3].host)
+ }
+
+ callback()
+ })
+ }, next)
+ },
// Rerun server 4
function (next) {
serversUtils.runServer(4, function (server) {
next()
})
},
- // Pod 6 ask pod 1, 2 and 3
+ // Pod 6 asks pod 1, 2 and 3
function (next) {
makeFriends(6, next)
},
export * from './job.model'
export * from './oauth-client-local.model'
export * from './pod.model'
+export * from './result-list.model'
export * from './request-scheduler.model'
export * from './user-video-rate.model'
export * from './user.model'
--- /dev/null
+export interface ResultList<T> {
+ total: number
+ data: T[]
+}
dependencies:
"@types/express" "*"
-"@types/node@*", "@types/node@^7.0.18":
- version "7.0.32"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.32.tgz#6afe6c66520a4c316623a14aef123908d01b4bba"
+"@types/node@*", "@types/node@^8.0.3":
+ version "8.0.3"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.3.tgz#fca61c26f83e5f453166114f57d53a47feb36d45"
"@types/request@^0.0.44":
version "0.0.44"
version "2.11.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
-bluebird@^3.0.5, bluebird@^3.4.0, bluebird@^3.4.6:
+bluebird@^3.0.5, bluebird@^3.4.0, bluebird@^3.4.6, bluebird@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typescript@^2.3.4:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd"
+typescript@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc"
uid-number@^0.0.6, uid-number@~0.0.6:
version "0.0.6"