Move to promises
Closes https://github.com/Chocobozzz/PeerTube/issues/74
This commit is contained in:
parent
5fe7e89831
commit
6fcd19ba73
|
@ -49,6 +49,7 @@
|
||||||
"async": "^2.0.0",
|
"async": "^2.0.0",
|
||||||
"bcrypt": "^1.0.2",
|
"bcrypt": "^1.0.2",
|
||||||
"bittorrent-tracker": "^9.0.0",
|
"bittorrent-tracker": "^9.0.0",
|
||||||
|
"bluebird": "^3.5.0",
|
||||||
"body-parser": "^1.12.4",
|
"body-parser": "^1.12.4",
|
||||||
"concurrently": "^3.1.0",
|
"concurrently": "^3.1.0",
|
||||||
"config": "^1.14.0",
|
"config": "^1.14.0",
|
||||||
|
@ -78,7 +79,7 @@
|
||||||
"scripty": "^1.5.0",
|
"scripty": "^1.5.0",
|
||||||
"sequelize": "4.0.0-2",
|
"sequelize": "4.0.0-2",
|
||||||
"ts-node": "^3.0.6",
|
"ts-node": "^3.0.6",
|
||||||
"typescript": "^2.3.4",
|
"typescript": "^2.4.1",
|
||||||
"validator": "^7.0.0",
|
"validator": "^7.0.0",
|
||||||
"winston": "^2.1.1",
|
"winston": "^2.1.1",
|
||||||
"ws": "^2.0.0"
|
"ws": "^2.0.0"
|
||||||
|
@ -95,7 +96,7 @@
|
||||||
"@types/mkdirp": "^0.3.29",
|
"@types/mkdirp": "^0.3.29",
|
||||||
"@types/morgan": "^1.7.32",
|
"@types/morgan": "^1.7.32",
|
||||||
"@types/multer": "^0.0.34",
|
"@types/multer": "^0.0.34",
|
||||||
"@types/node": "^7.0.18",
|
"@types/node": "^8.0.3",
|
||||||
"@types/request": "^0.0.44",
|
"@types/request": "^0.0.44",
|
||||||
"@types/sequelize": "^4.0.55",
|
"@types/sequelize": "^4.0.55",
|
||||||
"@types/validator": "^6.2.0",
|
"@types/validator": "^6.2.0",
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
import * as eachSeries from 'async/eachSeries'
|
|
||||||
import * as rimraf from 'rimraf'
|
import * as rimraf from 'rimraf'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { CONFIG } from '../../../server/initializers/constants'
|
import { CONFIG } from '../../../server/initializers/constants'
|
||||||
import { database as db } from '../../../server/initializers/database'
|
import { database as db } from '../../../server/initializers/database'
|
||||||
|
|
||||||
db.init(true, function () {
|
db.init(true)
|
||||||
db.sequelize.drop().asCallback(function (err) {
|
.then(() => {
|
||||||
if (err) throw err
|
return db.sequelize.drop()
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
|
console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
|
||||||
|
|
||||||
const STORAGE = CONFIG.STORAGE
|
const STORAGE = CONFIG.STORAGE
|
||||||
eachSeries(Object.keys(STORAGE), function (storage, callbackEach) {
|
Promise.mapSeries(Object.keys(STORAGE), storage => {
|
||||||
const storageDir = STORAGE[storage]
|
const storageDir = STORAGE[storage]
|
||||||
|
|
||||||
|
return new Promise((res, rej) => {
|
||||||
rimraf(storageDir, function (err) {
|
rimraf(storageDir, function (err) {
|
||||||
|
if (err) return rej(err)
|
||||||
|
|
||||||
console.info('%s deleted.', storageDir)
|
console.info('%s deleted.', storageDir)
|
||||||
return callbackEach(err)
|
return res()
|
||||||
})
|
|
||||||
}, function () {
|
|
||||||
process.exit(0)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
})
|
||||||
|
|
|
@ -11,13 +11,11 @@ if (program.user === undefined) {
|
||||||
process.exit(-1)
|
process.exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.init(true, function () {
|
db.init(true)
|
||||||
db.User.loadByUsername(program.user, function (err, user) {
|
.then(() => {
|
||||||
if (err) {
|
return db.User.loadByUsername(program.user)
|
||||||
console.error(err)
|
})
|
||||||
return
|
.then(user => {
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
console.error('User unknown.')
|
console.error('User unknown.')
|
||||||
return
|
return
|
||||||
|
@ -40,15 +38,9 @@ db.init(true, function () {
|
||||||
rl.on('line', function (password) {
|
rl.on('line', function (password) {
|
||||||
user.password = password
|
user.password = password
|
||||||
|
|
||||||
user.save().asCallback(function (err) {
|
user.save()
|
||||||
if (err) {
|
.then(() => console.log('User password updated.'))
|
||||||
console.error(err)
|
.catch(err => console.error(err))
|
||||||
} else {
|
.finally(() => process.exit(0))
|
||||||
console.log('User password updated.')
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env sh
|
#!/bin/bash
|
||||||
|
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
|
@ -6,5 +6,5 @@ cd client || exit -1
|
||||||
npm test || exit -1
|
npm test || exit -1
|
||||||
|
|
||||||
cd .. || 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
|
mocha --bail server/tests
|
||||||
|
|
|
@ -5,19 +5,20 @@ import { CONFIG, STATIC_PATHS } from '../server/initializers/constants'
|
||||||
import { database as db } from '../server/initializers/database'
|
import { database as db } from '../server/initializers/database'
|
||||||
import { hasFriends } from '../server/lib/friends'
|
import { hasFriends } from '../server/lib/friends'
|
||||||
|
|
||||||
db.init(true, function () {
|
db.init(true)
|
||||||
hasFriends(function (err, itHasFriends) {
|
.then(() => {
|
||||||
if (err) throw err
|
return hasFriends()
|
||||||
|
})
|
||||||
|
.then(itHasFriends => {
|
||||||
if (itHasFriends === true) {
|
if (itHasFriends === true) {
|
||||||
console.log('Cannot update host because you have friends!')
|
console.log('Cannot update host because you have friends!')
|
||||||
process.exit(-1)
|
process.exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Updating torrent files.')
|
console.log('Updating torrent files.')
|
||||||
db.Video.list(function (err, videos) {
|
return db.Video.list()
|
||||||
if (err) throw err
|
})
|
||||||
|
.then(videos => {
|
||||||
videos.forEach(function (video) {
|
videos.forEach(function (video) {
|
||||||
const torrentName = video.id + '.torrent'
|
const torrentName = video.id + '.torrent'
|
||||||
const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
|
const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
|
||||||
|
@ -33,5 +34,3 @@ db.init(true, function () {
|
||||||
|
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
20
server.ts
20
server.ts
|
@ -29,7 +29,7 @@ import { logger } from './server/helpers/logger'
|
||||||
import { API_VERSION, CONFIG } from './server/initializers/constants'
|
import { API_VERSION, CONFIG } from './server/initializers/constants'
|
||||||
// Initialize database and models
|
// Initialize database and models
|
||||||
import { database as db } from './server/initializers/database'
|
import { database as db } from './server/initializers/database'
|
||||||
db.init(false, onDatabaseInitDone)
|
db.init(false).then(() => onDatabaseInitDone())
|
||||||
|
|
||||||
// ----------- Checker -----------
|
// ----------- Checker -----------
|
||||||
import { checkMissedConfig, checkFFmpeg, checkConfig } from './server/initializers/checker'
|
import { checkMissedConfig, checkFFmpeg, checkConfig } from './server/initializers/checker'
|
||||||
|
@ -38,11 +38,7 @@ const missed = checkMissedConfig()
|
||||||
if (missed.length !== 0) {
|
if (missed.length !== 0) {
|
||||||
throw new Error('Miss some configurations keys : ' + missed)
|
throw new Error('Miss some configurations keys : ' + missed)
|
||||||
}
|
}
|
||||||
checkFFmpeg(function (err) {
|
checkFFmpeg()
|
||||||
if (err) {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const errorMessage = checkConfig()
|
const errorMessage = checkConfig()
|
||||||
if (errorMessage !== null) {
|
if (errorMessage !== null) {
|
||||||
|
@ -138,12 +134,11 @@ app.use(function (err, req, res, next) {
|
||||||
function onDatabaseInitDone () {
|
function onDatabaseInitDone () {
|
||||||
const port = CONFIG.LISTEN.PORT
|
const port = CONFIG.LISTEN.PORT
|
||||||
// Run the migration scripts if needed
|
// Run the migration scripts if needed
|
||||||
migrate(function (err) {
|
migrate()
|
||||||
if (err) throw err
|
.then(() => {
|
||||||
|
return installApplication()
|
||||||
installApplication(function (err) {
|
})
|
||||||
if (err) throw err
|
.then(() => {
|
||||||
|
|
||||||
// ----------- Make the server listening -----------
|
// ----------- Make the server listening -----------
|
||||||
server.listen(port, function () {
|
server.listen(port, function () {
|
||||||
// Activate the communication with friends
|
// Activate the communication with friends
|
||||||
|
@ -156,5 +151,4 @@ function onDatabaseInitDone () {
|
||||||
logger.info('Webserver: %s', CONFIG.WEBSERVER.URL)
|
logger.info('Webserver: %s', CONFIG.WEBSERVER.URL)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ function getLocalClient (req: express.Request, res: express.Response, next: expr
|
||||||
return res.type('json').status(403).end()
|
return res.type('json').status(403).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
db.OAuthClient.loadFirstClient(function (err, client) {
|
db.OAuthClient.loadFirstClient()
|
||||||
if (err) return next(err)
|
.then(client => {
|
||||||
if (!client) return next(new Error('No client available.'))
|
if (!client) throw new Error('No client available.')
|
||||||
|
|
||||||
const json: OAuthClientLocal = {
|
const json: OAuthClientLocal = {
|
||||||
client_id: client.clientId,
|
client_id: client.clientId,
|
||||||
|
@ -34,6 +34,7 @@ function getLocalClient (req: express.Request, res: express.Response, next: expr
|
||||||
}
|
}
|
||||||
res.json(json)
|
res.json(json)
|
||||||
})
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { CONFIG } from '../../initializers'
|
import { CONFIG } from '../../initializers'
|
||||||
|
@ -57,65 +56,39 @@ export {
|
||||||
function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const informations = req.body
|
const informations = req.body
|
||||||
|
|
||||||
waterfall<string, Error>([
|
|
||||||
function addPod (callback) {
|
|
||||||
const pod = db.Pod.build(informations)
|
const pod = db.Pod.build(informations)
|
||||||
pod.save().asCallback(function (err, podCreated) {
|
pod.save()
|
||||||
// Be sure about the number of parameters for the callback
|
.then(podCreated => {
|
||||||
return callback(err, podCreated)
|
return sendOwnedVideosToPod(podCreated.id)
|
||||||
})
|
})
|
||||||
},
|
.then(() => {
|
||||||
|
return getMyPublicCert()
|
||||||
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)
|
|
||||||
})
|
})
|
||||||
}
|
.then(cert => {
|
||||||
], function (err, cert) {
|
|
||||||
if (err) return next(err)
|
|
||||||
|
|
||||||
return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
|
return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
|
||||||
})
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
db.Pod.list(function (err, podsList) {
|
db.Pod.list()
|
||||||
if (err) return next(err)
|
.then(podsList => res.json(getFormatedObjects(podsList, podsList.length)))
|
||||||
|
.catch(err => next(err))
|
||||||
res.json(getFormatedObjects(podsList, podsList.length))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const hosts = req.body.hosts as string[]
|
const hosts = req.body.hosts as string[]
|
||||||
|
|
||||||
makeFriends(hosts, function (err) {
|
makeFriends(hosts)
|
||||||
if (err) {
|
.then(() => logger.info('Made friends!'))
|
||||||
logger.error('Could not make friends.', { error: err })
|
.catch(err => logger.error('Could not make friends.', { error: err }))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Made friends!')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Don't wait the process that could be long
|
||||||
res.type('json').status(204).end()
|
res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
quitFriends(function (err) {
|
quitFriends()
|
||||||
if (err) return next(err)
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => next(err))
|
||||||
res.type('json').status(204).end()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as waterfall from 'async/waterfall'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { checkSignature, signatureValidator } from '../../../middlewares'
|
import { checkSignature, signatureValidator } from '../../../middlewares'
|
||||||
|
@ -24,17 +23,10 @@ export {
|
||||||
function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const host = req.body.signature.host
|
const host = req.body.signature.host
|
||||||
|
|
||||||
waterfall([
|
db.Pod.loadByHost(host)
|
||||||
function loadPod (callback) {
|
.then(pod => {
|
||||||
db.Pod.loadByHost(host, callback)
|
return pod.destroy()
|
||||||
},
|
|
||||||
|
|
||||||
function deletePod (pod, callback) {
|
|
||||||
pod.destroy().asCallback(callback)
|
|
||||||
}
|
|
||||||
], function (err) {
|
|
||||||
if (err) return next(err)
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
})
|
||||||
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Promise from 'bluebird'
|
||||||
import { eachSeries, waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import {
|
import {
|
||||||
|
@ -16,20 +15,14 @@ import {
|
||||||
remoteQaduVideosValidator,
|
remoteQaduVideosValidator,
|
||||||
remoteEventsVideosValidator
|
remoteEventsVideosValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import {
|
import { logger, retryTransactionWrapper } from '../../../helpers'
|
||||||
logger,
|
|
||||||
commitTransaction,
|
|
||||||
retryTransactionWrapper,
|
|
||||||
rollbackTransaction,
|
|
||||||
startSerializableTransaction
|
|
||||||
} from '../../../helpers'
|
|
||||||
import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
|
import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
|
||||||
import { PodInstance, VideoInstance } from '../../../models'
|
import { PodInstance, VideoInstance } from '../../../models'
|
||||||
|
|
||||||
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
|
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
|
||||||
|
|
||||||
// Functions to call when processing a remote request
|
// 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.ADD] = addRemoteVideoRetryWrapper
|
||||||
functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
|
functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
|
||||||
functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
|
functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
|
||||||
|
@ -72,20 +65,19 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
|
||||||
|
|
||||||
// We need to process in the same order to keep consistency
|
// We need to process in the same order to keep consistency
|
||||||
// TODO: optimization
|
// TODO: optimization
|
||||||
eachSeries(requests, function (request: any, callbackEach) {
|
Promise.mapSeries(requests, (request: any) => {
|
||||||
const data = request.data
|
const data = request.data
|
||||||
|
|
||||||
// Get the function we need to call in order to process the request
|
// Get the function we need to call in order to process the request
|
||||||
const fun = functionsHash[request.type]
|
const fun = functionsHash[request.type]
|
||||||
if (fun === undefined) {
|
if (fun === undefined) {
|
||||||
logger.error('Unkown remote request type %s.', request.type)
|
logger.error('Unkown remote request type %s.', request.type)
|
||||||
return callbackEach(null)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fun.call(this, data, fromPod, callbackEach)
|
return fun.call(this, data, fromPod)
|
||||||
}, function (err) {
|
|
||||||
if (err) logger.error('Error managing remote videos.', { error: err })
|
|
||||||
})
|
})
|
||||||
|
.catch(err => logger.error('Error managing remote videos.', { error: err }))
|
||||||
|
|
||||||
// We don't need to keep the other pod waiting
|
// We don't need to keep the other pod waiting
|
||||||
return res.type('json').status(204).end()
|
return res.type('json').status(204).end()
|
||||||
|
@ -95,13 +87,12 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
|
||||||
const requests = req.body.data
|
const requests = req.body.data
|
||||||
const fromPod = res.locals.secure.pod
|
const fromPod = res.locals.secure.pod
|
||||||
|
|
||||||
eachSeries(requests, function (request: any, callbackEach) {
|
Promise.mapSeries(requests, (request: any) => {
|
||||||
const videoData = request.data
|
const videoData = request.data
|
||||||
|
|
||||||
quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod, callbackEach)
|
return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
|
||||||
}, function (err) {
|
|
||||||
if (err) logger.error('Error managing remote videos.', { error: err })
|
|
||||||
})
|
})
|
||||||
|
.catch(err => logger.error('Error managing remote videos.', { error: err }))
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
return res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
@ -110,37 +101,30 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
|
||||||
const requests = req.body.data
|
const requests = req.body.data
|
||||||
const fromPod = res.locals.secure.pod
|
const fromPod = res.locals.secure.pod
|
||||||
|
|
||||||
eachSeries(requests, function (request: any, callbackEach) {
|
Promise.mapSeries(requests, (request: any) => {
|
||||||
const eventData = request.data
|
const eventData = request.data
|
||||||
|
|
||||||
processVideosEventsRetryWrapper(eventData, fromPod, callbackEach)
|
return processVideosEventsRetryWrapper(eventData, fromPod)
|
||||||
}, function (err) {
|
|
||||||
if (err) logger.error('Error managing remote videos.', { error: err })
|
|
||||||
})
|
})
|
||||||
|
.catch(err => logger.error('Error managing remote videos.', { error: err }))
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
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 = {
|
const options = {
|
||||||
arguments: [ eventData, fromPod ],
|
arguments: [ eventData, fromPod ],
|
||||||
errorMessage: 'Cannot process videos events with many retries.'
|
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) {
|
function processVideosEvents (eventData: any, fromPod: PodInstance) {
|
||||||
waterfall([
|
|
||||||
startSerializableTransaction,
|
|
||||||
|
|
||||||
function findVideo (t, callback) {
|
return db.sequelize.transaction(t => {
|
||||||
fetchOwnedVideo(eventData.remoteId, function (err, videoInstance) {
|
return fetchOwnedVideo(eventData.remoteId)
|
||||||
return callback(err, t, videoInstance)
|
.then(videoInstance => {
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function updateVideoIntoDB (t, videoInstance, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
let columnToUpdate
|
let columnToUpdate
|
||||||
|
@ -163,18 +147,15 @@ function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallbac
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return callback(new Error('Unknown video event type.'))
|
throw new Error('Unknown video event type.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = {}
|
const query = {}
|
||||||
query[columnToUpdate] = eventData.count
|
query[columnToUpdate] = eventData.count
|
||||||
|
|
||||||
videoInstance.increment(query, options).asCallback(function (err) {
|
return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
|
||||||
return callback(err, t, videoInstance, qaduType)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ videoInstance, qaduType }) => {
|
||||||
|
|
||||||
function sendQaduToFriends (t, videoInstance, qaduType, callback) {
|
|
||||||
const qadusParams = [
|
const qadusParams = [
|
||||||
{
|
{
|
||||||
videoId: videoInstance.id,
|
videoId: videoInstance.id,
|
||||||
|
@ -182,46 +163,31 @@ function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallbac
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
|
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
|
||||||
commitTransaction
|
.catch(err => {
|
||||||
|
|
||||||
], function (err: Error, t: Sequelize.Transaction) {
|
|
||||||
if (err) {
|
|
||||||
logger.debug('Cannot process a video event.', { error: err })
|
logger.debug('Cannot process a video event.', { error: err })
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Remote video event processed for video %s.', eventData.remoteId)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
|
function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ videoData, fromPod ],
|
arguments: [ videoData, fromPod ],
|
||||||
errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
|
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
|
let videoName
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
startSerializableTransaction,
|
return fetchRemoteVideo(fromPod.host, videoData.remoteId)
|
||||||
|
.then(videoInstance => {
|
||||||
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 }
|
const options = { transaction: t }
|
||||||
|
|
||||||
videoName = videoInstance.name
|
videoName = videoInstance.name
|
||||||
|
@ -238,71 +204,47 @@ function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance, finalCa
|
||||||
videoInstance.set('dislikes', videoData.dislikes)
|
videoInstance.set('dislikes', videoData.dislikes)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoInstance.save(options).asCallback(function (err) {
|
return videoInstance.save(options)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
|
||||||
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
|
// Handle retries on fail
|
||||||
function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
|
function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ videoToCreateData, fromPod ],
|
arguments: [ videoToCreateData, fromPod ],
|
||||||
errorMessage: 'Cannot insert the remote video with many retries.'
|
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)
|
logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
|
||||||
|
|
||||||
waterfall([
|
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.')
|
||||||
|
|
||||||
startSerializableTransaction,
|
return undefined
|
||||||
|
|
||||||
function assertRemoteIdAndHostUnique (t, callback) {
|
|
||||||
db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId, function (err, video) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
if (video) return callback(new Error('RemoteId and host pair is not unique.'))
|
|
||||||
|
|
||||||
return callback(null, t)
|
|
||||||
})
|
})
|
||||||
},
|
.then(() => {
|
||||||
|
|
||||||
function findOrCreateAuthor (t, callback) {
|
|
||||||
const name = videoToCreateData.author
|
const name = videoToCreateData.author
|
||||||
const podId = fromPod.id
|
const podId = fromPod.id
|
||||||
// This author is from another pod so we do not associate a user
|
// This author is from another pod so we do not associate a user
|
||||||
const userId = null
|
const userId = null
|
||||||
|
|
||||||
db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
|
return db.Author.findOrCreateAuthor(name, podId, userId, t)
|
||||||
return callback(err, t, authorInstance)
|
|
||||||
})
|
})
|
||||||
},
|
.then(author => {
|
||||||
|
|
||||||
function findOrCreateTags (t, author, callback) {
|
|
||||||
const tags = videoToCreateData.tags
|
const tags = videoToCreateData.tags
|
||||||
|
|
||||||
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
|
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
|
||||||
return callback(err, t, author, tagInstances)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ author, tagInstances }) => {
|
||||||
|
|
||||||
function createVideoObject (t, author, tagInstances, callback) {
|
|
||||||
const videoData = {
|
const videoData = {
|
||||||
name: videoToCreateData.name,
|
name: videoToCreateData.name,
|
||||||
remoteId: videoToCreateData.remoteId,
|
remoteId: videoToCreateData.remoteId,
|
||||||
|
@ -324,87 +266,54 @@ function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance, finalCall
|
||||||
}
|
}
|
||||||
|
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
|
return { tagInstances, video }
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(err, t, tagInstances, video)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ tagInstances, video }) => {
|
||||||
|
return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
|
||||||
function insertVideoIntoDB (t, tagInstances, video, callback) {
|
})
|
||||||
|
.then(({ tagInstances, video }) => {
|
||||||
const options = {
|
const options = {
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
||||||
video.save(options).asCallback(function (err, videoCreated) {
|
return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
|
||||||
return callback(err, t, tagInstances, videoCreated)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ tagInstances, videoCreated }) => {
|
||||||
|
|
||||||
function associateTagsToVideo (t, tagInstances, video, callback) {
|
|
||||||
const options = {
|
const options = {
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
||||||
video.setTags(tagInstances, options).asCallback(function (err) {
|
return videoCreated.setTags(tagInstances, options)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => logger.info('Remote video %s inserted.', videoToCreateData.name))
|
||||||
commitTransaction
|
.catch(err => {
|
||||||
|
|
||||||
], 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 })
|
logger.debug('Cannot insert the remote video.', { error: err })
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Remote video %s inserted.', videoToCreateData.name)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle retries on fail
|
// Handle retries on fail
|
||||||
function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
|
function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ videoAttributesToUpdate, fromPod ],
|
arguments: [ videoAttributesToUpdate, fromPod ],
|
||||||
errorMessage: 'Cannot update the remote video with many retries'
|
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)
|
logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
|
return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
|
||||||
startSerializableTransaction,
|
.then(videoInstance => {
|
||||||
|
|
||||||
function findVideo (t, callback) {
|
|
||||||
fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
|
|
||||||
return callback(err, t, videoInstance)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function findOrCreateTags (t, videoInstance, callback) {
|
|
||||||
const tags = videoAttributesToUpdate.tags
|
const tags = videoAttributesToUpdate.tags
|
||||||
|
|
||||||
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
|
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
|
||||||
return callback(err, t, videoInstance, tagInstances)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ videoInstance, tagInstances }) => {
|
||||||
|
|
||||||
function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
videoInstance.set('name', videoAttributesToUpdate.name)
|
videoInstance.set('name', videoAttributesToUpdate.name)
|
||||||
|
@ -422,61 +331,37 @@ function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance,
|
||||||
videoInstance.set('likes', videoAttributesToUpdate.likes)
|
videoInstance.set('likes', videoAttributesToUpdate.likes)
|
||||||
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
|
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
|
||||||
|
|
||||||
videoInstance.save(options).asCallback(function (err) {
|
return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
|
||||||
return callback(err, t, videoInstance, tagInstances)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ videoInstance, tagInstances }) => {
|
||||||
|
|
||||||
function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
videoInstance.setTags(tagInstances, options).asCallback(function (err) {
|
return videoInstance.setTags(tagInstances, options)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => logger.info('Remote video %s updated', videoAttributesToUpdate.name))
|
||||||
commitTransaction
|
.catch(err => {
|
||||||
|
|
||||||
], function (err: Error, t: Sequelize.Transaction) {
|
|
||||||
if (err) {
|
|
||||||
// This is just a debug because we will retry the insert
|
// This is just a debug because we will retry the insert
|
||||||
logger.debug('Cannot update the remote video.', { error: err })
|
logger.debug('Cannot update the remote video.', { error: err })
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Remote video %s updated', videoAttributesToUpdate.name)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
// We need the instance because we have to remove some other stuffs (thumbnail etc)
|
||||||
fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
|
return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
|
||||||
// Do not return the error, continue the process
|
.then(video => {
|
||||||
if (err) return callback(null)
|
|
||||||
|
|
||||||
logger.debug('Removing remote video %s.', video.remoteId)
|
logger.debug('Removing remote video %s.', video.remoteId)
|
||||||
video.destroy().asCallback(function (err) {
|
return video.destroy()
|
||||||
// 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)
|
|
||||||
})
|
})
|
||||||
|
.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) {
|
function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance) {
|
||||||
fetchOwnedVideo(reportData.videoRemoteId, function (err, video) {
|
return fetchOwnedVideo(reportData.videoRemoteId)
|
||||||
if (err || !video) {
|
.then(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)
|
logger.debug('Reporting remote abuse for video %s.', video.id)
|
||||||
|
|
||||||
const videoAbuseData = {
|
const videoAbuseData = {
|
||||||
|
@ -486,38 +371,33 @@ function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance, callback
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
db.VideoAbuse.create(videoAbuseData).asCallback(function (err) {
|
return db.VideoAbuse.create(videoAbuseData)
|
||||||
if (err) {
|
})
|
||||||
logger.error('Cannot create remote abuse video.', { error: err })
|
.catch(err => logger.error('Cannot create remote abuse video.', { error: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null)
|
function fetchOwnedVideo (id: string) {
|
||||||
})
|
return db.Video.load(id)
|
||||||
})
|
.then(video => {
|
||||||
}
|
if (!video) throw new Error('Video not found')
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
|
return video
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
logger.error('Cannot load owned video from id.', { error: err, id })
|
logger.error('Cannot load owned video from id.', { error: err, id })
|
||||||
return callback(err)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, video)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchRemoteVideo (podHost: string, remoteId: string, callback: (err: Error, video?: VideoInstance) => void) {
|
function fetchRemoteVideo (podHost: string, remoteId: string) {
|
||||||
db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
|
return db.Video.loadByHostAndRemoteId(podHost, remoteId)
|
||||||
if (err || !video) {
|
.then(video => {
|
||||||
if (!err) err = new Error('video not found')
|
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 })
|
logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId })
|
||||||
return callback(err)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, video)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { parallel } from 'async'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractRequestScheduler,
|
AbstractRequestScheduler,
|
||||||
|
@ -27,24 +27,19 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
parallel({
|
Promise.props({
|
||||||
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
|
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
|
||||||
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
|
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
|
||||||
requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
|
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) {
|
function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
|
||||||
return function (callback) {
|
return requestScheduler.remainingRequestsCount().then(count => {
|
||||||
requestScheduler.remainingRequestsCount(function (err, count) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const result: RequestSchedulerStatsAttributes = {
|
const result: RequestSchedulerStatsAttributes = {
|
||||||
totalRequests: count,
|
totalRequests: count,
|
||||||
requestsLimitPods: requestScheduler.limitPods,
|
requestsLimitPods: requestScheduler.limitPods,
|
||||||
|
@ -53,7 +48,6 @@ function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler)
|
||||||
milliSecondsInterval: requestScheduler.requestInterval
|
milliSecondsInterval: requestScheduler.requestInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, result)
|
return result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { CONFIG, USER_ROLES } from '../../initializers'
|
import { USER_ROLES } from '../../initializers'
|
||||||
import { logger, getFormatedObjects } from '../../helpers'
|
import { logger, getFormatedObjects } from '../../helpers'
|
||||||
import {
|
import {
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -87,78 +86,61 @@ function createUser (req: express.Request, res: express.Response, next: express.
|
||||||
role: USER_ROLES.USER
|
role: USER_ROLES.USER
|
||||||
})
|
})
|
||||||
|
|
||||||
user.save().asCallback(function (err) {
|
user.save()
|
||||||
if (err) return next(err)
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => next(err))
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
|
db.User.loadByUsername(res.locals.oauth.token.user.username)
|
||||||
if (err) return next(err)
|
.then(user => res.json(user.toFormatedJSON()))
|
||||||
|
.catch(err => next(err))
|
||||||
return res.json(user.toFormatedJSON())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const videoId = '' + req.params.videoId
|
const videoId = '' + req.params.videoId
|
||||||
const userId = +res.locals.oauth.token.User.id
|
const userId = +res.locals.oauth.token.User.id
|
||||||
|
|
||||||
db.UserVideoRate.load(userId, videoId, null, function (err, ratingObj) {
|
db.UserVideoRate.load(userId, videoId, null)
|
||||||
if (err) return next(err)
|
.then(ratingObj => {
|
||||||
|
|
||||||
const rating = ratingObj ? ratingObj.type : 'none'
|
const rating = ratingObj ? ratingObj.type : 'none'
|
||||||
|
|
||||||
const json: FormatedUserVideoRate = {
|
const json: FormatedUserVideoRate = {
|
||||||
videoId,
|
videoId,
|
||||||
rating
|
rating
|
||||||
}
|
}
|
||||||
res.json(json)
|
res.json(json)
|
||||||
})
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
|
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) {
|
db.User.listForApi(req.query.start, req.query.count, req.query.sort)
|
||||||
if (err) return next(err)
|
.then(resultList => {
|
||||||
|
res.json(getFormatedObjects(resultList.data, resultList.total))
|
||||||
res.json(getFormatedObjects(usersList, usersTotal))
|
|
||||||
})
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
waterfall([
|
db.User.loadById(req.params.id)
|
||||||
function loadUser (callback) {
|
.then(user => user.destroy())
|
||||||
db.User.loadById(req.params.id, callback)
|
.then(() => res.sendStatus(204))
|
||||||
},
|
.catch(err => {
|
||||||
|
|
||||||
function deleteUser (user, callback) {
|
|
||||||
user.destroy().asCallback(callback)
|
|
||||||
}
|
|
||||||
], function andFinally (err) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Errors when removed the user.', { error: err })
|
logger.error('Errors when removed the user.', { error: err })
|
||||||
return next(err)
|
return next(err)
|
||||||
}
|
|
||||||
|
|
||||||
return res.sendStatus(204)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
|
db.User.loadByUsername(res.locals.oauth.token.user.username)
|
||||||
if (err) return next(err)
|
.then(user => {
|
||||||
|
|
||||||
if (req.body.password) user.password = req.body.password
|
if (req.body.password) user.password = req.body.password
|
||||||
if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
|
if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
|
||||||
|
|
||||||
user.save().asCallback(function (err) {
|
return user.save()
|
||||||
if (err) return next(err)
|
|
||||||
|
|
||||||
return res.sendStatus(204)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
.then(() => res.sendStatus(204))
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
function success (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function success (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
import { waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import * as friends from '../../../lib/friends'
|
import * as friends from '../../../lib/friends'
|
||||||
import {
|
import {
|
||||||
logger,
|
logger,
|
||||||
getFormatedObjects,
|
getFormatedObjects,
|
||||||
retryTransactionWrapper,
|
retryTransactionWrapper
|
||||||
startSerializableTransaction,
|
|
||||||
commitTransaction,
|
|
||||||
rollbackTransaction
|
|
||||||
} from '../../../helpers'
|
} from '../../../helpers'
|
||||||
import {
|
import {
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -21,6 +16,7 @@ import {
|
||||||
setVideoAbusesSort,
|
setVideoAbusesSort,
|
||||||
setPagination
|
setPagination
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
|
import { VideoInstance } from '../../../models'
|
||||||
|
|
||||||
const abuseVideoRouter = express.Router()
|
const abuseVideoRouter = express.Router()
|
||||||
|
|
||||||
|
@ -48,11 +44,9 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
|
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) {
|
db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
|
||||||
if (err) return next(err)
|
.then(result => res.json(getFormatedObjects(result.data, result.total)))
|
||||||
|
.catch(err => next(err))
|
||||||
res.json(getFormatedObjects(abusesList, abusesTotal))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
@ -61,14 +55,12 @@ function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Respon
|
||||||
errorMessage: 'Cannot report abuse to the video with many retries.'
|
errorMessage: 'Cannot report abuse to the video with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
retryTransactionWrapper(reportVideoAbuse, options, function (err) {
|
retryTransactionWrapper(reportVideoAbuse, options)
|
||||||
if (err) return next(err)
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => next(err))
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 videoInstance = res.locals.video
|
||||||
const reporterUsername = res.locals.oauth.token.User.username
|
const reporterUsername = res.locals.oauth.token.User.username
|
||||||
|
|
||||||
|
@ -79,17 +71,9 @@ function reportVideoAbuse (req: express.Request, res: express.Response, finalCal
|
||||||
reporterPodId: null // This is our pod that reported this abuse
|
reporterPodId: null // This is our pod that reported this abuse
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
|
return db.VideoAbuse.create(abuse, { transaction: t })
|
||||||
startSerializableTransaction,
|
.then(abuse => {
|
||||||
|
|
||||||
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
|
// We send the information to the destination pod
|
||||||
if (videoInstance.isOwned() === false) {
|
if (videoInstance.isOwned() === false) {
|
||||||
const reportData = {
|
const reportData = {
|
||||||
|
@ -98,21 +82,15 @@ function reportVideoAbuse (req: express.Request, res: express.Response, finalCal
|
||||||
videoRemoteId: videoInstance.remoteId
|
videoRemoteId: videoInstance.remoteId
|
||||||
}
|
}
|
||||||
|
|
||||||
friends.reportAbuseVideoToFriend(reportData, videoInstance)
|
return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, t)
|
return videoInstance
|
||||||
},
|
})
|
||||||
|
})
|
||||||
commitTransaction
|
.then((videoInstance: VideoInstance) => logger.info('Abuse report for video %s created.', videoInstance.name))
|
||||||
|
.catch(err => {
|
||||||
], function andFinally (err: Error, t: Sequelize.Transaction) {
|
|
||||||
if (err) {
|
|
||||||
logger.debug('Cannot update the video.', { error: err })
|
logger.debug('Cannot update the video.', { error: err })
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Abuse report for video %s created.', videoInstance.name)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,10 @@ function addVideoToBlacklist (req: express.Request, res: express.Response, next:
|
||||||
videoId: videoInstance.id
|
videoId: videoInstance.id
|
||||||
}
|
}
|
||||||
|
|
||||||
db.BlacklistedVideo.create(toCreate).asCallback(function (err) {
|
db.BlacklistedVideo.create(toCreate)
|
||||||
if (err) {
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => {
|
||||||
logger.error('Errors when blacklisting video ', { error: err })
|
logger.error('Errors when blacklisting video ', { error: err })
|
||||||
return next(err)
|
return next(err)
|
||||||
}
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Promise from 'bluebird'
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as multer from 'multer'
|
import * as multer from 'multer'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import {
|
import {
|
||||||
|
@ -35,13 +33,12 @@ import {
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import {
|
import {
|
||||||
logger,
|
logger,
|
||||||
commitTransaction,
|
|
||||||
retryTransactionWrapper,
|
retryTransactionWrapper,
|
||||||
rollbackTransaction,
|
|
||||||
startSerializableTransaction,
|
|
||||||
generateRandomString,
|
generateRandomString,
|
||||||
getFormatedObjects
|
getFormatedObjects,
|
||||||
|
renamePromise
|
||||||
} from '../../../helpers'
|
} from '../../../helpers'
|
||||||
|
import { TagInstance } from '../../../models'
|
||||||
|
|
||||||
import { abuseVideoRouter } from './abuse'
|
import { abuseVideoRouter } from './abuse'
|
||||||
import { blacklistRouter } from './blacklist'
|
import { blacklistRouter } from './blacklist'
|
||||||
|
@ -60,9 +57,14 @@ const storage = multer.diskStorage({
|
||||||
if (file.mimetype === 'video/webm') extension = 'webm'
|
if (file.mimetype === 'video/webm') extension = 'webm'
|
||||||
else if (file.mimetype === 'video/mp4') extension = 'mp4'
|
else if (file.mimetype === 'video/mp4') extension = 'mp4'
|
||||||
else if (file.mimetype === 'video/ogg') extension = 'ogv'
|
else if (file.mimetype === 'video/ogg') extension = 'ogv'
|
||||||
generateRandomString(16, function (err, randomString) {
|
generateRandomString(16)
|
||||||
const fieldname = err ? undefined : randomString
|
.then(randomString => {
|
||||||
cb(null, fieldname + '.' + extension)
|
const filename = randomString
|
||||||
|
cb(null, filename + '.' + extension)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot generate random string for file name.', { error: err })
|
||||||
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -144,22 +146,18 @@ function addVideoRetryWrapper (req: express.Request, res: express.Response, next
|
||||||
errorMessage: 'Cannot insert the video with many retries.'
|
errorMessage: 'Cannot insert the video with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
retryTransactionWrapper(addVideo, options, function (err) {
|
retryTransactionWrapper(addVideo, options)
|
||||||
if (err) return next(err)
|
.then(() => {
|
||||||
|
|
||||||
// TODO : include Location of the new video -> 201
|
// TODO : include Location of the new video -> 201
|
||||||
return res.type('json').status(204).end()
|
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
|
const videoInfos = req.body
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
|
|
||||||
startSerializableTransaction,
|
|
||||||
|
|
||||||
function findOrCreateAuthor (t, callback) {
|
|
||||||
const user = res.locals.oauth.token.User
|
const user = res.locals.oauth.token.User
|
||||||
|
|
||||||
const name = user.username
|
const name = user.username
|
||||||
|
@ -167,20 +165,14 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
|
||||||
const podId = null
|
const podId = null
|
||||||
const userId = user.id
|
const userId = user.id
|
||||||
|
|
||||||
db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
|
return db.Author.findOrCreateAuthor(name, podId, userId, t)
|
||||||
return callback(err, t, authorInstance)
|
.then(author => {
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function findOrCreateTags (t, author, callback) {
|
|
||||||
const tags = videoInfos.tags
|
const tags = videoInfos.tags
|
||||||
|
if (!tags) return { author, tagInstances: undefined }
|
||||||
|
|
||||||
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
|
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
|
||||||
return callback(err, t, author, tagInstances)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ author, tagInstances }) => {
|
||||||
|
|
||||||
function createVideoObject (t, author, tagInstances, callback) {
|
|
||||||
const videoData = {
|
const videoData = {
|
||||||
name: videoInfos.name,
|
name: videoInfos.name,
|
||||||
remoteId: null,
|
remoteId: null,
|
||||||
|
@ -195,74 +187,56 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
|
||||||
}
|
}
|
||||||
|
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
|
return { author, tagInstances, video }
|
||||||
return callback(null, t, author, tagInstances, video)
|
})
|
||||||
},
|
.then(({ 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 videoDir = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const source = path.join(videoDir, videoFile.filename)
|
const source = path.join(videoDir, videoFile.filename)
|
||||||
const destination = path.join(videoDir, video.getVideoFilename())
|
const destination = path.join(videoDir, video.getVideoFilename())
|
||||||
|
|
||||||
fs.rename(source, destination, function (err) {
|
return renamePromise(source, destination)
|
||||||
if (err) return callback(err)
|
.then(() => {
|
||||||
|
// This is important in case if there is another attempt in the retry process
|
||||||
// This is important in case if there is another attempt
|
|
||||||
videoFile.filename = video.getVideoFilename()
|
videoFile.filename = video.getVideoFilename()
|
||||||
return callback(null, t, author, tagInstances, video)
|
return { author, tagInstances, video }
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(({ author, tagInstances, video }) => {
|
||||||
function insertVideoIntoDB (t, author, tagInstances, video, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
// Add tags association
|
return video.save(options)
|
||||||
video.save(options).asCallback(function (err, videoCreated) {
|
.then(videoCreated => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// Do not forget to add Author informations to the created video
|
// Do not forget to add Author informations to the created video
|
||||||
videoCreated.Author = author
|
videoCreated.Author = author
|
||||||
|
|
||||||
return callback(err, t, tagInstances, videoCreated)
|
return { tagInstances, video: videoCreated }
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(({ tagInstances, video }) => {
|
||||||
|
if (!tagInstances) return video
|
||||||
|
|
||||||
function associateTagsToVideo (t, tagInstances, video, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
return video.setTags(tagInstances, options)
|
||||||
video.setTags(tagInstances, options).asCallback(function (err) {
|
.then(() => {
|
||||||
video.Tags = tagInstances
|
video.Tags = tagInstances
|
||||||
|
return video
|
||||||
return callback(err, t, video)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(video => {
|
||||||
function sendToFriends (t, video, callback) {
|
|
||||||
// Let transcoding job send the video to friends because the videofile extension might change
|
// Let transcoding job send the video to friends because the videofile extension might change
|
||||||
if (CONFIG.TRANSCODING.ENABLED === true) return callback(null, t)
|
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
|
||||||
|
|
||||||
video.toAddRemoteJSON(function (err, remoteVideo) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
|
return video.toAddRemoteJSON()
|
||||||
|
.then(remoteVideo => {
|
||||||
// Now we'll add the video's meta data to our friends
|
// Now we'll add the video's meta data to our friends
|
||||||
addVideoToFriends(remoteVideo, t, function (err) {
|
return addVideoToFriends(remoteVideo, t)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => logger.info('Video with name %s created.', videoInfos.name))
|
||||||
commitTransaction
|
.catch((err: Error) => {
|
||||||
|
logger.debug('Cannot insert the video.', { error: err.stack })
|
||||||
], function andFinally (err: Error, t: Sequelize.Transaction) {
|
throw err
|
||||||
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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,34 +246,29 @@ function updateVideoRetryWrapper (req: express.Request, res: express.Response, n
|
||||||
errorMessage: 'Cannot update the video with many retries.'
|
errorMessage: 'Cannot update the video with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
retryTransactionWrapper(updateVideo, options, function (err) {
|
retryTransactionWrapper(updateVideo, options)
|
||||||
if (err) return next(err)
|
.then(() => {
|
||||||
|
|
||||||
// TODO : include Location of the new video -> 201
|
// TODO : include Location of the new video -> 201
|
||||||
return res.type('json').status(204).end()
|
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 videoInstance = res.locals.video
|
||||||
const videoFieldsSave = videoInstance.toJSON()
|
const videoFieldsSave = videoInstance.toJSON()
|
||||||
const videoInfosToUpdate = req.body
|
const videoInfosToUpdate = req.body
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
|
let tagsPromise: Promise<TagInstance[]>
|
||||||
startSerializableTransaction,
|
if (!videoInfosToUpdate.tags) {
|
||||||
|
tagsPromise = Promise.resolve(null)
|
||||||
function findOrCreateTags (t, callback) {
|
|
||||||
if (videoInfosToUpdate.tags) {
|
|
||||||
db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
|
|
||||||
return callback(err, t, tagInstances)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
return callback(null, t, null)
|
tagsPromise = db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
function updateVideoIntoDB (t, tagInstances, callback) {
|
return tagsPromise
|
||||||
|
.then(tagInstances => {
|
||||||
const options = {
|
const options = {
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
@ -311,38 +280,30 @@ function updateVideo (req: express.Request, res: express.Response, finalCallback
|
||||||
if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
|
if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
|
||||||
if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
|
if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
|
||||||
|
|
||||||
videoInstance.save(options).asCallback(function (err) {
|
return videoInstance.save(options).then(() => tagInstances)
|
||||||
return callback(err, t, tagInstances)
|
|
||||||
})
|
})
|
||||||
},
|
.then(tagInstances => {
|
||||||
|
if (!tagInstances) return
|
||||||
|
|
||||||
function associateTagsToVideo (t, tagInstances, callback) {
|
|
||||||
if (tagInstances) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
return videoInstance.setTags(tagInstances, options)
|
||||||
videoInstance.setTags(tagInstances, options).asCallback(function (err) {
|
.then(() => {
|
||||||
videoInstance.Tags = tagInstances
|
videoInstance.Tags = tagInstances
|
||||||
|
|
||||||
return callback(err, t)
|
return
|
||||||
})
|
})
|
||||||
} else {
|
})
|
||||||
return callback(null, t)
|
.then(() => {
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
function sendToFriends (t, callback) {
|
|
||||||
const json = videoInstance.toUpdateRemoteJSON()
|
const json = videoInstance.toUpdateRemoteJSON()
|
||||||
|
|
||||||
// Now we'll update the video's meta data to our friends
|
// Now we'll update the video's meta data to our friends
|
||||||
updateVideoToFriends(json, t, function (err) {
|
return updateVideoToFriends(json, t)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => {
|
||||||
commitTransaction
|
logger.info('Video with name %s updated.', videoInstance.name)
|
||||||
|
})
|
||||||
], function andFinally (err: Error, t: Sequelize.Transaction) {
|
.catch(err => {
|
||||||
if (err) {
|
|
||||||
logger.debug('Cannot update the video.', { error: err })
|
logger.debug('Cannot update the video.', { error: err })
|
||||||
|
|
||||||
// Force fields we want to update
|
// Force fields we want to update
|
||||||
|
@ -353,11 +314,7 @@ function updateVideo (req: express.Request, res: express.Response, finalCallback
|
||||||
videoInstance.set(key, value)
|
videoInstance.set(key, value)
|
||||||
})
|
})
|
||||||
|
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Video with name %s updated.', videoInstance.name)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,20 +323,17 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
|
||||||
|
|
||||||
if (videoInstance.isOwned()) {
|
if (videoInstance.isOwned()) {
|
||||||
// The increment is done directly in the database, not using the instance value
|
// The increment is done directly in the database, not using the instance value
|
||||||
videoInstance.increment('views').asCallback(function (err) {
|
videoInstance.increment('views')
|
||||||
if (err) {
|
.then(() => {
|
||||||
logger.error('Cannot add view to video %d.', videoInstance.id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: make a real view system
|
// FIXME: make a real view system
|
||||||
// For example, only add a view when a user watch a video during 30s etc
|
// For example, only add a view when a user watch a video during 30s etc
|
||||||
const qaduParams = {
|
const qaduParams = {
|
||||||
videoId: videoInstance.id,
|
videoId: videoInstance.id,
|
||||||
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
|
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
|
||||||
}
|
}
|
||||||
quickAndDirtyUpdateVideoToFriends(qaduParams)
|
return quickAndDirtyUpdateVideoToFriends(qaduParams)
|
||||||
})
|
})
|
||||||
|
.catch(err => logger.error('Cannot add view to video %d.', videoInstance.id, { error: err }))
|
||||||
} else {
|
} else {
|
||||||
// Just send the event to our friends
|
// Just send the event to our friends
|
||||||
const eventParams = {
|
const eventParams = {
|
||||||
|
@ -394,33 +348,24 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
|
||||||
}
|
}
|
||||||
|
|
||||||
function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
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) {
|
db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
|
||||||
if (err) return next(err)
|
.then(result => res.json(getFormatedObjects(result.data, result.total)))
|
||||||
|
.catch(err => next(err))
|
||||||
res.json(getFormatedObjects(videosList, videosTotal))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function removeVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.video
|
||||||
|
|
||||||
videoInstance.destroy().asCallback(function (err) {
|
videoInstance.destroy()
|
||||||
if (err) {
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => {
|
||||||
logger.error('Errors when removed the video.', { error: err })
|
logger.error('Errors when removed the video.', { error: err })
|
||||||
return next(err)
|
return next(err)
|
||||||
}
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
db.Video.searchAndPopulateAuthorAndPodAndTags(
|
db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
|
||||||
req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
|
.then(result => res.json(getFormatedObjects(result.data, result.total)))
|
||||||
function (err, videosList, videosTotal) {
|
.catch(err => next(err))
|
||||||
if (err) return next(err)
|
|
||||||
|
|
||||||
res.json(getFormatedObjects(videosList, videosTotal))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
import { waterfall } from 'async'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import {
|
import {
|
||||||
logger,
|
logger,
|
||||||
retryTransactionWrapper,
|
retryTransactionWrapper
|
||||||
startSerializableTransaction,
|
|
||||||
commitTransaction,
|
|
||||||
rollbackTransaction
|
|
||||||
} from '../../../helpers'
|
} from '../../../helpers'
|
||||||
import {
|
import {
|
||||||
VIDEO_RATE_TYPES,
|
VIDEO_RATE_TYPES,
|
||||||
|
@ -46,28 +41,19 @@ function rateVideoRetryWrapper (req: express.Request, res: express.Response, nex
|
||||||
errorMessage: 'Cannot update the user video rate.'
|
errorMessage: 'Cannot update the user video rate.'
|
||||||
}
|
}
|
||||||
|
|
||||||
retryTransactionWrapper(rateVideo, options, function (err) {
|
retryTransactionWrapper(rateVideo, options)
|
||||||
if (err) return next(err)
|
.then(() => res.type('json').status(204).end())
|
||||||
|
.catch(err => next(err))
|
||||||
return res.type('json').status(204).end()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 rateType = req.body.rating
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.video
|
||||||
const userInstance = res.locals.oauth.token.User
|
const userInstance = res.locals.oauth.token.User
|
||||||
|
|
||||||
waterfall([
|
return db.sequelize.transaction(t => {
|
||||||
startSerializableTransaction,
|
return db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
|
||||||
|
.then(previousRate => {
|
||||||
function findPreviousRate (t, callback) {
|
|
||||||
db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) {
|
|
||||||
return callback(err, t, previousRate)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function insertUserRateIntoDB (t, previousRate, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
let likesToIncrement = 0
|
let likesToIncrement = 0
|
||||||
|
@ -84,9 +70,7 @@ function rateVideo (req: express.Request, res: express.Response, finalCallback:
|
||||||
|
|
||||||
previousRate.type = rateType
|
previousRate.type = rateType
|
||||||
|
|
||||||
previousRate.save(options).asCallback(function (err) {
|
return previousRate.save(options).then(() => ({ t, likesToIncrement, dislikesToIncrement }))
|
||||||
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
|
||||||
})
|
|
||||||
} else { // There was not a previous rate, insert a new one
|
} else { // There was not a previous rate, insert a new one
|
||||||
const query = {
|
const query = {
|
||||||
userId: userInstance.id,
|
userId: userInstance.id,
|
||||||
|
@ -94,13 +78,10 @@ function rateVideo (req: express.Request, res: express.Response, finalCallback:
|
||||||
type: rateType
|
type: rateType
|
||||||
}
|
}
|
||||||
|
|
||||||
db.UserVideoRate.create(query, options).asCallback(function (err) {
|
return db.UserVideoRate.create(query, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
|
||||||
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
.then(({ likesToIncrement, dislikesToIncrement }) => {
|
||||||
function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) {
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
const incrementQuery = {
|
const incrementQuery = {
|
||||||
likes: likesToIncrement,
|
likes: likesToIncrement,
|
||||||
|
@ -109,14 +90,11 @@ function rateVideo (req: express.Request, res: express.Response, finalCallback:
|
||||||
|
|
||||||
// Even if we do not own the video we increment the attributes
|
// Even if we do not own the video we increment the attributes
|
||||||
// It is usefull for the user to have a feedback
|
// It is usefull for the user to have a feedback
|
||||||
videoInstance.increment(incrementQuery, options).asCallback(function (err) {
|
return videoInstance.increment(incrementQuery, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
|
||||||
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ likesToIncrement, dislikesToIncrement }) => {
|
||||||
|
|
||||||
function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
|
|
||||||
// No need for an event type, we own the video
|
// No need for an event type, we own the video
|
||||||
if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement)
|
if (videoInstance.isOwned()) return { likesToIncrement, dislikesToIncrement }
|
||||||
|
|
||||||
const eventsParams = []
|
const eventsParams = []
|
||||||
|
|
||||||
|
@ -136,15 +114,12 @@ function rateVideo (req: express.Request, res: express.Response, finalCallback:
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventsToRemoteVideo(eventsParams, t, function (err) {
|
return addEventsToRemoteVideo(eventsParams, t).then(() => ({ likesToIncrement, dislikesToIncrement }))
|
||||||
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ 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
|
// 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
|
// Our rate was already sent by the addEvent function
|
||||||
if (videoInstance.isOwned() === false) return callback(null, t)
|
if (videoInstance.isOwned() === false) return undefined
|
||||||
|
|
||||||
const qadusParams = []
|
const qadusParams = []
|
||||||
|
|
||||||
|
@ -162,21 +137,13 @@ function rateVideo (req: express.Request, res: express.Response, finalCallback:
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
|
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
|
||||||
return callback(err, t)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(() => logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username))
|
||||||
commitTransaction
|
.catch(err => {
|
||||||
|
|
||||||
], function (err: Error, t: Sequelize.Transaction) {
|
|
||||||
if (err) {
|
|
||||||
// This is just a debug because we will retry the insert
|
// This is just a debug because we will retry the insert
|
||||||
logger.debug('Cannot add the user video rate.', { error: err })
|
logger.debug('Cannot add the user video rate.', { error: err })
|
||||||
return rollbackTransaction(err, t, finalCallback)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { parallel } from 'async'
|
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as fs from 'fs'
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from '../initializers/database'
|
import { database as db } from '../initializers/database'
|
||||||
import {
|
import {
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
STATIC_MAX_AGE
|
STATIC_MAX_AGE
|
||||||
} from '../initializers'
|
} from '../initializers'
|
||||||
import { root } from '../helpers'
|
import { root, readFileBufferPromise } from '../helpers'
|
||||||
import { VideoInstance } from '../models'
|
import { VideoInstance } from '../models'
|
||||||
|
|
||||||
const clientsRouter = express.Router()
|
const clientsRouter = express.Router()
|
||||||
|
@ -95,19 +94,15 @@ function generateWatchHtmlPage (req: express.Request, res: express.Response, nex
|
||||||
// Let Angular application handle errors
|
// Let Angular application handle errors
|
||||||
if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
|
if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
|
||||||
|
|
||||||
parallel({
|
Promise.all([
|
||||||
file: function (callback) {
|
readFileBufferPromise(indexPath),
|
||||||
fs.readFile(indexPath, callback)
|
db.Video.loadAndPopulateAuthorAndPodAndTags(videoId)
|
||||||
},
|
])
|
||||||
|
.then(([ file, video ]) => {
|
||||||
|
file = file as Buffer
|
||||||
|
video = video as VideoInstance
|
||||||
|
|
||||||
video: function (callback) {
|
const html = file.toString()
|
||||||
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
|
|
||||||
|
|
||||||
// Let Angular application handle errors
|
// Let Angular application handle errors
|
||||||
if (!video) return res.sendFile(indexPath)
|
if (!video) return res.sendFile(indexPath)
|
||||||
|
@ -115,4 +110,5 @@ function generateWatchHtmlPage (req: express.Request, res: express.Response, nex
|
||||||
const htmlStringPageWithTags = addOpenGraphTags(html, video)
|
const htmlStringPageWithTags = addOpenGraphTags(html, video)
|
||||||
res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
|
res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
|
||||||
})
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { join } from 'path'
|
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 () {
|
function isTestInstance () {
|
||||||
return process.env.NODE_ENV === 'test'
|
return process.env.NODE_ENV === 'test'
|
||||||
|
@ -14,9 +28,82 @@ function root () {
|
||||||
return join(__dirname, '..', '..', '..')
|
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 {
|
export {
|
||||||
isTestInstance,
|
isTestInstance,
|
||||||
root
|
root,
|
||||||
|
|
||||||
|
promisify0,
|
||||||
|
promisify1,
|
||||||
|
readdirPromise,
|
||||||
|
readFilePromise,
|
||||||
|
readFileBufferPromise,
|
||||||
|
unlinkPromise,
|
||||||
|
renamePromise,
|
||||||
|
writeFilePromise,
|
||||||
|
mkdirpPromise,
|
||||||
|
pseudoRandomBytesPromise,
|
||||||
|
accessPromise,
|
||||||
|
opensslExecPromise,
|
||||||
|
bcryptComparePromise,
|
||||||
|
bcryptGenSaltPromise,
|
||||||
|
bcryptHashPromise,
|
||||||
|
createTorrentPromise
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,28 @@
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
// TODO: import from ES6 when retry typing file will include errorFilter function
|
// TODO: import from ES6 when retry typing file will include errorFilter function
|
||||||
import * as retry from 'async/retry'
|
import * as retry from 'async/retry'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from '../initializers/database'
|
|
||||||
import { logger } from './logger'
|
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[] }
|
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 : []
|
const args = options.arguments ? options.arguments : []
|
||||||
|
|
||||||
transactionRetryer(
|
return transactionRetryer(
|
||||||
function (callback) {
|
function (callback) {
|
||||||
return functionToRetry.apply(this, args.concat([ callback ]))
|
functionToRetry.apply(this, args)
|
||||||
},
|
.then(result => callback(null, result))
|
||||||
function (err) {
|
.catch(err => callback(err))
|
||||||
if (err) {
|
|
||||||
logger.error(options.errorMessage, { error: err })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not return the error, continue the process
|
|
||||||
return finalCallback(null)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.catch(err => {
|
||||||
|
// Do not throw the error, continue the process
|
||||||
|
logger.error(options.errorMessage, { error: err })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function transactionRetryer (func: Function, callback: (err: Error) => void) {
|
function transactionRetryer (func: Function) {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
retry({
|
retry({
|
||||||
times: 5,
|
times: 5,
|
||||||
|
|
||||||
|
@ -49,22 +31,15 @@ function transactionRetryer (func: Function, callback: (err: Error) => void) {
|
||||||
logger.debug('Maybe retrying the transaction function.', { willRetry })
|
logger.debug('Maybe retrying the transaction function.', { willRetry })
|
||||||
return willRetry
|
return willRetry
|
||||||
}
|
}
|
||||||
}, func, callback)
|
}, func, function (err) {
|
||||||
}
|
err ? rej(err) : res()
|
||||||
|
})
|
||||||
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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
commitTransaction,
|
|
||||||
retryTransactionWrapper,
|
retryTransactionWrapper,
|
||||||
rollbackTransaction,
|
|
||||||
startSerializableTransaction,
|
|
||||||
transactionRetryer
|
transactionRetryer
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import * as bcrypt from 'bcrypt'
|
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as openssl from 'openssl-wrapper'
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +10,14 @@ import {
|
||||||
BCRYPT_SALT_SIZE,
|
BCRYPT_SALT_SIZE,
|
||||||
PUBLIC_CERT_NAME
|
PUBLIC_CERT_NAME
|
||||||
} from '../initializers'
|
} from '../initializers'
|
||||||
|
import {
|
||||||
|
readFilePromise,
|
||||||
|
bcryptComparePromise,
|
||||||
|
bcryptGenSaltPromise,
|
||||||
|
bcryptHashPromise,
|
||||||
|
accessPromise,
|
||||||
|
opensslExecPromise
|
||||||
|
} from './core-utils'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
|
|
||||||
function checkSignature (publicKey: string, data: string, hexSignature: string) {
|
function checkSignature (publicKey: string, data: string, hexSignature: string) {
|
||||||
|
@ -60,46 +66,32 @@ function sign (data: string|Object) {
|
||||||
return signature
|
return signature
|
||||||
}
|
}
|
||||||
|
|
||||||
function comparePassword (plainPassword: string, hashPassword: string, callback: (err: Error, match?: boolean) => void) {
|
function comparePassword (plainPassword: string, hashPassword: string) {
|
||||||
bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
|
return bcryptComparePromise(plainPassword, hashPassword)
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
return callback(null, isPasswordMatch)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCertsIfNotExist (callback: (err: Error) => void) {
|
function createCertsIfNotExist () {
|
||||||
certsExist(function (err, exist) {
|
return certsExist().then(exist => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
if (exist === true) {
|
if (exist === true) {
|
||||||
return callback(null)
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
createCerts(function (err) {
|
return createCerts()
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function cryptPassword (password: string, callback: (err: Error, hash?: string) => void) {
|
function cryptPassword (password: string) {
|
||||||
bcrypt.genSalt(BCRYPT_SALT_SIZE, function (err, salt) {
|
return bcryptGenSaltPromise(BCRYPT_SALT_SIZE).then(salt => bcryptHashPromise(password, salt))
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
bcrypt.hash(password, salt, function (err, hash) {
|
|
||||||
return callback(err, hash)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMyPrivateCert (callback: (err: Error, privateCert: string) => void) {
|
function getMyPrivateCert () {
|
||||||
const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
|
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)
|
const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
|
||||||
fs.readFile(certPath, 'utf8', callback)
|
return readFilePromise(certPath, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -116,23 +108,21 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function certsExist (callback: (err: Error, certsExist: boolean) => void) {
|
function certsExist () {
|
||||||
const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
|
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
|
// If there is an error the certificates do not exist
|
||||||
const exists = !err
|
return accessPromise(certPath)
|
||||||
return callback(null, exists)
|
.then(() => true)
|
||||||
})
|
.catch(() => false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCerts (callback: (err: Error) => void) {
|
function createCerts () {
|
||||||
certsExist(function (err, exist) {
|
return certsExist().then(exist => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
if (exist === true) {
|
if (exist === true) {
|
||||||
const errorMessage = 'Certs already exist.'
|
const errorMessage = 'Certs already exist.'
|
||||||
logger.warning(errorMessage)
|
logger.warning(errorMessage)
|
||||||
return callback(new Error(errorMessage))
|
throw new Error(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Generating a RSA key...')
|
logger.info('Generating a RSA key...')
|
||||||
|
@ -142,12 +132,8 @@ function createCerts (callback: (err: Error) => void) {
|
||||||
'out': privateCertPath,
|
'out': privateCertPath,
|
||||||
'2048': false
|
'2048': false
|
||||||
}
|
}
|
||||||
openssl.exec('genrsa', genRsaOptions, function (err) {
|
return opensslExecPromise('genrsa', genRsaOptions)
|
||||||
if (err) {
|
.then(() => {
|
||||||
logger.error('Cannot create private key on this pod.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('RSA key generated.')
|
logger.info('RSA key generated.')
|
||||||
logger.info('Managing public key...')
|
logger.info('Managing public key...')
|
||||||
|
|
||||||
|
@ -157,15 +143,16 @@ function createCerts (callback: (err: Error) => void) {
|
||||||
'pubout': true,
|
'pubout': true,
|
||||||
'out': publicCertPath
|
'out': publicCertPath
|
||||||
}
|
}
|
||||||
openssl.exec('rsa', rsaOptions, function (err) {
|
return opensslExecPromise('rsa', rsaOptions)
|
||||||
if (err) {
|
.then(() => logger.info('Public key managed.'))
|
||||||
|
.catch(err => {
|
||||||
logger.error('Cannot create public key on this pod.')
|
logger.error('Cannot create public key on this pod.')
|
||||||
return callback(err)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Public key managed.')
|
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot create private key on this pod.')
|
||||||
|
throw err
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as replay from 'request-replay'
|
import * as replay from 'request-replay'
|
||||||
import * as request from 'request'
|
import * as request from 'request'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RETRY_REQUESTS,
|
RETRY_REQUESTS,
|
||||||
|
@ -14,9 +15,10 @@ type MakeRetryRequestParams = {
|
||||||
method: 'GET'|'POST',
|
method: 'GET'|'POST',
|
||||||
json: Object
|
json: Object
|
||||||
}
|
}
|
||||||
function makeRetryRequest (params: MakeRetryRequestParams, callback: request.RequestCallback) {
|
function makeRetryRequest (params: MakeRetryRequestParams) {
|
||||||
|
return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
|
||||||
replay(
|
replay(
|
||||||
request(params, callback),
|
request(params, (err, response, body) => err ? rej(err) : res({ response, body })),
|
||||||
{
|
{
|
||||||
retries: RETRY_REQUESTS,
|
retries: RETRY_REQUESTS,
|
||||||
factor: 3,
|
factor: 3,
|
||||||
|
@ -24,6 +26,7 @@ function makeRetryRequest (params: MakeRetryRequestParams, callback: request.Req
|
||||||
errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
|
errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type MakeSecureRequestParams = {
|
type MakeSecureRequestParams = {
|
||||||
|
@ -33,14 +36,15 @@ type MakeSecureRequestParams = {
|
||||||
sign: boolean
|
sign: boolean
|
||||||
data?: Object
|
data?: Object
|
||||||
}
|
}
|
||||||
function makeSecureRequest (params: MakeSecureRequestParams, callback: request.RequestCallback) {
|
function makeSecureRequest (params: MakeSecureRequestParams) {
|
||||||
|
return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
|
||||||
const requestParams = {
|
const requestParams = {
|
||||||
url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
|
url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
|
||||||
json: {}
|
json: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.method !== 'POST') {
|
if (params.method !== 'POST') {
|
||||||
return callback(new Error('Cannot make a secure request with a non POST method.'), null, null)
|
return rej(new Error('Cannot make a secure request with a non POST method.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signature if it is specified in the params
|
// Add signature if it is specified in the params
|
||||||
|
@ -67,7 +71,8 @@ function makeSecureRequest (params: MakeSecureRequestParams, callback: request.R
|
||||||
requestParams.json['data'] = params.data
|
requestParams.json['data'] = params.data
|
||||||
}
|
}
|
||||||
|
|
||||||
request.post(requestParams, callback)
|
request.post(requestParams, (err, response, body) => err ? rej(err) : res({ response, body }))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { pseudoRandomBytes } from 'crypto'
|
import { pseudoRandomBytesPromise } from './core-utils'
|
||||||
|
import { ResultList } from '../../shared'
|
||||||
import { logger } from './logger'
|
|
||||||
|
|
||||||
function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
res.type('json').status(400).end()
|
res.type('json').status(400).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRandomString (size: number, callback: (err: Error, randomString?: string) => void) {
|
function generateRandomString (size: number) {
|
||||||
pseudoRandomBytes(size, function (err, raw) {
|
return pseudoRandomBytesPromise(size).then(raw => raw.toString('hex'))
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormatableToJSON {
|
interface FormatableToJSON {
|
||||||
|
@ -33,17 +22,18 @@ function getFormatedObjects<U, T extends FormatableToJSON> (objects: T[], object
|
||||||
formatedObjects.push(object.toFormatedJSON())
|
formatedObjects.push(object.toFormatedJSON())
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
const res: ResultList<U> = {
|
||||||
total: objectsTotal,
|
total: objectsTotal,
|
||||||
data: formatedObjects
|
data: formatedObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
badRequest,
|
badRequest,
|
||||||
createEmptyCallback,
|
|
||||||
generateRandomString,
|
generateRandomString,
|
||||||
getFormatedObjects
|
getFormatedObjects
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as config from 'config'
|
||||||
|
|
||||||
import { database as db } from './database'
|
import { database as db } from './database'
|
||||||
import { CONFIG } from './constants'
|
import { CONFIG } from './constants'
|
||||||
|
import { promisify0 } from '../helpers/core-utils'
|
||||||
|
|
||||||
// Some checks on configuration files
|
// Some checks on configuration files
|
||||||
function checkConfig () {
|
function checkConfig () {
|
||||||
|
@ -35,41 +36,36 @@ function checkMissedConfig () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the available codecs
|
// Check the available codecs
|
||||||
function checkFFmpeg (callback: (err: Error) => void) {
|
function checkFFmpeg () {
|
||||||
const Ffmpeg = require('fluent-ffmpeg')
|
const Ffmpeg = require('fluent-ffmpeg')
|
||||||
|
const getAvailableCodecsPromise = promisify0(Ffmpeg.getAvailableCodecs)
|
||||||
|
|
||||||
Ffmpeg.getAvailableCodecs(function (err, codecs) {
|
getAvailableCodecsPromise()
|
||||||
if (err) return callback(err)
|
.then(codecs => {
|
||||||
if (CONFIG.TRANSCODING.ENABLED === false) return callback(null)
|
if (CONFIG.TRANSCODING.ENABLED === false) return undefined
|
||||||
|
|
||||||
const canEncode = [ 'libx264' ]
|
const canEncode = [ 'libx264' ]
|
||||||
canEncode.forEach(function (codec) {
|
canEncode.forEach(function (codec) {
|
||||||
if (codecs[codec] === undefined) {
|
if (codecs[codec] === undefined) {
|
||||||
return callback(new Error('Unknown codec ' + codec + ' in FFmpeg.'))
|
throw new Error('Unknown codec ' + codec + ' in FFmpeg.')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codecs[codec].canEncode !== true) {
|
if (codecs[codec].canEncode !== true) {
|
||||||
return callback(new Error('Unavailable encode codec ' + codec + ' in FFmpeg'))
|
throw new Error('Unavailable encode codec ' + codec + ' in FFmpeg')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function clientsExist (callback: (err: Error, clientsExist?: boolean) => void) {
|
function clientsExist () {
|
||||||
db.OAuthClient.countTotal(function (err, totalClients) {
|
return db.OAuthClient.countTotal().then(totalClients => {
|
||||||
if (err) return callback(err)
|
return totalClients !== 0
|
||||||
|
|
||||||
return callback(null, totalClients !== 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function usersExist (callback: (err: Error, usersExist?: boolean) => void) {
|
function usersExist () {
|
||||||
db.User.countTotal(function (err, totalUsers) {
|
return db.User.countTotal().then(totalUsers => {
|
||||||
if (err) return callback(err)
|
return totalUsers !== 0
|
||||||
|
|
||||||
return callback(null, totalUsers !== 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as fs from 'fs'
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
import { flattenDepth } from 'lodash'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { each } from 'async'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { CONFIG } from './constants'
|
import { CONFIG } from './constants'
|
||||||
// Do not use barrel, we need to load database first
|
// Do not use barrel, we need to load database first
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
import { isTestInstance } from '../helpers/core-utils'
|
import { isTestInstance, readdirPromise } from '../helpers/core-utils'
|
||||||
import {
|
import {
|
||||||
ApplicationModel,
|
ApplicationModel,
|
||||||
AuthorModel,
|
AuthorModel,
|
||||||
|
@ -33,7 +33,7 @@ const password = CONFIG.DATABASE.PASSWORD
|
||||||
|
|
||||||
const database: {
|
const database: {
|
||||||
sequelize?: Sequelize.Sequelize,
|
sequelize?: Sequelize.Sequelize,
|
||||||
init?: (silent: any, callback: any) => void,
|
init?: (silent: boolean) => Promise<void>,
|
||||||
|
|
||||||
Application?: ApplicationModel,
|
Application?: ApplicationModel,
|
||||||
Author?: AuthorModel,
|
Author?: AuthorModel,
|
||||||
|
@ -72,19 +72,17 @@ const sequelize = new Sequelize(dbname, username, password, {
|
||||||
|
|
||||||
database.sequelize = sequelize
|
database.sequelize = sequelize
|
||||||
|
|
||||||
database.init = function (silent: boolean, callback: (err: Error) => void) {
|
database.init = function (silent: boolean) {
|
||||||
const modelDirectory = join(__dirname, '..', 'models')
|
const modelDirectory = join(__dirname, '..', 'models')
|
||||||
|
|
||||||
getModelFiles(modelDirectory, function (err, filePaths) {
|
return getModelFiles(modelDirectory).then(filePaths => {
|
||||||
if (err) throw err
|
filePaths.forEach(filePath => {
|
||||||
|
|
||||||
filePaths.forEach(function (filePath) {
|
|
||||||
const model = sequelize.import(filePath)
|
const model = sequelize.import(filePath)
|
||||||
|
|
||||||
database[model['name']] = model
|
database[model['name']] = model
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.keys(database).forEach(function (modelName) {
|
Object.keys(database).forEach(modelName => {
|
||||||
if ('associate' in database[modelName]) {
|
if ('associate' in database[modelName]) {
|
||||||
database[modelName].associate(database)
|
database[modelName].associate(database)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +90,7 @@ database.init = function (silent: boolean, callback: (err: Error) => void) {
|
||||||
|
|
||||||
if (!silent) logger.info('Database %s is ready.', dbname)
|
if (!silent) logger.info('Database %s is ready.', dbname)
|
||||||
|
|
||||||
return callback(null)
|
return undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,11 +102,10 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function getModelFiles (modelDirectory: string, callback: (err: Error, filePaths: string[]) => void) {
|
function getModelFiles (modelDirectory: string) {
|
||||||
fs.readdir(modelDirectory, function (err, files) {
|
return readdirPromise(modelDirectory)
|
||||||
if (err) throw err
|
.then(files => {
|
||||||
|
const directories: string[] = files.filter(function (directory) {
|
||||||
const directories = files.filter(function (directory) {
|
|
||||||
// Find directories
|
// Find directories
|
||||||
if (
|
if (
|
||||||
directory.endsWith('.js.map') ||
|
directory.endsWith('.js.map') ||
|
||||||
|
@ -119,15 +116,16 @@ function getModelFiles (modelDirectory: string, callback: (err: Error, filePaths
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
let modelFilePaths: string[] = []
|
return directories
|
||||||
|
})
|
||||||
|
.then(directories => {
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
// For each directory we read it and append model in the modelFilePaths array
|
// For each directory we read it and append model in the modelFilePaths array
|
||||||
each(directories, function (directory: string, eachCallback: ErrorCallback<Error>) {
|
directories.forEach(directory => {
|
||||||
const modelDirectoryPath = join(modelDirectory, directory)
|
const modelDirectoryPath = join(modelDirectory, directory)
|
||||||
|
|
||||||
fs.readdir(modelDirectoryPath, function (err, files) {
|
const promise = readdirPromise(modelDirectoryPath).then(files => {
|
||||||
if (err) return eachCallback(err)
|
|
||||||
|
|
||||||
const filteredFiles = files.filter(file => {
|
const filteredFiles = files.filter(file => {
|
||||||
if (
|
if (
|
||||||
file === 'index.js' || file === 'index.ts' ||
|
file === 'index.js' || file === 'index.ts' ||
|
||||||
|
@ -137,16 +135,17 @@ function getModelFiles (modelDirectory: string, callback: (err: Error, filePaths
|
||||||
) return false
|
) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}).map(file => {
|
}).map(file => join(modelDirectoryPath, file))
|
||||||
return join(modelDirectoryPath, file)
|
|
||||||
|
return filteredFiles
|
||||||
})
|
})
|
||||||
|
|
||||||
modelFilePaths = modelFilePaths.concat(filteredFiles)
|
tasks.push(promise)
|
||||||
|
})
|
||||||
|
|
||||||
return eachCallback(null)
|
return Promise.all(tasks)
|
||||||
})
|
|
||||||
}, function (err: Error) {
|
|
||||||
return callback(err, modelFilePaths)
|
|
||||||
})
|
})
|
||||||
|
.then((filteredFiles: string[][]) => {
|
||||||
|
return flattenDepth<string>(filteredFiles, 1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as config from 'config'
|
import * as config from 'config'
|
||||||
import { each, series } from 'async'
|
|
||||||
import * as mkdirp from 'mkdirp'
|
|
||||||
import * as passwordGenerator from 'password-generator'
|
import * as passwordGenerator from 'password-generator'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from './database'
|
import { database as db } from './database'
|
||||||
import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION } from './constants'
|
import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION } from './constants'
|
||||||
import { clientsExist, usersExist } from './checker'
|
import { clientsExist, usersExist } from './checker'
|
||||||
import { logger, createCertsIfNotExist, root } from '../helpers'
|
import { logger, createCertsIfNotExist, root, mkdirpPromise } from '../helpers'
|
||||||
|
|
||||||
function installApplication (callback: (err: Error) => void) {
|
function installApplication () {
|
||||||
series([
|
return db.sequelize.sync()
|
||||||
function createDatabase (callbackAsync) {
|
.then(() => createDirectoriesIfNotExist())
|
||||||
db.sequelize.sync().asCallback(callbackAsync)
|
.then(() => createCertsIfNotExist())
|
||||||
// db.sequelize.sync({ force: true }).asCallback(callbackAsync)
|
.then(() => createOAuthClientIfNotExist())
|
||||||
},
|
.then(() => createOAuthAdminIfNotExist())
|
||||||
|
|
||||||
function createDirectories (callbackAsync) {
|
|
||||||
createDirectoriesIfNotExist(callbackAsync)
|
|
||||||
},
|
|
||||||
|
|
||||||
function createCertificates (callbackAsync) {
|
|
||||||
createCertsIfNotExist(callbackAsync)
|
|
||||||
},
|
|
||||||
|
|
||||||
function createOAuthClient (callbackAsync) {
|
|
||||||
createOAuthClientIfNotExist(callbackAsync)
|
|
||||||
},
|
|
||||||
|
|
||||||
function createOAuthUser (callbackAsync) {
|
|
||||||
createOAuthAdminIfNotExist(callbackAsync)
|
|
||||||
}
|
|
||||||
], callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -42,21 +24,22 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function createDirectoriesIfNotExist (callback: (err: Error) => void) {
|
function createDirectoriesIfNotExist () {
|
||||||
const storages = config.get('storage')
|
const storages = config.get('storage')
|
||||||
|
|
||||||
each(Object.keys(storages), function (key, callbackEach) {
|
const tasks = []
|
||||||
|
Object.keys(storages).forEach(key => {
|
||||||
const dir = storages[key]
|
const dir = storages[key]
|
||||||
mkdirp(join(root(), dir), callbackEach)
|
tasks.push(mkdirpPromise(join(root(), dir)))
|
||||||
}, callback)
|
})
|
||||||
|
|
||||||
|
return Promise.all(tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOAuthClientIfNotExist (callback: (err: Error) => void) {
|
function createOAuthClientIfNotExist () {
|
||||||
clientsExist(function (err, exist) {
|
return clientsExist().then(exist => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// Nothing to do, clients already 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.')
|
logger.info('Creating a default OAuth Client.')
|
||||||
|
|
||||||
|
@ -69,23 +52,19 @@ function createOAuthClientIfNotExist (callback: (err: Error) => void) {
|
||||||
redirectUris: null
|
redirectUris: null
|
||||||
})
|
})
|
||||||
|
|
||||||
client.save().asCallback(function (err, createdClient) {
|
return client.save().then(createdClient => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
logger.info('Client id: ' + createdClient.clientId)
|
logger.info('Client id: ' + createdClient.clientId)
|
||||||
logger.info('Client secret: ' + createdClient.clientSecret)
|
logger.info('Client secret: ' + createdClient.clientSecret)
|
||||||
|
|
||||||
return callback(null)
|
return undefined
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOAuthAdminIfNotExist (callback: (err: Error) => void) {
|
function createOAuthAdminIfNotExist () {
|
||||||
usersExist(function (err, exist) {
|
return usersExist().then(exist => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// Nothing to do, users already exist
|
// Nothing to do, users already exist
|
||||||
if (exist === true) return callback(null)
|
if (exist === true) return undefined
|
||||||
|
|
||||||
logger.info('Creating the administrator.')
|
logger.info('Creating the administrator.')
|
||||||
|
|
||||||
|
@ -116,14 +95,12 @@ function createOAuthAdminIfNotExist (callback: (err: Error) => void) {
|
||||||
role
|
role
|
||||||
}
|
}
|
||||||
|
|
||||||
db.User.create(userData, createOptions).asCallback(function (err, createdUser) {
|
return db.User.create(userData, createOptions).then(createdUser => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
logger.info('Username: ' + username)
|
logger.info('Username: ' + username)
|
||||||
logger.info('User password: ' + password)
|
logger.info('User password: ' + password)
|
||||||
|
|
||||||
logger.info('Creating Application table.')
|
logger.info('Creating Application table.')
|
||||||
db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION }).asCallback(callback)
|
return db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { waterfall } from 'async'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
function up (utils: {
|
||||||
function up (utils, finalCallback) {
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.STRING(400),
|
type: Sequelize.STRING(400),
|
||||||
|
@ -11,27 +14,16 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: ''
|
defaultValue: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall([
|
return q.addColumn('Pods', 'email', data)
|
||||||
|
.then(() => {
|
||||||
function addEmailColumn (callback) {
|
|
||||||
q.addColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
|
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function updateWithFakeEmails (callback) {
|
|
||||||
const query = 'UPDATE "Pods" SET "email" = \'dummy@example.com\''
|
const query = 'UPDATE "Pods" SET "email" = \'dummy@example.com\''
|
||||||
utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
|
return utils.sequelize.query(query, { transaction: utils.transaction })
|
||||||
return callback(err)
|
|
||||||
})
|
})
|
||||||
},
|
.then(() => {
|
||||||
|
|
||||||
function nullOnDefault (callback) {
|
|
||||||
data.defaultValue = null
|
data.defaultValue = null
|
||||||
|
|
||||||
q.changeColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(callback)
|
return q.changeColumn('Pods', 'email', data)
|
||||||
}
|
})
|
||||||
], finalCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,37 +1,28 @@
|
||||||
import { waterfall } from 'async'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
function up (utils: {
|
||||||
function up (utils, finalCallback) {
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.STRING(400),
|
type: Sequelize.STRING(400),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: ''
|
defaultValue: ''
|
||||||
}
|
}
|
||||||
|
return q.addColumn('Users', 'email', data)
|
||||||
waterfall([
|
.then(() => {
|
||||||
|
|
||||||
function addEmailColumn (callback) {
|
|
||||||
q.addColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
|
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function updateWithFakeEmails (callback) {
|
|
||||||
const query = 'UPDATE "Users" SET "email" = CONCAT("username", \'@example.com\')'
|
const query = 'UPDATE "Users" SET "email" = CONCAT("username", \'@example.com\')'
|
||||||
utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
|
return utils.sequelize.query(query, { transaction: utils.transaction })
|
||||||
return callback(err)
|
|
||||||
})
|
})
|
||||||
},
|
.then(() => {
|
||||||
|
|
||||||
function nullOnDefault (callback) {
|
|
||||||
data.defaultValue = null
|
data.defaultValue = null
|
||||||
|
|
||||||
q.changeColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(callback)
|
return q.changeColumn('Users', 'email', data)
|
||||||
}
|
})
|
||||||
], finalCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
import * as Sequelize from 'sequelize'
|
||||||
function up (utils, finalCallback) {
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -9,7 +14,7 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
q.addColumn('Videos', 'views', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
return q.addColumn('Videos', 'views', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
import * as Sequelize from 'sequelize'
|
||||||
function up (utils, finalCallback) {
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -9,7 +14,7 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
q.addColumn('Videos', 'likes', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
return q.addColumn('Videos', 'likes', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
import * as Sequelize from 'sequelize'
|
||||||
function up (utils, finalCallback) {
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -9,7 +14,7 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
q.addColumn('Videos', 'dislikes', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
return q.addColumn('Videos', 'dislikes', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { waterfall } from 'async'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
function up (utils: {
|
||||||
function up (utils, finalCallback) {
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -11,20 +14,12 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall([
|
return q.addColumn('Videos', 'category', data)
|
||||||
|
.then(() => {
|
||||||
function addCategoryColumn (callback) {
|
|
||||||
q.addColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(function (err) {
|
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function nullOnDefault (callback) {
|
|
||||||
data.defaultValue = null
|
data.defaultValue = null
|
||||||
|
|
||||||
q.changeColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(callback)
|
return q.changeColumn('Videos', 'category', data)
|
||||||
}
|
})
|
||||||
], finalCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { waterfall } from 'async'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
function up (utils: {
|
||||||
function up (utils, finalCallback) {
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -11,20 +14,11 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall([
|
return q.addColumn('Videos', 'licence', data)
|
||||||
|
.then(() => {
|
||||||
function addLicenceColumn (callback) {
|
|
||||||
q.addColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(function (err) {
|
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function nullOnDefault (callback) {
|
|
||||||
data.defaultValue = null
|
data.defaultValue = null
|
||||||
|
return q.changeColumn('Videos', 'licence', data)
|
||||||
q.changeColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(callback)
|
})
|
||||||
}
|
|
||||||
], finalCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { waterfall } from 'async'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
function up (utils: {
|
||||||
function up (utils, finalCallback) {
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.BOOLEAN,
|
type: Sequelize.BOOLEAN,
|
||||||
|
@ -11,20 +14,12 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall([
|
return q.addColumn('Videos', 'nsfw', data)
|
||||||
|
.then(() => {
|
||||||
function addNSFWColumn (callback) {
|
|
||||||
q.addColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(function (err) {
|
|
||||||
return callback(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function nullOnDefault (callback) {
|
|
||||||
data.defaultValue = null
|
data.defaultValue = null
|
||||||
|
|
||||||
q.changeColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(callback)
|
return q.changeColumn('Videos', 'nsfw', data)
|
||||||
}
|
})
|
||||||
], finalCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
import * as Sequelize from 'sequelize'
|
||||||
function up (utils, finalCallback) {
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.BOOLEAN,
|
type: Sequelize.BOOLEAN,
|
||||||
|
@ -9,7 +14,7 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
}
|
}
|
||||||
|
|
||||||
q.addColumn('Users', 'displayNSFW', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
return q.addColumn('Users', 'displayNSFW', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
import * as Sequelize from 'sequelize'
|
||||||
function up (utils, finalCallback) {
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
const q = utils.queryInterface
|
const q = utils.queryInterface
|
||||||
const Sequelize = utils.Sequelize
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
|
@ -9,7 +14,7 @@ function up (utils, finalCallback) {
|
||||||
defaultValue: null
|
defaultValue: null
|
||||||
}
|
}
|
||||||
|
|
||||||
q.addColumn('Videos', 'language', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
return q.addColumn('Videos', 'language', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function down (options, callback) {
|
function down (options, callback) {
|
||||||
|
|
|
@ -1,70 +1,54 @@
|
||||||
import { waterfall, eachSeries } from 'async'
|
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from './database'
|
import { database as db } from './database'
|
||||||
import { LAST_MIGRATION_VERSION } from './constants'
|
import { LAST_MIGRATION_VERSION } from './constants'
|
||||||
import { logger } from '../helpers'
|
import { logger, readdirPromise } 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)
|
|
||||||
|
|
||||||
|
function migrate () {
|
||||||
|
const p = db.sequelize.getQueryInterface().showAllTables()
|
||||||
|
.then(tables => {
|
||||||
// No tables, we don't need to migrate anything
|
// No tables, we don't need to migrate anything
|
||||||
// The installer will do that
|
// The installer will do that
|
||||||
if (tables.length === 0) return finalCallback(null)
|
if (tables.length === 0) throw null
|
||||||
|
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
},
|
.then(() => {
|
||||||
|
return db.Application.loadMigrationVersion()
|
||||||
function loadMigrationVersion (callback) {
|
})
|
||||||
db.Application.loadMigrationVersion(callback)
|
.then(actualVersion => {
|
||||||
},
|
|
||||||
|
|
||||||
function createMigrationRowIfNotExists (actualVersion, callback) {
|
|
||||||
if (actualVersion === null) {
|
if (actualVersion === null) {
|
||||||
db.Application.create({
|
return db.Application.create({ migrationVersion: 0 }).then(() => 0)
|
||||||
migrationVersion: 0
|
|
||||||
}, function (err) {
|
|
||||||
return callback(err, 0)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, actualVersion)
|
return actualVersion
|
||||||
},
|
})
|
||||||
|
.then(actualVersion => {
|
||||||
|
// No need migrations, abort
|
||||||
|
if (actualVersion >= LAST_MIGRATION_VERSION) throw null
|
||||||
|
|
||||||
function abortMigrationIfNotNeeded (actualVersion, callback) {
|
return actualVersion
|
||||||
// No need migrations
|
})
|
||||||
if (actualVersion >= LAST_MIGRATION_VERSION) return finalCallback(null)
|
.then(actualVersion => {
|
||||||
|
|
||||||
return callback(null, actualVersion)
|
|
||||||
},
|
|
||||||
|
|
||||||
function getMigrations (actualVersion, callback) {
|
|
||||||
// If there are a new migration scripts
|
// If there are a new migration scripts
|
||||||
logger.info('Begin migrations.')
|
logger.info('Begin migrations.')
|
||||||
|
|
||||||
getMigrationScripts(function (err, migrationScripts) {
|
return getMigrationScripts().then(migrationScripts => ({ actualVersion, migrationScripts }))
|
||||||
return callback(err, actualVersion, migrationScripts)
|
|
||||||
})
|
})
|
||||||
},
|
.then(({ actualVersion, migrationScripts }) => {
|
||||||
|
return Promise.mapSeries(migrationScripts, entity => {
|
||||||
function doMigrations (actualVersion, migrationScripts, callback) {
|
return executeMigration(actualVersion, entity)
|
||||||
eachSeries(migrationScripts, function (entity: any, callbackEach) {
|
})
|
||||||
executeMigration(actualVersion, entity, callbackEach)
|
})
|
||||||
}, function (err) {
|
.then(() => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
|
logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
}
|
.catch(err => {
|
||||||
], finalCallback)
|
if (err === null) return undefined
|
||||||
|
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -75,12 +59,12 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
type GetMigrationScriptsCallback = (err: Error, filesToMigrate?: { version: string, script: string }[]) => void
|
function getMigrationScripts () {
|
||||||
function getMigrationScripts (callback: GetMigrationScriptsCallback) {
|
return readdirPromise(path.join(__dirname, 'migrations')).then(files => {
|
||||||
fs.readdir(path.join(__dirname, 'migrations'), function (err, files) {
|
const filesToMigrate: {
|
||||||
if (err) return callback(err)
|
version: string,
|
||||||
|
script: string
|
||||||
const filesToMigrate = []
|
}[] = []
|
||||||
|
|
||||||
files.forEach(function (file) {
|
files.forEach(function (file) {
|
||||||
// Filename is something like 'version-blabla.js'
|
// Filename is something like 'version-blabla.js'
|
||||||
|
@ -91,15 +75,15 @@ function getMigrationScripts (callback: GetMigrationScriptsCallback) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
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)
|
const versionScript = parseInt(entity.version, 10)
|
||||||
|
|
||||||
// Do not execute old migration scripts
|
// Do not execute old migration scripts
|
||||||
if (versionScript <= actualVersion) return callback(null)
|
if (versionScript <= actualVersion) return undefined
|
||||||
|
|
||||||
// Load the migration module and run it
|
// Load the migration module and run it
|
||||||
const migrationScriptName = entity.script
|
const migrationScriptName = entity.script
|
||||||
|
@ -107,30 +91,17 @@ function executeMigration (actualVersion: number, entity: { version: string, scr
|
||||||
|
|
||||||
const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
|
const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
|
||||||
|
|
||||||
db.sequelize.transaction().asCallback(function (err, t) {
|
return db.sequelize.transaction(t => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
transaction: t,
|
transaction: t,
|
||||||
queryInterface: db.sequelize.getQueryInterface(),
|
queryInterface: db.sequelize.getQueryInterface(),
|
||||||
sequelize: db.sequelize,
|
sequelize: db.sequelize
|
||||||
Sequelize: Sequelize
|
|
||||||
}
|
|
||||||
migrationScript.up(options, function (err) {
|
|
||||||
if (err) {
|
|
||||||
t.rollback()
|
|
||||||
return callback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
migrationScript.up(options)
|
||||||
|
.then(() => {
|
||||||
// Update the new migration version
|
// Update the new migration version
|
||||||
db.Application.updateMigrationVersion(versionScript, t, function (err) {
|
db.Application.updateMigrationVersion(versionScript, t)
|
||||||
if (err) {
|
|
||||||
t.rollback()
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.commit().asCallback(callback)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { each, eachLimit, eachSeries, series, waterfall } from 'async'
|
|
||||||
import * as request from 'request'
|
import * as request from 'request'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from '../initializers/database'
|
import { database as db } from '../initializers/database'
|
||||||
import {
|
import {
|
||||||
|
@ -15,8 +15,7 @@ import {
|
||||||
logger,
|
logger,
|
||||||
getMyPublicCert,
|
getMyPublicCert,
|
||||||
makeSecureRequest,
|
makeSecureRequest,
|
||||||
makeRetryRequest,
|
makeRetryRequest
|
||||||
createEmptyCallback
|
|
||||||
} from '../helpers'
|
} from '../helpers'
|
||||||
import {
|
import {
|
||||||
RequestScheduler,
|
RequestScheduler,
|
||||||
|
@ -53,24 +52,24 @@ function activateSchedulers () {
|
||||||
requestVideoEventScheduler.activate()
|
requestVideoEventScheduler.activate()
|
||||||
}
|
}
|
||||||
|
|
||||||
function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction, callback: (err: Error) => void) {
|
function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction) {
|
||||||
const options = {
|
const options = {
|
||||||
type: ENDPOINT_ACTIONS.ADD,
|
type: ENDPOINT_ACTIONS.ADD,
|
||||||
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
||||||
data: videoData,
|
data: videoData,
|
||||||
transaction
|
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 = {
|
const options = {
|
||||||
type: ENDPOINT_ACTIONS.UPDATE,
|
type: ENDPOINT_ACTIONS.UPDATE,
|
||||||
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
||||||
data: videoData,
|
data: videoData,
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
createRequest(options, callback)
|
return createRequest(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeVideoToFriends (videoParams: Object) {
|
function removeVideoToFriends (videoParams: Object) {
|
||||||
|
@ -80,121 +79,93 @@ function removeVideoToFriends (videoParams: Object) {
|
||||||
data: videoParams,
|
data: videoParams,
|
||||||
transaction: null
|
transaction: null
|
||||||
}
|
}
|
||||||
createRequest(options)
|
return createRequest(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance) {
|
function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance, transaction: Sequelize.Transaction) {
|
||||||
const options = {
|
const options = {
|
||||||
type: ENDPOINT_ACTIONS.REPORT_ABUSE,
|
type: ENDPOINT_ACTIONS.REPORT_ABUSE,
|
||||||
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
||||||
data: reportData,
|
data: reportData,
|
||||||
toIds: [ video.Author.podId ],
|
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 = {
|
const options = {
|
||||||
videoId: qaduParam.videoId,
|
videoId: qaduParam.videoId,
|
||||||
type: qaduParam.type,
|
type: qaduParam.type,
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
return createVideoQaduRequest(options, callback)
|
return createVideoQaduRequest(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function quickAndDirtyUpdatesVideoToFriends (
|
function quickAndDirtyUpdatesVideoToFriends (qadusParams: QaduParam[], transaction: Sequelize.Transaction) {
|
||||||
qadusParams: QaduParam[],
|
|
||||||
transaction: Sequelize.Transaction,
|
|
||||||
finalCallback: (err: Error) => void
|
|
||||||
) {
|
|
||||||
const tasks = []
|
const tasks = []
|
||||||
|
|
||||||
qadusParams.forEach(function (qaduParams) {
|
qadusParams.forEach(function (qaduParams) {
|
||||||
const fun = function (callback) {
|
tasks.push(quickAndDirtyUpdateVideoToFriends(qaduParams, transaction))
|
||||||
quickAndDirtyUpdateVideoToFriends(qaduParams, transaction, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.push(fun)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
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 = {
|
const options = {
|
||||||
videoId: eventParam.videoId,
|
videoId: eventParam.videoId,
|
||||||
type: eventParam.type,
|
type: eventParam.type,
|
||||||
transaction
|
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 = []
|
const tasks = []
|
||||||
|
|
||||||
eventsParams.forEach(function (eventParams) {
|
eventsParams.forEach(function (eventParams) {
|
||||||
const fun = function (callback) {
|
tasks.push(addEventToRemoteVideo(eventParams, transaction))
|
||||||
addEventToRemoteVideo(eventParams, transaction, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.push(fun)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
series(tasks, finalCallback)
|
return Promise.all(tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasFriends (callback: (err: Error, hasFriends?: boolean) => void) {
|
function hasFriends () {
|
||||||
db.Pod.countAll(function (err, count) {
|
return db.Pod.countAll().then(count => count !== 0)
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const hasFriends = (count !== 0)
|
|
||||||
callback(null, hasFriends)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeFriends (hosts: string[], callback: (err: Error) => void) {
|
function makeFriends (hosts: string[]) {
|
||||||
const podsScore = {}
|
const podsScore = {}
|
||||||
|
|
||||||
logger.info('Make friends!')
|
logger.info('Make friends!')
|
||||||
getMyPublicCert(function (err, cert) {
|
return getMyPublicCert()
|
||||||
if (err) {
|
.then(cert => {
|
||||||
logger.error('Cannot read public cert.')
|
return Promise.mapSeries(hosts, host => {
|
||||||
return callback(err)
|
return computeForeignPodsList(host, podsScore)
|
||||||
}
|
}).then(() => cert)
|
||||||
|
})
|
||||||
eachSeries(hosts, function (host, callbackEach) {
|
.then(cert => {
|
||||||
computeForeignPodsList(host, podsScore, callbackEach)
|
|
||||||
}, function (err: Error) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
logger.debug('Pods scores computed.', { podsScore: podsScore })
|
logger.debug('Pods scores computed.', { podsScore: podsScore })
|
||||||
const podsList = computeWinningPods(hosts, podsScore)
|
const podsList = computeWinningPods(hosts, podsScore)
|
||||||
logger.debug('Pods that we keep.', { podsToKeep: podsList })
|
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
|
// Stop pool requests
|
||||||
requestScheduler.deactivate()
|
requestScheduler.deactivate()
|
||||||
|
|
||||||
waterfall([
|
return requestScheduler.flush()
|
||||||
function flushRequests (callbackAsync) {
|
.then(() => {
|
||||||
requestScheduler.flush(err => callbackAsync(err))
|
return requestVideoQaduScheduler.flush()
|
||||||
},
|
})
|
||||||
|
.then(() => {
|
||||||
function flushVideoQaduRequests (callbackAsync) {
|
return db.Pod.list()
|
||||||
requestVideoQaduScheduler.flush(err => callbackAsync(err))
|
})
|
||||||
},
|
.then(pods => {
|
||||||
|
|
||||||
function getPodsList (callbackAsync) {
|
|
||||||
return db.Pod.list(callbackAsync)
|
|
||||||
},
|
|
||||||
|
|
||||||
function announceIQuitMyFriends (pods, callbackAsync) {
|
|
||||||
const requestParams = {
|
const requestParams = {
|
||||||
method: 'POST' as 'POST',
|
method: 'POST' as 'POST',
|
||||||
path: '/api/' + API_VERSION + '/remote/pods/remove',
|
path: '/api/' + API_VERSION + '/remote/pods/remove',
|
||||||
|
@ -205,50 +176,37 @@ function quitFriends (callback: (err: Error) => void) {
|
||||||
// Announce we quit them
|
// Announce we quit them
|
||||||
// We don't care if the request fails
|
// We don't care if the request fails
|
||||||
// The other pod will exclude us automatically after a while
|
// 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
|
requestParams.toPod = pod
|
||||||
makeSecureRequest(requestParams, callbackEach)
|
return makeSecureRequest(requestParams)
|
||||||
}, function (err) {
|
}, { concurrency: REQUESTS_IN_PARALLEL })
|
||||||
if (err) {
|
.then(() => pods)
|
||||||
|
.catch(err => {
|
||||||
logger.error('Some errors while quitting friends.', { err: err })
|
logger.error('Some errors while quitting friends.', { err: err })
|
||||||
// Don't stop the process
|
// Don't stop the process
|
||||||
}
|
|
||||||
|
|
||||||
return callbackAsync(null, pods)
|
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
.then(pods => {
|
||||||
function removePodsFromDB (pods, callbackAsync) {
|
const tasks = []
|
||||||
each(pods, function (pod: any, callbackEach) {
|
pods.forEach(pod => tasks.push(pod.destroy()))
|
||||||
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)
|
|
||||||
|
|
||||||
|
return Promise.all(pods)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
logger.info('Removed all remote videos.')
|
logger.info('Removed all remote videos.')
|
||||||
return callback(null)
|
// Don't forget to re activate the scheduler, even if there was an error
|
||||||
|
return requestScheduler.activate()
|
||||||
})
|
})
|
||||||
|
.finally(() => requestScheduler.activate())
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendOwnedVideosToPod (podId: number) {
|
function sendOwnedVideosToPod (podId: number) {
|
||||||
db.Video.listOwnedAndPopulateAuthorAndTags(function (err, videosList) {
|
db.Video.listOwnedAndPopulateAuthorAndTags()
|
||||||
if (err) {
|
.then(videosList => {
|
||||||
logger.error('Cannot get the list of videos we own.')
|
const tasks = []
|
||||||
return
|
videosList.forEach(video => {
|
||||||
}
|
const promise = video.toAddRemoteJSON()
|
||||||
|
.then(remoteVideo => {
|
||||||
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 = {
|
const options = {
|
||||||
type: 'add',
|
type: 'add',
|
||||||
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
endpoint: REQUEST_ENDPOINTS.VIDEOS,
|
||||||
|
@ -256,9 +214,18 @@ function sendOwnedVideosToPod (podId: number) {
|
||||||
toIds: [ podId ],
|
toIds: [ podId ],
|
||||||
transaction: null
|
transaction: null
|
||||||
}
|
}
|
||||||
createRequest(options)
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,23 +264,22 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }, callback: (err: Error) => void) {
|
function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }) {
|
||||||
getForeignPodsList(host, function (err, res) {
|
// TODO: type res
|
||||||
if (err) return callback(err)
|
return getForeignPodsList(host).then((res: any) => {
|
||||||
|
|
||||||
const foreignPodsList = res.data
|
const foreignPodsList = res.data
|
||||||
|
|
||||||
// Let's give 1 point to the pod we ask the friends list
|
// Let's give 1 point to the pod we ask the friends list
|
||||||
foreignPodsList.push({ host })
|
foreignPodsList.push({ host })
|
||||||
|
|
||||||
foreignPodsList.forEach(function (foreignPod) {
|
foreignPodsList.forEach(foreignPod => {
|
||||||
const foreignPodHost = foreignPod.host
|
const foreignPodHost = foreignPod.host
|
||||||
|
|
||||||
if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
|
if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
|
||||||
else podsScore[foreignPodHost] = 1
|
else podsScore[foreignPodHost] = 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return callback(null)
|
return undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +289,7 @@ function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: num
|
||||||
const podsList = []
|
const podsList = []
|
||||||
const baseScore = hosts.length / 2
|
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 the pod is not me and with a good score we add it
|
||||||
if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
|
if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
|
||||||
podsList.push({ host: podHost })
|
podsList.push({ host: podHost })
|
||||||
|
@ -333,28 +299,30 @@ function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: num
|
||||||
return podsList
|
return podsList
|
||||||
}
|
}
|
||||||
|
|
||||||
function getForeignPodsList (host: string, callback: (err: Error, foreignPodsList?: any) => void) {
|
function getForeignPodsList (host: string) {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
const path = '/api/' + API_VERSION + '/pods'
|
const path = '/api/' + API_VERSION + '/pods'
|
||||||
|
|
||||||
request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
|
request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
|
||||||
if (err) return callback(err)
|
if (err) return rej(err)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(body)
|
const json = JSON.parse(body)
|
||||||
return callback(null, json)
|
return res(json)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return callback(err)
|
return rej(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeRequestsToWinningPods (cert: string, podsList: PodInstance[], callback: (err: Error) => void) {
|
function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
|
||||||
// Stop pool requests
|
// Stop pool requests
|
||||||
requestScheduler.deactivate()
|
requestScheduler.deactivate()
|
||||||
// Flush pool requests
|
// Flush pool requests
|
||||||
requestScheduler.forceSend()
|
requestScheduler.forceSend()
|
||||||
|
|
||||||
eachLimit(podsList, REQUESTS_IN_PARALLEL, function (pod: PodInstance, callbackEach) {
|
return Promise.map(podsList, pod => {
|
||||||
const params = {
|
const params = {
|
||||||
url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/pods/',
|
url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/pods/',
|
||||||
method: 'POST' as 'POST',
|
method: 'POST' as 'POST',
|
||||||
|
@ -365,38 +333,35 @@ function makeRequestsToWinningPods (cert: string, podsList: PodInstance[], callb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRetryRequest(params, function (err, res, body: { cert: string, email: string }) {
|
return makeRetryRequest(params)
|
||||||
if (err) {
|
.then(({ response, body }) => {
|
||||||
logger.error('Error with adding %s pod.', pod.host, { error: err })
|
body = body as { cert: string, email: string }
|
||||||
// Don't break the process
|
|
||||||
return callbackEach()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
|
const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
|
||||||
podObj.save().asCallback(function (err, podCreated) {
|
return podObj.save()
|
||||||
if (err) {
|
.then(podCreated => {
|
||||||
logger.error('Cannot add friend %s pod.', pod.host, { error: err })
|
|
||||||
return callbackEach()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add our videos to the request scheduler
|
// Add our videos to the request scheduler
|
||||||
sendOwnedVideosToPod(podCreated.id)
|
sendOwnedVideosToPod(podCreated.id)
|
||||||
|
})
|
||||||
return callbackEach()
|
.catch(err => {
|
||||||
|
logger.error('Cannot add friend %s pod.', pod.host, { error: err })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.error('Status not 200 for %s pod.', pod.host)
|
logger.error('Status not 200 for %s pod.', pod.host)
|
||||||
return callbackEach()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, function endRequests () {
|
.catch(err => {
|
||||||
|
logger.error('Error with adding %s pod.', pod.host, { error: err.stack })
|
||||||
|
// Don't break the process
|
||||||
|
})
|
||||||
|
}, { concurrency: REQUESTS_IN_PARALLEL })
|
||||||
|
.then(() => logger.debug('makeRequestsToWinningPods finished.'))
|
||||||
|
.finally(() => {
|
||||||
// Final callback, we've ended all the requests
|
// Final callback, we've ended all the requests
|
||||||
// Now we made new friends, we can re activate the pool of requests
|
// Now we made new friends, we can re activate the pool of requests
|
||||||
requestScheduler.activate()
|
requestScheduler.activate()
|
||||||
|
|
||||||
logger.debug('makeRequestsToWinningPods finished.')
|
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,33 +373,22 @@ type CreateRequestOptions = {
|
||||||
toIds?: number[]
|
toIds?: number[]
|
||||||
transaction: Sequelize.Transaction
|
transaction: Sequelize.Transaction
|
||||||
}
|
}
|
||||||
function createRequest (options: CreateRequestOptions, callback?: (err: Error) => void) {
|
function createRequest (options: CreateRequestOptions) {
|
||||||
if (!callback) callback = function () { /* empty */ }
|
if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions)
|
||||||
|
|
||||||
if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions, callback)
|
|
||||||
|
|
||||||
// If the "toIds" pods is not specified, we send the request to all our friends
|
// If the "toIds" pods is not specified, we send the request to all our friends
|
||||||
db.Pod.listAllIds(options.transaction, function (err, podIds) {
|
return db.Pod.listAllIds(options.transaction).then(podIds => {
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot get pod ids', { error: err })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const newOptions = Object.assign(options, { toIds: 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) {
|
function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions) {
|
||||||
if (!callback) callback = createEmptyCallback()
|
return requestVideoQaduScheduler.createRequest(options)
|
||||||
|
|
||||||
requestVideoQaduScheduler.createRequest(options, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVideoEventRequest (options: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
|
function createVideoEventRequest (options: RequestVideoEventSchedulerOptions) {
|
||||||
if (!callback) callback = createEmptyCallback()
|
return requestVideoEventScheduler.createRequest(options)
|
||||||
|
|
||||||
requestVideoEventScheduler.createRequest(options, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMe (host: string) {
|
function isMe (host: string) {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import * as videoTranscoder from './video-transcoder'
|
import * as videoTranscoder from './video-transcoder'
|
||||||
|
|
||||||
import { VideoInstance } from '../../../models'
|
|
||||||
|
|
||||||
export interface JobHandler<T> {
|
export interface JobHandler<T> {
|
||||||
process (data: object, callback: (err: Error, videoInstance?: T) => void)
|
process (data: object): T
|
||||||
onError (err: Error, jobId: number, video: T, callback: (err: Error) => void)
|
onError (err: Error, jobId: number)
|
||||||
onSuccess (data: any, jobId: number, video: T, callback: (err: Error) => void)
|
onSuccess (jobId: number, jobResult: T)
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
|
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
|
||||||
|
|
|
@ -3,29 +3,23 @@ import { logger } from '../../../helpers'
|
||||||
import { addVideoToFriends } from '../../../lib'
|
import { addVideoToFriends } from '../../../lib'
|
||||||
import { VideoInstance } from '../../../models'
|
import { VideoInstance } from '../../../models'
|
||||||
|
|
||||||
function process (data: { id: string }, callback: (err: Error, videoInstance?: VideoInstance) => void) {
|
function process (data: { id: string }) {
|
||||||
db.Video.loadAndPopulateAuthorAndPodAndTags(data.id, function (err, video) {
|
return db.Video.loadAndPopulateAuthorAndPodAndTags(data.id).then(video => {
|
||||||
if (err) return callback(err)
|
return video.transcodeVideofile().then(() => video)
|
||||||
|
|
||||||
video.transcodeVideofile(function (err) {
|
|
||||||
return callback(err, 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 })
|
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)
|
logger.info('Job %d is a success.', jobId)
|
||||||
|
|
||||||
video.toAddRemoteJSON(function (err, remoteVideo) {
|
video.toAddRemoteJSON().then(remoteVideo => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// Now we'll add the video's meta data to our friends
|
// Now we'll add the video's meta data to our friends
|
||||||
addVideoToFriends(remoteVideo, null, callback)
|
return addVideoToFriends(remoteVideo, null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@ class JobScheduler {
|
||||||
|
|
||||||
// Finish processing jobs from a previous start
|
// Finish processing jobs from a previous start
|
||||||
const state = JOB_STATES.PROCESSING
|
const state = JOB_STATES.PROCESSING
|
||||||
db.Job.listWithLimit(limit, state, (err, jobs) => {
|
db.Job.listWithLimit(limit, state)
|
||||||
this.enqueueJobs(err, jobsQueue, jobs)
|
.then(jobs => {
|
||||||
|
this.enqueueJobs(jobsQueue, jobs)
|
||||||
|
|
||||||
forever(
|
forever(
|
||||||
next => {
|
next => {
|
||||||
|
@ -43,26 +44,23 @@ class JobScheduler {
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = JOB_STATES.PENDING
|
const state = JOB_STATES.PENDING
|
||||||
db.Job.listWithLimit(limit, state, (err, jobs) => {
|
db.Job.listWithLimit(limit, state)
|
||||||
if (err) {
|
.then(jobs => {
|
||||||
logger.error('Cannot list pending jobs.', { error: err })
|
this.enqueueJobs(jobsQueue, jobs)
|
||||||
} else {
|
|
||||||
jobs.forEach(job => {
|
|
||||||
jobsQueue.push(job)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimization: we could use "drain" from queue object
|
// Optimization: we could use "drain" from queue object
|
||||||
return setTimeout(next, JOBS_FETCHING_INTERVAL)
|
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 }) }
|
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 = {
|
const createQuery = {
|
||||||
state: JOB_STATES.PENDING,
|
state: JOB_STATES.PENDING,
|
||||||
handlerName,
|
handlerName,
|
||||||
|
@ -70,67 +68,62 @@ class JobScheduler {
|
||||||
}
|
}
|
||||||
const options = { transaction }
|
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[]) {
|
private enqueueJobs (jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
|
||||||
if (err) {
|
jobs.forEach(job => jobsQueue.push(job))
|
||||||
logger.error('Cannot list pending jobs.', { error: err })
|
|
||||||
} else {
|
|
||||||
jobs.forEach(job => {
|
|
||||||
jobsQueue.push(job)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private processJob (job: JobInstance, callback: (err: Error) => void) {
|
private processJob (job: JobInstance, callback: (err: Error) => void) {
|
||||||
const jobHandler = jobHandlers[job.handlerName]
|
const jobHandler = jobHandlers[job.handlerName]
|
||||||
|
|
||||||
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) {
|
if (jobHandler === undefined) {
|
||||||
logger.error('Unknown job handler for job %s.', job.handlerName)
|
logger.error('Unknown job handler for job %s.', job.handlerName)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return jobHandler.process(job.handlerInputData, (err, result) => {
|
logger.info('Processing job %d with handler %s.', job.id, job.handlerName)
|
||||||
if (err) {
|
|
||||||
|
job.state = JOB_STATES.PROCESSING
|
||||||
|
return job.save()
|
||||||
|
.then(() => {
|
||||||
|
return jobHandler.process(job.handlerInputData)
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
result => {
|
||||||
|
return this.onJobSuccess(jobHandler, job, result)
|
||||||
|
},
|
||||||
|
|
||||||
|
err => {
|
||||||
logger.error('Error in job handler %s.', job.handlerName, { error: 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.state = JOB_STATES.ERROR
|
||||||
|
|
||||||
job.save().asCallback(err => {
|
return job.save()
|
||||||
if (err) return this.cannotSaveJobError(err, callback)
|
.then(() => jobHandler.onError(err, job.id))
|
||||||
|
.catch(err => this.cannotSaveJobError(err))
|
||||||
return jobHandler.onError(err, job.id, jobResult, callback)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.state = JOB_STATES.SUCCESS
|
||||||
|
|
||||||
job.save().asCallback(err => {
|
return job.save()
|
||||||
if (err) return this.cannotSaveJobError(err, callback)
|
.then(() => jobHandler.onSuccess(job.id, jobResult))
|
||||||
|
.catch(err => this.cannotSaveJobError(err))
|
||||||
return jobHandler.onSuccess(err, job.id, jobResult, callback)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private cannotSaveJobError (err: Error, callback: (err: Error) => void) {
|
private cannotSaveJobError (err: Error) {
|
||||||
logger.error('Cannot save new job state.', { error: err })
|
logger.error('Cannot save new job state.', { error: err })
|
||||||
return callback(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,10 @@ function getUser (username: string, password: string) {
|
||||||
return db.User.getByUsername(username).then(function (user) {
|
return db.User.getByUsername(username).then(function (user) {
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
|
|
||||||
// We need to return a promise
|
return user.isPasswordMatch(password).then(passwordMatch => {
|
||||||
return new Promise(function (resolve, reject) {
|
if (passwordMatch === false) return null
|
||||||
return user.isPasswordMatch(password, function (err, isPasswordMatch) {
|
|
||||||
if (err) return reject(err)
|
|
||||||
|
|
||||||
if (isPasswordMatch === true) {
|
return user
|
||||||
return resolve(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(null)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -80,8 +73,6 @@ function saveToken (token: TokenInfo, client: OAuthClientInstance, user: UserIns
|
||||||
tokenCreated.user = user
|
tokenCreated.user = user
|
||||||
|
|
||||||
return tokenCreated
|
return tokenCreated
|
||||||
}).catch(function (err) {
|
|
||||||
throw err
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import * as eachLimit from 'async/eachLimit'
|
import { isEmpty } from 'lodash'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { logger, makeSecureRequest } from '../../helpers'
|
import { logger, makeSecureRequest } from '../../helpers'
|
||||||
import { PodInstance } from '../../models'
|
import { AbstractRequestClass, AbstractRequestToPodClass, PodInstance } from '../../models'
|
||||||
import {
|
import {
|
||||||
API_VERSION,
|
API_VERSION,
|
||||||
REQUESTS_IN_PARALLEL,
|
REQUESTS_IN_PARALLEL,
|
||||||
REQUESTS_INTERVAL
|
REQUESTS_INTERVAL
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
|
|
||||||
abstract class AbstractRequestScheduler {
|
abstract class AbstractRequestScheduler <T> {
|
||||||
requestInterval: number
|
requestInterval: number
|
||||||
limitPods: number
|
limitPods: number
|
||||||
limitPerPod: number
|
limitPerPod: number
|
||||||
|
@ -24,9 +25,9 @@ abstract class AbstractRequestScheduler {
|
||||||
this.requestInterval = REQUESTS_INTERVAL
|
this.requestInterval = REQUESTS_INTERVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getRequestModel ()
|
abstract getRequestModel (): AbstractRequestClass<T>
|
||||||
abstract getRequestToPodModel ()
|
abstract getRequestToPodModel (): AbstractRequestToPodClass
|
||||||
abstract buildRequestObjects (requests: any)
|
abstract buildRequestObjects (requestsGrouped: T): {}
|
||||||
|
|
||||||
activate () {
|
activate () {
|
||||||
logger.info('Requests scheduler activated.')
|
logger.info('Requests scheduler activated.')
|
||||||
|
@ -55,20 +56,18 @@ abstract class AbstractRequestScheduler {
|
||||||
return REQUESTS_INTERVAL - (Date.now() - this.lastRequestTimestamp)
|
return REQUESTS_INTERVAL - (Date.now() - this.lastRequestTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingRequestsCount (callback: (err: Error, total: number) => void) {
|
remainingRequestsCount () {
|
||||||
return this.getRequestModel().countTotalRequests(callback)
|
return this.getRequestModel().countTotalRequests()
|
||||||
}
|
}
|
||||||
|
|
||||||
flush (callback: (err: Error) => void) {
|
flush () {
|
||||||
this.getRequestModel().removeAll(callback)
|
return this.getRequestModel().removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Make a requests to friends of a certain type
|
// Make a requests to friends of a certain type
|
||||||
protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object, callback) {
|
protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object) {
|
||||||
if (!callback) callback = function () { /* empty */ }
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
toPod: toPod,
|
toPod: toPod,
|
||||||
sign: true, // Prove our identity
|
sign: true, // Prove our identity
|
||||||
|
@ -79,65 +78,64 @@ abstract class AbstractRequestScheduler {
|
||||||
|
|
||||||
// Make multiple retry requests to all of pods
|
// Make multiple retry requests to all of pods
|
||||||
// The function fire some useful callbacks
|
// The function fire some useful callbacks
|
||||||
makeSecureRequest(params, (err, res) => {
|
return makeSecureRequest(params)
|
||||||
if (err || (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 204)) {
|
.then(({ response, body }) => {
|
||||||
err = err ? err.message : 'Status code not 20x : ' + res.statusCode
|
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 })
|
logger.error('Error sending secure request to %s pod.', toPod.host, { error: err })
|
||||||
|
|
||||||
return callback(err)
|
throw err
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make all the requests of the scheduler
|
// Make all the requests of the scheduler
|
||||||
protected makeRequests () {
|
protected makeRequests () {
|
||||||
this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod, (err, requests) => {
|
return this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod)
|
||||||
if (err) {
|
.then((requestsGrouped: T) => {
|
||||||
logger.error('Cannot get the list of "%s".', this.description, { err: err })
|
// We want to group requests by destinations pod and endpoint
|
||||||
return // Abort
|
const requestsToMake = this.buildRequestObjects(requestsGrouped)
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no requests, abort
|
// If there are no requests, abort
|
||||||
if (requests.length === 0) {
|
if (isEmpty(requestsToMake) === true) {
|
||||||
logger.info('No "%s" to make.', this.description)
|
logger.info('No "%s" to make.', this.description)
|
||||||
return
|
return { goodPods: [], badPods: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to group requests by destinations pod and endpoint
|
|
||||||
const requestsToMakeGrouped = this.buildRequestObjects(requests)
|
|
||||||
|
|
||||||
logger.info('Making "%s" to friends.', this.description)
|
logger.info('Making "%s" to friends.', this.description)
|
||||||
|
|
||||||
const goodPods = []
|
const goodPods = []
|
||||||
const badPods = []
|
const badPods = []
|
||||||
|
|
||||||
eachLimit(Object.keys(requestsToMakeGrouped), REQUESTS_IN_PARALLEL, (hashKey, callbackEach) => {
|
return Promise.map(Object.keys(requestsToMake), hashKey => {
|
||||||
const requestToMake = requestsToMakeGrouped[hashKey]
|
const requestToMake = requestsToMake[hashKey]
|
||||||
const toPod = requestToMake.toPod
|
const toPod: PodInstance = requestToMake.toPod
|
||||||
|
|
||||||
this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas, (err) => {
|
|
||||||
if (err) {
|
|
||||||
badPods.push(requestToMake.toPod.id)
|
|
||||||
return callbackEach()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas)
|
||||||
|
.then(() => {
|
||||||
logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
|
logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
|
||||||
goodPods.push(requestToMake.toPod.id)
|
goodPods.push(requestToMake.toPod.id)
|
||||||
|
|
||||||
// Remove the pod id of these request ids
|
|
||||||
this.getRequestToPodModel().removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id, callbackEach)
|
|
||||||
|
|
||||||
this.afterRequestHook()
|
this.afterRequestHook()
|
||||||
})
|
|
||||||
}, () => {
|
|
||||||
// All the requests were made, we update the pods score
|
|
||||||
db.Pod.updatePodsScore(goodPods, badPods)
|
|
||||||
|
|
||||||
|
// 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.afterRequestsHook()
|
||||||
|
|
||||||
|
// All the requests were made, we update the pods score
|
||||||
|
return db.Pod.updatePodsScore(goodPods, badPods)
|
||||||
})
|
})
|
||||||
})
|
.catch(err => logger.error('Cannot get the list of "%s".', this.description, { error: err.stack }))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected afterRequestHook () {
|
protected afterRequestHook () {
|
||||||
|
|
|
@ -3,10 +3,8 @@ import * as Sequelize from 'sequelize'
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { AbstractRequestScheduler } from './abstract-request-scheduler'
|
import { AbstractRequestScheduler } from './abstract-request-scheduler'
|
||||||
import { logger } from '../../helpers'
|
import { logger } from '../../helpers'
|
||||||
import {
|
import { REQUESTS_LIMIT_PODS, REQUESTS_LIMIT_PER_POD } from '../../initializers'
|
||||||
REQUESTS_LIMIT_PODS,
|
import { RequestsGrouped } from '../../models'
|
||||||
REQUESTS_LIMIT_PER_POD
|
|
||||||
} from '../../initializers'
|
|
||||||
import { RequestEndpoint } from '../../../shared'
|
import { RequestEndpoint } from '../../../shared'
|
||||||
|
|
||||||
export type RequestSchedulerOptions = {
|
export type RequestSchedulerOptions = {
|
||||||
|
@ -17,7 +15,7 @@ export type RequestSchedulerOptions = {
|
||||||
transaction: Sequelize.Transaction
|
transaction: Sequelize.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestScheduler extends AbstractRequestScheduler {
|
class RequestScheduler extends AbstractRequestScheduler<RequestsGrouped> {
|
||||||
constructor () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
@ -36,11 +34,11 @@ class RequestScheduler extends AbstractRequestScheduler {
|
||||||
return db.RequestToPod
|
return db.RequestToPod
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRequestObjects (requests: { [ toPodId: number ]: any }) {
|
buildRequestObjects (requestsGrouped: RequestsGrouped) {
|
||||||
const requestsToMakeGrouped = {}
|
const requestsToMakeGrouped = {}
|
||||||
|
|
||||||
Object.keys(requests).forEach(toPodId => {
|
Object.keys(requestsGrouped).forEach(toPodId => {
|
||||||
requests[toPodId].forEach(data => {
|
requestsGrouped[toPodId].forEach(data => {
|
||||||
const request = data.request
|
const request = data.request
|
||||||
const pod = data.pod
|
const pod = data.pod
|
||||||
const hashKey = toPodId + request.endpoint
|
const hashKey = toPodId + request.endpoint
|
||||||
|
@ -62,12 +60,12 @@ class RequestScheduler extends AbstractRequestScheduler {
|
||||||
return requestsToMakeGrouped
|
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
|
// TODO: check the setPods works
|
||||||
const podIds = []
|
const podIds = []
|
||||||
|
|
||||||
// If there are no destination pods abort
|
// If there are no destination pods abort
|
||||||
if (toIds.length === 0) return callback(null)
|
if (toIds.length === 0) return undefined
|
||||||
|
|
||||||
toIds.forEach(toPod => {
|
toIds.forEach(toPod => {
|
||||||
podIds.push(toPod)
|
podIds.push(toPod)
|
||||||
|
@ -85,10 +83,9 @@ class RequestScheduler extends AbstractRequestScheduler {
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Request.create(createQuery, dbRequestOptions).asCallback((err, request) => {
|
return db.Request.create(createQuery, dbRequestOptions)
|
||||||
if (err) return callback(err)
|
.then(request => {
|
||||||
|
return request.setPods(podIds, dbRequestOptions)
|
||||||
return request.setPods(podIds, dbRequestOptions).asCallback(callback)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,9 +93,8 @@ class RequestScheduler extends AbstractRequestScheduler {
|
||||||
|
|
||||||
afterRequestsHook () {
|
afterRequestsHook () {
|
||||||
// Flush requests with no pod
|
// Flush requests with no pod
|
||||||
this.getRequestModel().removeWithEmptyTo(err => {
|
this.getRequestModel().removeWithEmptyTo()
|
||||||
if (err) logger.error('Error when removing requests with no pods.', { error: err })
|
.catch(err => logger.error('Error when removing requests with no pods.', { error: err }))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
REQUESTS_VIDEO_EVENT_LIMIT_PER_POD,
|
REQUESTS_VIDEO_EVENT_LIMIT_PER_POD,
|
||||||
REQUEST_VIDEO_EVENT_ENDPOINT
|
REQUEST_VIDEO_EVENT_ENDPOINT
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
|
import { RequestsVideoEventGrouped } from '../../models'
|
||||||
import { RequestVideoEventType } from '../../../shared'
|
import { RequestVideoEventType } from '../../../shared'
|
||||||
|
|
||||||
export type RequestVideoEventSchedulerOptions = {
|
export type RequestVideoEventSchedulerOptions = {
|
||||||
|
@ -16,7 +17,7 @@ export type RequestVideoEventSchedulerOptions = {
|
||||||
transaction?: Sequelize.Transaction
|
transaction?: Sequelize.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestVideoEventScheduler extends AbstractRequestScheduler {
|
class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoEventGrouped> {
|
||||||
constructor () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
|
||||||
return db.RequestVideoEvent
|
return db.RequestVideoEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRequestObjects (eventsToProcess: { [ toPodId: number ]: any }[]) {
|
buildRequestObjects (eventRequests: RequestsVideoEventGrouped) {
|
||||||
const requestsToMakeGrouped = {}
|
const requestsToMakeGrouped = {}
|
||||||
|
|
||||||
/* Example:
|
/* Example:
|
||||||
|
@ -50,8 +51,8 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
|
||||||
|
|
||||||
// We group video events per video and per pod
|
// We group video events per video and per pod
|
||||||
// We add the counts of the same event types
|
// We add the counts of the same event types
|
||||||
Object.keys(eventsToProcess).forEach(toPodId => {
|
Object.keys(eventRequests).forEach(toPodId => {
|
||||||
eventsToProcess[toPodId].forEach(eventToProcess => {
|
eventRequests[toPodId].forEach(eventToProcess => {
|
||||||
if (!eventsPerVideoPerPod[toPodId]) eventsPerVideoPerPod[toPodId] = {}
|
if (!eventsPerVideoPerPod[toPodId]) eventsPerVideoPerPod[toPodId] = {}
|
||||||
|
|
||||||
if (!requestsToMakeGrouped[toPodId]) {
|
if (!requestsToMakeGrouped[toPodId]) {
|
||||||
|
@ -97,7 +98,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
|
||||||
return requestsToMakeGrouped
|
return requestsToMakeGrouped
|
||||||
}
|
}
|
||||||
|
|
||||||
createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
|
createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions) {
|
||||||
if (count === undefined) count = 1
|
if (count === undefined) count = 1
|
||||||
|
|
||||||
const dbRequestOptions: Sequelize.CreateOptions = {}
|
const dbRequestOptions: Sequelize.CreateOptions = {}
|
||||||
|
@ -109,7 +110,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
|
||||||
videoId
|
videoId
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.RequestVideoEvent.create(createQuery, dbRequestOptions).asCallback(callback)
|
return db.RequestVideoEvent.create(createQuery, dbRequestOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
REQUEST_VIDEO_QADU_ENDPOINT,
|
REQUEST_VIDEO_QADU_ENDPOINT,
|
||||||
REQUEST_VIDEO_QADU_TYPES
|
REQUEST_VIDEO_QADU_TYPES
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
|
import { RequestsVideoQaduGrouped } from '../../models'
|
||||||
import { RequestVideoQaduType } from '../../../shared'
|
import { RequestVideoQaduType } from '../../../shared'
|
||||||
|
|
||||||
export type RequestVideoQaduSchedulerOptions = {
|
export type RequestVideoQaduSchedulerOptions = {
|
||||||
|
@ -17,7 +18,7 @@ export type RequestVideoQaduSchedulerOptions = {
|
||||||
transaction?: Sequelize.Transaction
|
transaction?: Sequelize.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestVideoQaduScheduler extends AbstractRequestScheduler {
|
class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQaduGrouped> {
|
||||||
constructor () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler {
|
||||||
return db.RequestVideoQadu
|
return db.RequestVideoQadu
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRequestObjects (requests: { [ toPodId: number ]: any }[]) {
|
buildRequestObjects (requests: RequestsVideoQaduGrouped) {
|
||||||
const requestsToMakeGrouped = {}
|
const requestsToMakeGrouped = {}
|
||||||
|
|
||||||
Object.keys(requests).forEach(toPodId => {
|
Object.keys(requests).forEach(toPodId => {
|
||||||
|
@ -105,20 +106,18 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler {
|
||||||
return requestsToMakeGrouped
|
return requestsToMakeGrouped
|
||||||
}
|
}
|
||||||
|
|
||||||
createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions, callback: (err: Error) => void) {
|
createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions) {
|
||||||
const dbRequestOptions: Sequelize.BulkCreateOptions = {}
|
const dbRequestOptions: Sequelize.BulkCreateOptions = {}
|
||||||
if (transaction) dbRequestOptions.transaction = transaction
|
if (transaction) dbRequestOptions.transaction = transaction
|
||||||
|
|
||||||
// Send the update to all our friends
|
// Send the update to all our friends
|
||||||
db.Pod.listAllIds(transaction, function (err, podIds) {
|
return db.Pod.listAllIds(transaction).then(podIds => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const queries = []
|
const queries = []
|
||||||
podIds.forEach(podId => {
|
podIds.forEach(podId => {
|
||||||
queries.push({ type, videoId, podId })
|
queries.push({ type, videoId, podId })
|
||||||
})
|
})
|
||||||
|
|
||||||
return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions).asCallback(callback)
|
return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,8 @@ import {
|
||||||
|
|
||||||
function checkSignature (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function checkSignature (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const host = req.body.signature.host
|
const host = req.body.signature.host
|
||||||
db.Pod.loadByHost(host, function (err, pod) {
|
db.Pod.loadByHost(host)
|
||||||
if (err) {
|
.then(pod => {
|
||||||
logger.error('Cannot get signed host in body.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pod === null) {
|
if (pod === null) {
|
||||||
logger.error('Unknown pod %s.', host)
|
logger.error('Unknown pod %s.', host)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
|
@ -44,6 +40,10 @@ function checkSignature (req: express.Request, res: express.Response, next: expr
|
||||||
logger.error('Signature is not okay in body for %s.', req.body.signature.host)
|
logger.error('Signature is not okay in body for %s.', req.body.signature.host)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot get signed host in body.', { error: err })
|
||||||
|
return res.sendStatus(500)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -19,12 +19,8 @@ function makeFriendsValidator (req: express.Request, res: express.Response, next
|
||||||
logger.debug('Checking makeFriends parameters', { parameters: req.body })
|
logger.debug('Checking makeFriends parameters', { parameters: req.body })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
hasFriends(function (err, heHasFriends) {
|
hasFriends()
|
||||||
if (err) {
|
.then(heHasFriends => {
|
||||||
logger.error('Cannot know if we have friends.', { error: err })
|
|
||||||
res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (heHasFriends === true) {
|
if (heHasFriends === true) {
|
||||||
// We need to quit our friends before make new ones
|
// We need to quit our friends before make new ones
|
||||||
return res.sendStatus(409)
|
return res.sendStatus(409)
|
||||||
|
@ -32,6 +28,10 @@ function makeFriendsValidator (req: express.Request, res: express.Response, next
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot know if we have friends.', { error: err })
|
||||||
|
res.sendStatus(500)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,8 @@ function podsAddValidator (req: express.Request, res: express.Response, next: ex
|
||||||
logger.debug('Checking podsAdd parameters', { parameters: req.body })
|
logger.debug('Checking podsAdd parameters', { parameters: req.body })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
db.Pod.loadByHost(req.body.host, function (err, pod) {
|
db.Pod.loadByHost(req.body.host)
|
||||||
if (err) {
|
.then(pod => {
|
||||||
logger.error('Cannot load pod by host.', { error: err })
|
|
||||||
res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pod with this host already exists
|
// Pod with this host already exists
|
||||||
if (pod) {
|
if (pod) {
|
||||||
return res.sendStatus(409)
|
return res.sendStatus(409)
|
||||||
|
@ -55,6 +51,10 @@ function podsAddValidator (req: express.Request, res: express.Response, next: ex
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot load pod by host.', { error: err })
|
||||||
|
res.sendStatus(500)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,16 +13,16 @@ function usersAddValidator (req: express.Request, res: express.Response, next: e
|
||||||
logger.debug('Checking usersAdd parameters', { parameters: req.body })
|
logger.debug('Checking usersAdd parameters', { parameters: req.body })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
db.User.loadByUsernameOrEmail(req.body.username, req.body.email, function (err, user) {
|
db.User.loadByUsernameOrEmail(req.body.username, req.body.email)
|
||||||
if (err) {
|
.then(user => {
|
||||||
logger.error('Error in usersAdd request validator.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user) return res.status(409).send('User already exists.')
|
if (user) return res.status(409).send('User already exists.')
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Error in usersAdd request validator.', { error: err })
|
||||||
|
return res.sendStatus(500)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,18 +32,18 @@ function usersRemoveValidator (req: express.Request, res: express.Response, next
|
||||||
logger.debug('Checking usersRemove parameters', { parameters: req.params })
|
logger.debug('Checking usersRemove parameters', { parameters: req.params })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
db.User.loadById(req.params.id, function (err, user) {
|
db.User.loadById(req.params.id)
|
||||||
if (err) {
|
.then(user => {
|
||||||
logger.error('Error in usersRemove request validator.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) return res.status(404).send('User not found')
|
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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,16 +64,16 @@ function usersVideoRatingValidator (req: express.Request, res: express.Response,
|
||||||
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
|
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
db.Video.load(req.params.videoId, function (err, video) {
|
db.Video.load(req.params.videoId)
|
||||||
if (err) {
|
.then(video => {
|
||||||
logger.error('Error in user request validator.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!video) return res.status(404).send('Video not found')
|
if (!video) return res.status(404).send('Video not found')
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Error in user request validator.', { error: err })
|
||||||
|
return res.sendStatus(500)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'express-validator'
|
import 'express-validator'
|
||||||
import * as multer from 'multer'
|
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
|
@ -24,18 +23,19 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
const videoFile = req.files.videofile[0]
|
const videoFile = req.files.videofile[0]
|
||||||
|
|
||||||
db.Video.getDurationFromFile(videoFile.path, function (err, duration) {
|
db.Video.getDurationFromFile(videoFile.path)
|
||||||
if (err) {
|
.then(duration => {
|
||||||
return res.status(400).send('Cannot retrieve metadata of the file.')
|
if (!isVideoDurationValid('' + duration)) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!isVideoDurationValid(duration)) {
|
|
||||||
return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
|
return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile['duration'] = duration
|
videoFile['duration'] = duration
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Error in getting duration from file.', { error: err })
|
||||||
|
res.status(400).send('Cannot retrieve metadata of the file.')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,27 +157,22 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function checkVideoExists (id: string, res: express.Response, callback: () => void) {
|
function checkVideoExists (id: string, res: express.Response, callback: () => void) {
|
||||||
db.Video.loadAndPopulateAuthorAndPodAndTags(id, function (err, video) {
|
db.Video.loadAndPopulateAuthorAndPodAndTags(id).then(video => {
|
||||||
if (err) {
|
|
||||||
logger.error('Error in video request validator.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!video) return res.status(404).send('Video not found')
|
if (!video) return res.status(404).send('Video not found')
|
||||||
|
|
||||||
res.locals.video = video
|
res.locals.video = video
|
||||||
callback()
|
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) {
|
function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) {
|
||||||
// Retrieve the user who did the request
|
// Retrieve the user who did the request
|
||||||
db.User.loadById(userId, function (err, user) {
|
db.User.loadById(userId)
|
||||||
if (err) {
|
.then(user => {
|
||||||
logger.error('Error in video request validator.', { error: err })
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user can delete the video
|
// Check if the user can delete the video
|
||||||
// The user can delete it if s/he is an admin
|
// The user can delete it if s/he is an admin
|
||||||
// Or if s/he is the video's author
|
// Or if s/he is the video's author
|
||||||
|
@ -194,6 +189,10 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac
|
||||||
// If we reach this comment, we can delete the video
|
// If we reach this comment, we can delete the video
|
||||||
callback()
|
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) {
|
function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
export namespace ApplicationMethods {
|
export namespace ApplicationMethods {
|
||||||
export type LoadMigrationVersionCallback = (err: Error, version: number) => void
|
export type LoadMigrationVersion = () => Promise<number>
|
||||||
export type LoadMigrationVersion = (callback: LoadMigrationVersionCallback) => void
|
|
||||||
|
|
||||||
export type UpdateMigrationVersionCallback = (err: Error, applicationInstance: ApplicationAttributes) => void
|
export type UpdateMigrationVersion = (
|
||||||
export type UpdateMigrationVersion = (newVersion: number, transaction: Sequelize.Transaction, callback: UpdateMigrationVersionCallback) => void
|
newVersion: number,
|
||||||
|
transaction: Sequelize.Transaction
|
||||||
|
) => Promise<[ number, ApplicationInstance[] ]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApplicationClass {
|
export interface ApplicationClass {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
ApplicationClass,
|
|
||||||
ApplicationAttributes,
|
ApplicationAttributes,
|
||||||
ApplicationInstance,
|
ApplicationInstance,
|
||||||
|
|
||||||
|
@ -35,23 +34,19 @@ export default function defineApplication (sequelize: Sequelize.Sequelize, DataT
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
loadMigrationVersion = function (callback: ApplicationMethods.LoadMigrationVersionCallback) {
|
loadMigrationVersion = function () {
|
||||||
const query = {
|
const query = {
|
||||||
attributes: [ 'migrationVersion' ]
|
attributes: [ 'migrationVersion' ]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Application.findOne(query).asCallback(function (err, data) {
|
return Application.findOne(query).then(data => data ? data.migrationVersion : null)
|
||||||
const version = data ? data.migrationVersion : null
|
|
||||||
|
|
||||||
return callback(err, version)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction, callback: ApplicationMethods.UpdateMigrationVersionCallback) {
|
updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction) {
|
||||||
const options: Sequelize.UpdateOptions = {
|
const options: Sequelize.UpdateOptions = {
|
||||||
where: {},
|
where: {},
|
||||||
transaction: transaction
|
transaction: transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
return Application.update({ migrationVersion: newVersion }, options).asCallback(callback)
|
return Application.update({ migrationVersion: newVersion }, options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { JobState } from '../../../shared/models/job.model'
|
import { JobState } from '../../../shared/models/job.model'
|
||||||
|
|
||||||
export namespace JobMethods {
|
export namespace JobMethods {
|
||||||
export type ListWithLimitCallback = (err: Error, jobInstances: JobInstance[]) => void
|
export type ListWithLimit = (limit: number, state: JobState) => Promise<JobInstance[]>
|
||||||
export type ListWithLimit = (limit: number, state: JobState, callback: ListWithLimitCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JobClass {
|
export interface JobClass {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { JOB_STATES } from '../../initializers'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
JobClass,
|
|
||||||
JobInstance,
|
JobInstance,
|
||||||
JobAttributes,
|
JobAttributes,
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ export default function defineJob (sequelize: Sequelize.Sequelize, DataTypes: Se
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
listWithLimit = function (limit: number, state: JobState, callback: JobMethods.ListWithLimitCallback) {
|
listWithLimit = function (limit: number, state: JobState) {
|
||||||
const query = {
|
const query = {
|
||||||
order: [
|
order: [
|
||||||
[ 'id', 'ASC' ]
|
[ 'id', 'ASC' ]
|
||||||
|
@ -60,5 +59,5 @@ listWithLimit = function (limit: number, state: JobState, callback: JobMethods.L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Job.findAll(query).asCallback(callback)
|
return Job.findAll(query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
export namespace OAuthClientMethods {
|
export namespace OAuthClientMethods {
|
||||||
export type CountTotalCallback = (err: Error, total: number) => void
|
export type CountTotal = () => Promise<number>
|
||||||
export type CountTotal = (callback: CountTotalCallback) => void
|
|
||||||
|
|
||||||
export type LoadFirstClientCallback = (err: Error, client: OAuthClientInstance) => void
|
export type LoadFirstClient = () => Promise<OAuthClientInstance>
|
||||||
export type LoadFirstClient = (callback: LoadFirstClientCallback) => void
|
|
||||||
|
|
||||||
export type GetByIdAndSecret = (clientId, clientSecret) => void
|
export type GetByIdAndSecret = (clientId: string, clientSecret: string) => Promise<OAuthClientInstance>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OAuthClientClass {
|
export interface OAuthClientClass {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
OAuthClientClass,
|
|
||||||
OAuthClientInstance,
|
OAuthClientInstance,
|
||||||
OAuthClientAttributes,
|
OAuthClientAttributes,
|
||||||
|
|
||||||
|
@ -67,12 +66,12 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotal = function (callback: OAuthClientMethods.CountTotalCallback) {
|
countTotal = function () {
|
||||||
return OAuthClient.count().asCallback(callback)
|
return OAuthClient.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFirstClient = function (callback: OAuthClientMethods.LoadFirstClientCallback) {
|
loadFirstClient = function () {
|
||||||
return OAuthClient.findOne().asCallback(callback)
|
return OAuthClient.findOne()
|
||||||
}
|
}
|
||||||
|
|
||||||
getByIdAndSecret = function (clientId: string, clientSecret: string) {
|
getByIdAndSecret = function (clientId: string, clientSecret: string) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { UserModel } from '../user'
|
import { UserModel } from '../user'
|
||||||
|
|
||||||
|
@ -15,12 +15,11 @@ export type OAuthTokenInfo = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace OAuthTokenMethods {
|
export namespace OAuthTokenMethods {
|
||||||
export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Bluebird<OAuthTokenInfo>
|
export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Promise<OAuthTokenInfo>
|
||||||
export type GetByTokenAndPopulateUser = (bearerToken: string) => Bluebird<OAuthTokenInstance>
|
export type GetByTokenAndPopulateUser = (bearerToken: string) => Promise<OAuthTokenInstance>
|
||||||
export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Bluebird<OAuthTokenInstance>
|
export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Promise<OAuthTokenInstance>
|
||||||
|
|
||||||
export type RemoveByUserIdCallback = (err: Error) => void
|
export type RemoveByUserId = (userId) => Promise<number>
|
||||||
export type RemoveByUserId = (userId, callback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OAuthTokenClass {
|
export interface OAuthTokenClass {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { logger } from '../../helpers'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
OAuthTokenClass,
|
|
||||||
OAuthTokenInstance,
|
OAuthTokenInstance,
|
||||||
OAuthTokenAttributes,
|
OAuthTokenAttributes,
|
||||||
|
|
||||||
|
@ -149,12 +148,12 @@ getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
removeByUserId = function (userId, callback) {
|
removeByUserId = function (userId: number) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
userId: userId
|
userId: userId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OAuthToken.destroy(query).asCallback(callback)
|
return OAuthToken.destroy(query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// Don't use barrel, import just what we need
|
// Don't use barrel, import just what we need
|
||||||
import { Pod as FormatedPod } from '../../../shared/models/pod.model'
|
import { Pod as FormatedPod } from '../../../shared/models/pod.model'
|
||||||
|
@ -6,32 +7,23 @@ import { Pod as FormatedPod } from '../../../shared/models/pod.model'
|
||||||
export namespace PodMethods {
|
export namespace PodMethods {
|
||||||
export type ToFormatedJSON = (this: PodInstance) => FormatedPod
|
export type ToFormatedJSON = (this: PodInstance) => FormatedPod
|
||||||
|
|
||||||
export type CountAllCallback = (err: Error, total: number) => void
|
export type CountAll = () => Promise<number>
|
||||||
export type CountAll = (callback) => void
|
|
||||||
|
|
||||||
export type IncrementScoresCallback = (err: Error) => void
|
export type IncrementScores = (ids: number[], value: number) => Promise<[ number, PodInstance[] ]>
|
||||||
export type IncrementScores = (ids: number[], value: number, callback?: IncrementScoresCallback) => void
|
|
||||||
|
|
||||||
export type ListCallback = (err: Error, podInstances?: PodInstance[]) => void
|
export type List = () => Promise<PodInstance[]>
|
||||||
export type List = (callback: ListCallback) => void
|
|
||||||
|
|
||||||
export type ListAllIdsCallback = (err: Error, ids?: number[]) => void
|
export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
|
||||||
export type ListAllIds = (transaction: Sequelize.Transaction, callback: ListAllIdsCallback) => void
|
|
||||||
|
|
||||||
export type ListRandomPodIdsWithRequestCallback = (err: Error, podInstanceIds?: number[]) => void
|
export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
|
||||||
export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: ListRandomPodIdsWithRequestCallback) => void
|
|
||||||
|
|
||||||
export type ListBadPodsCallback = (err: Error, podInstances?: PodInstance[]) => void
|
export type ListBadPods = () => Promise<PodInstance[]>
|
||||||
export type ListBadPods = (callback: ListBadPodsCallback) => void
|
|
||||||
|
|
||||||
export type LoadCallback = (err: Error, podInstance: PodInstance) => void
|
export type Load = (id: number) => Promise<PodInstance>
|
||||||
export type Load = (id: number, callback: LoadCallback) => void
|
|
||||||
|
|
||||||
export type LoadByHostCallback = (err: Error, podInstance: PodInstance) => void
|
export type LoadByHost = (host: string) => Promise<PodInstance>
|
||||||
export type LoadByHost = (host: string, callback: LoadByHostCallback) => void
|
|
||||||
|
|
||||||
export type RemoveAllCallback = (err: Error) => void
|
export type RemoveAll = () => Promise<number>
|
||||||
export type RemoveAll = (callback: RemoveAllCallback) => void
|
|
||||||
|
|
||||||
export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
|
export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { each, waterfall } from 'async'
|
|
||||||
import { map } from 'lodash'
|
import { map } from 'lodash'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
|
@ -7,7 +6,6 @@ import { logger, isHostValid } from '../../helpers'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
PodClass,
|
|
||||||
PodInstance,
|
PodInstance,
|
||||||
PodAttributes,
|
PodAttributes,
|
||||||
|
|
||||||
|
@ -118,13 +116,11 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countAll = function (callback: PodMethods.CountAllCallback) {
|
countAll = function () {
|
||||||
return Pod.count().asCallback(callback)
|
return Pod.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
incrementScores = function (ids: number[], value: number, callback?: PodMethods.IncrementScoresCallback) {
|
incrementScores = function (ids: number[], value: number) {
|
||||||
if (!callback) callback = function () { /* empty */ }
|
|
||||||
|
|
||||||
const update = {
|
const update = {
|
||||||
score: Sequelize.literal('score +' + value)
|
score: Sequelize.literal('score +' + value)
|
||||||
}
|
}
|
||||||
|
@ -139,33 +135,28 @@ incrementScores = function (ids: number[], value: number, callback?: PodMethods.
|
||||||
validate: false
|
validate: false
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pod.update(update, options).asCallback(callback)
|
return Pod.update(update, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
list = function (callback: PodMethods.ListCallback) {
|
list = function () {
|
||||||
return Pod.findAll().asCallback(callback)
|
return Pod.findAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
listAllIds = function (transaction: Sequelize.Transaction, callback: PodMethods.ListAllIdsCallback) {
|
listAllIds = function (transaction: Sequelize.Transaction) {
|
||||||
const query: any = {
|
const query: Sequelize.FindOptions = {
|
||||||
attributes: [ 'id' ]
|
attributes: [ 'id' ],
|
||||||
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction !== null) query.transaction = transaction
|
return Pod.findAll(query).then(pods => {
|
||||||
|
return map(pods, 'id')
|
||||||
return Pod.findAll(query).asCallback(function (err: Error, pods) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
return callback(null, map(pods, 'id'))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: PodMethods.ListRandomPodIdsWithRequestCallback) {
|
listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
|
||||||
Pod.count().asCallback(function (err, count) {
|
return Pod.count().then(count => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// Optimization...
|
// Optimization...
|
||||||
if (count === 0) return callback(null, [])
|
if (count === 0) return []
|
||||||
|
|
||||||
let start = Math.floor(Math.random() * count) - limit
|
let start = Math.floor(Math.random() * count) - limit
|
||||||
if (start < 0) start = 0
|
if (start < 0) start = 0
|
||||||
|
@ -186,55 +177,54 @@ listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, ta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pod.findAll(query).asCallback(function (err, pods) {
|
return Pod.findAll(query).then(pods => {
|
||||||
if (err) return callback(err)
|
return map(pods, 'id')
|
||||||
|
|
||||||
return callback(null, map(pods, 'id'))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listBadPods = function (callback: PodMethods.ListBadPodsCallback) {
|
listBadPods = function () {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
score: { $lte: 0 }
|
score: { $lte: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pod.findAll(query).asCallback(callback)
|
return Pod.findAll(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
load = function (id: number, callback: PodMethods.LoadCallback) {
|
load = function (id: number) {
|
||||||
return Pod.findById(id).asCallback(callback)
|
return Pod.findById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadByHost = function (host: string, callback: PodMethods.LoadByHostCallback) {
|
loadByHost = function (host: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
host: host
|
host: host
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pod.findOne(query).asCallback(callback)
|
return Pod.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll = function (callback: PodMethods.RemoveAllCallback) {
|
removeAll = function () {
|
||||||
return Pod.destroy().asCallback(callback)
|
return Pod.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePodsScore = function (goodPods: number[], badPods: number[]) {
|
updatePodsScore = function (goodPods: number[], badPods: number[]) {
|
||||||
logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
|
logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
|
||||||
|
|
||||||
if (goodPods.length !== 0) {
|
if (goodPods.length !== 0) {
|
||||||
incrementScores(goodPods, PODS_SCORE.BONUS, function (err) {
|
incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
|
||||||
if (err) logger.error('Cannot increment scores of good pods.', { error: err })
|
logger.error('Cannot increment scores of good pods.', { error: err })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badPods.length !== 0) {
|
if (badPods.length !== 0) {
|
||||||
incrementScores(badPods, PODS_SCORE.MALUS, function (err) {
|
incrementScores(badPods, PODS_SCORE.MALUS)
|
||||||
|
.then(() => removeBadPods())
|
||||||
|
.catch(err => {
|
||||||
if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
|
if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
|
||||||
removeBadPods()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,32 +233,19 @@ updatePodsScore = function (goodPods: number[], badPods: number[]) {
|
||||||
|
|
||||||
// Remove pods with a score of 0 (too many requests where they were unreachable)
|
// Remove pods with a score of 0 (too many requests where they were unreachable)
|
||||||
function removeBadPods () {
|
function removeBadPods () {
|
||||||
waterfall([
|
return listBadPods()
|
||||||
function findBadPods (callback) {
|
.then(pods => {
|
||||||
listBadPods(function (err, pods) {
|
const podsRemovePromises = pods.map(pod => pod.destroy())
|
||||||
if (err) {
|
return Promise.all(podsRemovePromises).then(() => pods.length)
|
||||||
logger.error('Cannot find bad pods.', { error: err })
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, pods)
|
|
||||||
})
|
})
|
||||||
},
|
.then(numberOfPodsRemoved => {
|
||||||
|
if (numberOfPodsRemoved) {
|
||||||
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) {
|
|
||||||
logger.error('Cannot remove bad pods.', { error: err })
|
|
||||||
} else if (numberOfPodsRemoved) {
|
|
||||||
logger.info('Removed %d pods.', numberOfPodsRemoved)
|
logger.info('Removed %d pods.', numberOfPodsRemoved)
|
||||||
} else {
|
} else {
|
||||||
logger.info('No need to remove bad pods.')
|
logger.info('No need to remove bad pods.')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Cannot remove bad pods.', { error: err })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
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>
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
export * from './abstract-request-interface'
|
||||||
export * from './request-interface'
|
export * from './request-interface'
|
||||||
export * from './request-to-pod-interface'
|
export * from './request-to-pod-interface'
|
||||||
export * from './request-video-event-interface'
|
export * from './request-video-event-interface'
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
import { AbstractRequestClass } from './abstract-request-interface'
|
||||||
import { PodInstance, PodAttributes } from '../pod'
|
import { PodInstance, PodAttributes } from '../pod'
|
||||||
import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
|
import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
|
||||||
|
|
||||||
|
@ -11,20 +13,16 @@ export type RequestsGrouped = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace RequestMethods {
|
export namespace RequestMethods {
|
||||||
export type CountTotalRequestsCallback = (err: Error, total: number) => void
|
export type CountTotalRequests = () => Promise<number>
|
||||||
export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
|
|
||||||
|
|
||||||
export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsGrouped) => void
|
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
|
||||||
export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback: ListWithLimitAndRandomCallback) => void
|
|
||||||
|
|
||||||
export type RemoveWithEmptyToCallback = (err: Error) => void
|
export type RemoveWithEmptyTo = () => Promise<number>
|
||||||
export type RemoveWithEmptyTo = (callback: RemoveWithEmptyToCallback) => void
|
|
||||||
|
|
||||||
export type RemoveAllCallback = (err: Error) => void
|
export type RemoveAll = () => Promise<void>
|
||||||
export type RemoveAll = (callback: RemoveAllCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestClass {
|
export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
|
||||||
countTotalRequests: RequestMethods.CountTotalRequests
|
countTotalRequests: RequestMethods.CountTotalRequests
|
||||||
listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
|
listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
|
||||||
removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
|
removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
import { AbstractRequestToPodClass } from './abstract-request-interface'
|
||||||
|
|
||||||
export namespace RequestToPodMethods {
|
export namespace RequestToPodMethods {
|
||||||
export type RemoveByRequestIdsAndPodCallback = (err: Error) => void
|
export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
|
||||||
export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number, callback?: RemoveByRequestIdsAndPodCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestToPodClass {
|
export interface RequestToPodClass extends AbstractRequestToPodClass {
|
||||||
removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
|
removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
RequestToPodClass,
|
|
||||||
RequestToPodInstance,
|
RequestToPodInstance,
|
||||||
RequestToPodAttributes,
|
RequestToPodAttributes,
|
||||||
|
|
||||||
|
@ -38,9 +37,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callback?: RequestToPodMethods.RemoveByRequestIdsAndPodCallback) {
|
removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
|
||||||
if (!callback) callback = function () { /* empty */ }
|
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
requestId: {
|
requestId: {
|
||||||
|
@ -50,5 +47,5 @@ removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestToPod.destroy(query).asCallback(callback)
|
return RequestToPod.destroy(query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
|
||||||
import { VideoInstance } from '../video'
|
import { VideoInstance } from '../video'
|
||||||
import { PodInstance } from '../pod'
|
import { PodInstance } from '../pod'
|
||||||
|
|
||||||
|
@ -16,20 +18,16 @@ export type RequestsVideoEventGrouped = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace RequestVideoEventMethods {
|
export namespace RequestVideoEventMethods {
|
||||||
export type CountTotalRequestsCallback = (err: Error, total: number) => void
|
export type CountTotalRequests = () => Promise<number>
|
||||||
export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
|
|
||||||
|
|
||||||
export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoEventGrouped) => void
|
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
|
||||||
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
|
|
||||||
|
|
||||||
export type RemoveByRequestIdsAndPodCallback = () => void
|
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
|
||||||
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
|
|
||||||
|
|
||||||
export type RemoveAllCallback = () => void
|
export type RemoveAll = () => Promise<void>
|
||||||
export type RemoveAll = (callback: RemoveAllCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoEventClass {
|
export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
|
||||||
countTotalRequests: RequestVideoEventMethods.CountTotalRequests
|
countTotalRequests: RequestVideoEventMethods.CountTotalRequests
|
||||||
listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
|
listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
|
||||||
removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
|
removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
|
||||||
|
@ -41,10 +39,12 @@ export interface RequestVideoEventAttributes {
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoEventInstance extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
|
export interface RequestVideoEventInstance
|
||||||
|
extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
Video: VideoInstance
|
Video: VideoInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
|
export interface RequestVideoEventModel
|
||||||
|
extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers'
|
||||||
import { isVideoEventCountValid } from '../../helpers'
|
import { isVideoEventCountValid } from '../../helpers'
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
RequestVideoEventClass,
|
|
||||||
RequestVideoEventInstance,
|
RequestVideoEventInstance,
|
||||||
RequestVideoEventAttributes,
|
RequestVideoEventAttributes,
|
||||||
|
|
||||||
|
@ -77,23 +76,21 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotalRequests = function (callback: RequestVideoEventMethods.CountTotalRequestsCallback) {
|
countTotalRequests = function () {
|
||||||
const query = {}
|
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
|
const Pod = db.Pod
|
||||||
|
|
||||||
// We make a join between videos and authors to find the podId of our video event requests
|
// 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" ' +
|
const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' +
|
||||||
'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
|
'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
|
||||||
|
|
||||||
Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins, function (err, podIds) {
|
return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// We don't have friends that have requests
|
// We don't have friends that have requests
|
||||||
if (podIds.length === 0) return callback(null, [])
|
if (podIds.length === 0) return []
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
order: [
|
order: [
|
||||||
|
@ -121,16 +118,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestVideoEvent.findAll(query).asCallback(function (err, requests) {
|
return RequestVideoEvent.findAll(query).then(requests => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
|
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 = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
|
@ -152,12 +147,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestVideoEvent.destroy(query).asCallback(callback)
|
return RequestVideoEvent.destroy(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll = function (callback: RequestVideoEventMethods.RemoveAllCallback) {
|
removeAll = function () {
|
||||||
// Delete all requests
|
// Delete all requests
|
||||||
RequestVideoEvent.truncate({ cascade: true }).asCallback(callback)
|
return RequestVideoEvent.truncate({ cascade: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
|
||||||
import { VideoInstance } from '../video'
|
import { VideoInstance } from '../video'
|
||||||
import { PodInstance } from '../pod'
|
import { PodInstance } from '../pod'
|
||||||
|
|
||||||
|
@ -14,20 +16,16 @@ export type RequestsVideoQaduGrouped = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace RequestVideoQaduMethods {
|
export namespace RequestVideoQaduMethods {
|
||||||
export type CountTotalRequestsCallback = (err: Error, total: number) => void
|
export type CountTotalRequests = () => Promise<number>
|
||||||
export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
|
|
||||||
|
|
||||||
export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoQaduGrouped) => void
|
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
|
||||||
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
|
|
||||||
|
|
||||||
export type RemoveByRequestIdsAndPodCallback = () => void
|
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
|
||||||
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
|
|
||||||
|
|
||||||
export type RemoveAllCallback = () => void
|
export type RemoveAll = () => Promise<void>
|
||||||
export type RemoveAll = (callback: RemoveAllCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoQaduClass {
|
export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
|
||||||
countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
|
countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
|
||||||
listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
|
listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
|
||||||
removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
|
removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
|
||||||
|
@ -38,11 +36,13 @@ export interface RequestVideoQaduAttributes {
|
||||||
type: RequestVideoQaduType
|
type: RequestVideoQaduType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoQaduInstance extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
|
export interface RequestVideoQaduInstance
|
||||||
|
extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
Pod: PodInstance
|
Pod: PodInstance
|
||||||
Video: VideoInstance
|
Video: VideoInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
|
export interface RequestVideoQaduModel
|
||||||
|
extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import { database as db } from '../../initializers/database'
|
||||||
import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
|
import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
RequestVideoQaduClass,
|
|
||||||
RequestVideoQaduInstance,
|
RequestVideoQaduInstance,
|
||||||
RequestVideoQaduAttributes,
|
RequestVideoQaduAttributes,
|
||||||
|
|
||||||
|
@ -83,20 +82,18 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotalRequests = function (callback: RequestVideoQaduMethods.CountTotalRequestsCallback) {
|
countTotalRequests = function () {
|
||||||
const query = {}
|
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 Pod = db.Pod
|
||||||
const tableJoin = ''
|
const tableJoin = ''
|
||||||
|
|
||||||
Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin, function (err, podIds) {
|
return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// We don't have friends that have requests
|
// We don't have friends that have requests
|
||||||
if (podIds.length === 0) return callback(null, [])
|
if (podIds.length === 0) return []
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
include: [
|
include: [
|
||||||
|
@ -114,16 +111,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestVideoQadu.findAll(query).asCallback(function (err, requests) {
|
return RequestVideoQadu.findAll(query).then(requests => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
|
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 = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
|
@ -133,12 +128,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestVideoQadu.destroy(query).asCallback(callback)
|
return RequestVideoQadu.destroy(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll = function (callback: RequestVideoQaduMethods.RemoveAllCallback) {
|
removeAll = function () {
|
||||||
// Delete all requests
|
// Delete all requests
|
||||||
RequestVideoQadu.truncate({ cascade: true }).asCallback(callback)
|
return RequestVideoQadu.truncate({ cascade: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { database as db } from '../../initializers/database'
|
||||||
import { REQUEST_ENDPOINTS } from '../../initializers'
|
import { REQUEST_ENDPOINTS } from '../../initializers'
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
RequestClass,
|
|
||||||
RequestInstance,
|
RequestInstance,
|
||||||
RequestAttributes,
|
RequestAttributes,
|
||||||
|
|
||||||
|
@ -60,25 +59,23 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotalRequests = function (callback: RequestMethods.CountTotalRequestsCallback) {
|
countTotalRequests = function () {
|
||||||
// We need to include Pod because there are no cascade delete when a pod is removed
|
// 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
|
// So we could count requests that do not have existing pod anymore
|
||||||
const query = {
|
const query = {
|
||||||
include: [ Request['sequelize'].models.Pod ]
|
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 Pod = db.Pod
|
||||||
const tableJoin = ''
|
const tableJoin = ''
|
||||||
|
|
||||||
Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', '', function (err, podIds) {
|
return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// We don't have friends that have requests
|
// 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
|
// The first x requests of these pods
|
||||||
// It is very important to sort by id ASC to keep the requests order!
|
// It is very important to sort by id ASC to keep the requests order!
|
||||||
|
@ -98,23 +95,20 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Request.findAll(query).asCallback(function (err, requests) {
|
return Request.findAll(query).then(requests => {
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
|
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
|
||||||
return callback(err, requestsGrouped)
|
return requestsGrouped
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll = function (callback: RequestMethods.RemoveAllCallback) {
|
removeAll = function () {
|
||||||
// Delete all requests
|
// Delete all requests
|
||||||
Request.truncate({ cascade: true }).asCallback(callback)
|
return Request.truncate({ cascade: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallback) {
|
removeWithEmptyTo = function () {
|
||||||
if (!callback) callback = function () { /* empty */ }
|
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
|
@ -125,7 +119,7 @@ removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Request.destroy(query).asCallback(callback)
|
return Request.destroy(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,35 +1,29 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
// Don't use barrel, import just what we need
|
// Don't use barrel, import just what we need
|
||||||
import { UserRole, User as FormatedUser } from '../../../shared/models/user.model'
|
import { UserRole, User as FormatedUser } from '../../../shared/models/user.model'
|
||||||
|
import { ResultList } from '../../../shared/models/result-list.model'
|
||||||
|
|
||||||
export namespace UserMethods {
|
export namespace UserMethods {
|
||||||
export type IsPasswordMatchCallback = (err: Error, same: boolean) => void
|
export type IsPasswordMatch = (this: UserInstance, password: string) => Promise<boolean>
|
||||||
export type IsPasswordMatch = (this: UserInstance, password: string, callback: IsPasswordMatchCallback) => void
|
|
||||||
|
|
||||||
export type ToFormatedJSON = (this: UserInstance) => FormatedUser
|
export type ToFormatedJSON = (this: UserInstance) => FormatedUser
|
||||||
export type IsAdmin = (this: UserInstance) => boolean
|
export type IsAdmin = (this: UserInstance) => boolean
|
||||||
|
|
||||||
export type CountTotalCallback = (err: Error, total: number) => void
|
export type CountTotal = () => Promise<number>
|
||||||
export type CountTotal = (callback: CountTotalCallback) => void
|
|
||||||
|
|
||||||
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 = () => Promise<UserInstance[]>
|
||||||
export type List = (callback: ListCallback) => void
|
|
||||||
|
|
||||||
export type ListForApiCallback = (err: Error, userInstances?: UserInstance[], total?: number) => void
|
export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<UserInstance> >
|
||||||
export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
|
|
||||||
|
|
||||||
export type LoadByIdCallback = (err: Error, userInstance: UserInstance) => void
|
export type LoadById = (id: number) => Promise<UserInstance>
|
||||||
export type LoadById = (id: number, callback: LoadByIdCallback) => void
|
|
||||||
|
|
||||||
export type LoadByUsernameCallback = (err: Error, userInstance: UserInstance) => void
|
export type LoadByUsername = (username: string) => Promise<UserInstance>
|
||||||
export type LoadByUsername = (username: string, callback: LoadByUsernameCallback) => void
|
|
||||||
|
|
||||||
export type LoadByUsernameOrEmailCallback = (err: Error, userInstance: UserInstance) => void
|
export type LoadByUsernameOrEmail = (username: string, email: string) => Promise<UserInstance>
|
||||||
export type LoadByUsernameOrEmail = (username: string, email: string, callback: LoadByUsernameOrEmailCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserClass {
|
export interface UserClass {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { VideoRateType } from '../../../shared/models/user-video-rate.model'
|
import { VideoRateType } from '../../../shared/models/user-video-rate.model'
|
||||||
|
|
||||||
export namespace UserVideoRateMethods {
|
export namespace UserVideoRateMethods {
|
||||||
export type LoadCallback = (err: Error, userVideoRateInstance: UserVideoRateInstance) => void
|
export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
|
||||||
export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: LoadCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserVideoRateClass {
|
export interface UserVideoRateClass {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { VIDEO_RATE_TYPES } from '../../initializers'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
UserVideoRateClass,
|
|
||||||
UserVideoRateInstance,
|
UserVideoRateInstance,
|
||||||
UserVideoRateAttributes,
|
UserVideoRateAttributes,
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
const options: Sequelize.FindOptions = {
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
|
@ -75,5 +74,5 @@ load = function (userId: number, videoId: string, transaction: Sequelize.Transac
|
||||||
}
|
}
|
||||||
if (transaction) options.transaction = transaction
|
if (transaction) options.transaction = transaction
|
||||||
|
|
||||||
return UserVideoRate.findOne(options).asCallback(callback)
|
return UserVideoRate.findOne(options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
UserClass,
|
|
||||||
UserInstance,
|
UserInstance,
|
||||||
UserAttributes,
|
UserAttributes,
|
||||||
|
|
||||||
|
@ -118,21 +117,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeCreateOrUpdate (user: UserInstance) {
|
function beforeCreateOrUpdate (user: UserInstance) {
|
||||||
return new Promise(function (resolve, reject) {
|
return cryptPassword(user.password).then(hash => {
|
||||||
cryptPassword(user.password, function (err, hash) {
|
|
||||||
if (err) return reject(err)
|
|
||||||
|
|
||||||
user.password = hash
|
user.password = hash
|
||||||
|
return undefined
|
||||||
return resolve()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ METHODS ------------------------------
|
// ------------------------------ METHODS ------------------------------
|
||||||
|
|
||||||
isPasswordMatch = function (this: UserInstance, password: string, callback: UserMethods.IsPasswordMatchCallback) {
|
isPasswordMatch = function (this: UserInstance, password: string) {
|
||||||
return comparePassword(password, this.password, callback)
|
return comparePassword(password, this.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
toFormatedJSON = function (this: UserInstance) {
|
toFormatedJSON = function (this: UserInstance) {
|
||||||
|
@ -164,8 +158,8 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotal = function (callback: UserMethods.CountTotalCallback) {
|
countTotal = function () {
|
||||||
return this.count().asCallback(callback)
|
return this.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
getByUsername = function (username: string) {
|
getByUsername = function (username: string) {
|
||||||
|
@ -178,44 +172,45 @@ getByUsername = function (username: string) {
|
||||||
return User.findOne(query)
|
return User.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
list = function (callback: UserMethods.ListCallback) {
|
list = function () {
|
||||||
return User.find().asCallback(callback)
|
return User.findAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
listForApi = function (start: number, count: number, sort: string, callback: UserMethods.ListForApiCallback) {
|
listForApi = function (start: number, count: number, sort: string) {
|
||||||
const query = {
|
const query = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
order: [ getSort(sort) ]
|
order: [ getSort(sort) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
return User.findAndCountAll(query).asCallback(function (err, result) {
|
return User.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
if (err) return callback(err)
|
return {
|
||||||
|
data: rows,
|
||||||
return callback(null, result.rows, result.count)
|
total: count
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById = function (id: number, callback: UserMethods.LoadByIdCallback) {
|
loadById = function (id: number) {
|
||||||
return User.findById(id).asCallback(callback)
|
return User.findById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadByUsername = function (username: string, callback: UserMethods.LoadByUsernameCallback) {
|
loadByUsername = function (username: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
username: username
|
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 = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
$or: [ { username }, { email } ]
|
$or: [ { username }, { email } ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return User.findOne(query).asCallback(callback)
|
return User.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { PodInstance } from '../pod'
|
import { PodInstance } from '../pod'
|
||||||
|
|
||||||
export namespace AuthorMethods {
|
export namespace AuthorMethods {
|
||||||
export type FindOrCreateAuthorCallback = (err: Error, authorInstance?: AuthorInstance) => void
|
export type FindOrCreateAuthor = (
|
||||||
export type FindOrCreateAuthor = (name: string, podId: number, userId: number, transaction: Sequelize.Transaction, callback: FindOrCreateAuthorCallback) => void
|
name: string,
|
||||||
|
podId: number,
|
||||||
|
userId: number,
|
||||||
|
transaction: Sequelize.Transaction
|
||||||
|
) => Promise<AuthorInstance>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthorClass {
|
export interface AuthorClass {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { isUserUsernameValid } from '../../helpers'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
AuthorClass,
|
|
||||||
AuthorInstance,
|
AuthorInstance,
|
||||||
AuthorAttributes,
|
AuthorAttributes,
|
||||||
|
|
||||||
|
@ -74,30 +73,18 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
findOrCreateAuthor = function (
|
findOrCreateAuthor = function (name: string, podId: number, userId: number, transaction: Sequelize.Transaction) {
|
||||||
name: string,
|
|
||||||
podId: number,
|
|
||||||
userId: number,
|
|
||||||
transaction: Sequelize.Transaction,
|
|
||||||
callback: AuthorMethods.FindOrCreateAuthorCallback
|
|
||||||
) {
|
|
||||||
const author = {
|
const author = {
|
||||||
name,
|
name,
|
||||||
podId,
|
podId,
|
||||||
userId
|
userId
|
||||||
}
|
}
|
||||||
|
|
||||||
const query: any = {
|
const query: Sequelize.FindOrInitializeOptions<AuthorAttributes> = {
|
||||||
where: author,
|
where: author,
|
||||||
defaults: author
|
defaults: author,
|
||||||
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction !== null) query.transaction = transaction
|
return Author.findOrCreate(query).then(([ authorInstance ]) => authorInstance)
|
||||||
|
|
||||||
Author.findOrCreate(query).asCallback(function (err, result) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
// [ instance, wasCreated ]
|
|
||||||
return callback(null, result[0])
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
export namespace TagMethods {
|
export namespace TagMethods {
|
||||||
export type FindOrCreateTagsCallback = (err: Error, tagInstances: TagInstance[]) => void
|
export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction) => Promise<TagInstance[]>
|
||||||
export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction, callback: FindOrCreateTagsCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagClass {
|
export interface TagClass {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { each } from 'async'
|
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import {
|
import {
|
||||||
TagClass,
|
|
||||||
TagInstance,
|
TagInstance,
|
||||||
TagAttributes,
|
TagAttributes,
|
||||||
|
|
||||||
|
@ -52,10 +51,9 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction, callback: TagMethods.FindOrCreateTagsCallback) {
|
findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction) {
|
||||||
const tagInstances = []
|
const tasks: Promise<TagInstance>[] = []
|
||||||
|
tags.forEach(tag => {
|
||||||
each<string, Error>(tags, function (tag, callbackEach) {
|
|
||||||
const query: any = {
|
const query: any = {
|
||||||
where: {
|
where: {
|
||||||
name: tag
|
name: tag
|
||||||
|
@ -67,15 +65,9 @@ findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction,
|
||||||
|
|
||||||
if (transaction) query.transaction = transaction
|
if (transaction) query.transaction = transaction
|
||||||
|
|
||||||
Tag.findOrCreate(query).asCallback(function (err, res) {
|
const promise = Tag.findOrCreate(query).then(([ tagInstance ]) => tagInstance)
|
||||||
if (err) return callbackEach(err)
|
tasks.push(promise)
|
||||||
|
})
|
||||||
|
|
||||||
// res = [ tag, isCreated ]
|
return Promise.all(tasks)
|
||||||
const tag = res[0]
|
|
||||||
tagInstances.push(tag)
|
|
||||||
return callbackEach()
|
|
||||||
})
|
|
||||||
}, function (err) {
|
|
||||||
return callback(err, tagInstances)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { PodInstance } from '../pod'
|
import { PodInstance } from '../pod'
|
||||||
|
import { ResultList } from '../../../shared'
|
||||||
|
|
||||||
// Don't use barrel, import just what we need
|
// Don't use barrel, import just what we need
|
||||||
import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model'
|
import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model'
|
||||||
|
@ -8,8 +10,7 @@ import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-a
|
||||||
export namespace VideoAbuseMethods {
|
export namespace VideoAbuseMethods {
|
||||||
export type ToFormatedJSON = (this: VideoAbuseInstance) => FormatedVideoAbuse
|
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) => Promise< ResultList<VideoAbuseInstance> >
|
||||||
export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoAbuseClass {
|
export interface VideoAbuseClass {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { isVideoAbuseReporterUsernameValid, isVideoAbuseReasonValid } from '../.
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import {
|
import {
|
||||||
VideoAbuseClass,
|
|
||||||
VideoAbuseInstance,
|
VideoAbuseInstance,
|
||||||
VideoAbuseAttributes,
|
VideoAbuseAttributes,
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listForApi = function (start: number, count: number, sort: string, callback: VideoAbuseMethods.ListForApiCallback) {
|
listForApi = function (start: number, count: number, sort: string) {
|
||||||
const query = {
|
const query = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
|
@ -122,11 +121,7 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return VideoAbuse.findAndCountAll(query).asCallback(function (err, result) {
|
return VideoAbuse.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
if (err) return callback(err)
|
return { total: count, data: rows }
|
||||||
|
|
||||||
return callback(null, result.rows, result.count)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
|
import { ResultList } from '../../../shared'
|
||||||
|
|
||||||
// Don't use barrel, import just what we need
|
// Don't use barrel, import just what we need
|
||||||
import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model'
|
import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model'
|
||||||
|
@ -6,20 +9,15 @@ import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/mo
|
||||||
export namespace BlacklistedVideoMethods {
|
export namespace BlacklistedVideoMethods {
|
||||||
export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo
|
export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo
|
||||||
|
|
||||||
export type CountTotalCallback = (err: Error, total: number) => void
|
export type CountTotal = () => Promise<number>
|
||||||
export type CountTotal = (callback: CountTotalCallback) => void
|
|
||||||
|
|
||||||
export type ListCallback = (err: Error, backlistedVideoInstances: BlacklistedVideoInstance[]) => void
|
export type List = () => Promise<BlacklistedVideoInstance[]>
|
||||||
export type List = (callback: ListCallback) => void
|
|
||||||
|
|
||||||
export type ListForApiCallback = (err: Error, blacklistedVIdeoInstances?: BlacklistedVideoInstance[], total?: number) => void
|
export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<BlacklistedVideoInstance> >
|
||||||
export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
|
|
||||||
|
|
||||||
export type LoadByIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
|
export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
|
||||||
export type LoadById = (id: number, callback: LoadByIdCallback) => void
|
|
||||||
|
|
||||||
export type LoadByVideoIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
|
export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance>
|
||||||
export type LoadByVideoId = (id: string, callback: LoadByVideoIdCallback) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlacklistedVideoClass {
|
export interface BlacklistedVideoClass {
|
||||||
|
@ -35,7 +33,8 @@ export interface BlacklistedVideoAttributes {
|
||||||
videoId: string
|
videoId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlacklistedVideoInstance extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
|
export interface BlacklistedVideoInstance
|
||||||
|
extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
@ -43,4 +42,5 @@ export interface BlacklistedVideoInstance extends BlacklistedVideoClass, Blackli
|
||||||
toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
|
toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlacklistedVideoModel extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
|
export interface BlacklistedVideoModel
|
||||||
|
extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import {
|
import {
|
||||||
BlacklistedVideoClass,
|
|
||||||
BlacklistedVideoInstance,
|
BlacklistedVideoInstance,
|
||||||
BlacklistedVideoAttributes,
|
BlacklistedVideoAttributes,
|
||||||
|
|
||||||
|
@ -66,38 +65,39 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
countTotal = function (callback: BlacklistedVideoMethods.CountTotalCallback) {
|
countTotal = function () {
|
||||||
return BlacklistedVideo.count().asCallback(callback)
|
return BlacklistedVideo.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
list = function (callback: BlacklistedVideoMethods.ListCallback) {
|
list = function () {
|
||||||
return BlacklistedVideo.findAll().asCallback(callback)
|
return BlacklistedVideo.findAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
listForApi = function (start: number, count: number, sort: string, callback: BlacklistedVideoMethods.ListForApiCallback) {
|
listForApi = function (start: number, count: number, sort: string) {
|
||||||
const query = {
|
const query = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
order: [ getSort(sort) ]
|
order: [ getSort(sort) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
return BlacklistedVideo.findAndCountAll(query).asCallback(function (err, result) {
|
return BlacklistedVideo.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
if (err) return callback(err)
|
return {
|
||||||
|
data: rows,
|
||||||
return callback(null, result.rows, result.count)
|
total: count
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById = function (id: number, callback: BlacklistedVideoMethods.LoadByIdCallback) {
|
loadById = function (id: number) {
|
||||||
return BlacklistedVideo.findById(id).asCallback(callback)
|
return BlacklistedVideo.findById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadByVideoId = function (id: string, callback: BlacklistedVideoMethods.LoadByIdCallback) {
|
loadByVideoId = function (id: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
videoId: id
|
videoId: id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BlacklistedVideo.find(query).asCallback(callback)
|
return BlacklistedVideo.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { AuthorInstance } from './author-interface'
|
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
|
// Don't use barrel, import just what we need
|
||||||
import { Video as FormatedVideo } from '../../../shared/models/video.model'
|
import { Video as FormatedVideo } from '../../../shared/models/video.model'
|
||||||
|
import { ResultList } from '../../../shared/models/result-list.model'
|
||||||
|
|
||||||
export type FormatedAddRemoteVideo = {
|
export type FormatedAddRemoteVideo = {
|
||||||
name: string
|
name: string
|
||||||
|
@ -56,46 +58,32 @@ export namespace VideoMethods {
|
||||||
export type IsOwned = (this: VideoInstance) => boolean
|
export type IsOwned = (this: VideoInstance) => boolean
|
||||||
export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo
|
export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo
|
||||||
|
|
||||||
export type ToAddRemoteJSONCallback = (err: Error, videoFormated?: FormatedAddRemoteVideo) => void
|
export type ToAddRemoteJSON = (this: VideoInstance) => Promise<FormatedAddRemoteVideo>
|
||||||
export type ToAddRemoteJSON = (this: VideoInstance, callback: ToAddRemoteJSONCallback) => void
|
|
||||||
|
|
||||||
export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo
|
export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo
|
||||||
|
|
||||||
export type TranscodeVideofileCallback = (err: Error) => void
|
export type TranscodeVideofile = (this: VideoInstance) => Promise<void>
|
||||||
export type TranscodeVideofile = (this: VideoInstance, callback: TranscodeVideofileCallback) => void
|
|
||||||
|
|
||||||
export type GenerateThumbnailFromDataCallback = (err: Error, thumbnailName?: string) => void
|
// Return thumbnail name
|
||||||
export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string, callback: GenerateThumbnailFromDataCallback) => void
|
export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string>
|
||||||
|
export type GetDurationFromFile = (videoPath: string) => Promise<number>
|
||||||
|
|
||||||
export type GetDurationFromFileCallback = (err: Error, duration?: number) => void
|
export type List = () => Promise<VideoInstance[]>
|
||||||
export type GetDurationFromFile = (videoPath, callback) => void
|
export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]>
|
||||||
|
export type ListOwnedByAuthor = (author: string) => Promise<VideoInstance[]>
|
||||||
|
|
||||||
export type ListCallback = (err: Error, videoInstances: VideoInstance[]) => void
|
export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> >
|
||||||
export type List = (callback: ListCallback) => void
|
export type SearchAndPopulateAuthorAndPodAndTags = (
|
||||||
|
value: string,
|
||||||
|
field: string,
|
||||||
|
start: number,
|
||||||
|
count: number,
|
||||||
|
sort: string
|
||||||
|
) => Promise< ResultList<VideoInstance> >
|
||||||
|
|
||||||
export type ListForApiCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
|
export type Load = (id: string) => Promise<VideoInstance>
|
||||||
export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
|
export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance>
|
||||||
|
export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance>
|
||||||
export type LoadByHostAndRemoteIdCallback = (err: Error, videoInstance: VideoInstance) => void
|
export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance>
|
||||||
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 interface VideoClass {
|
export interface VideoClass {
|
||||||
|
@ -139,7 +127,7 @@ export interface VideoAttributes {
|
||||||
dislikes?: number
|
dislikes?: number
|
||||||
|
|
||||||
Author?: AuthorInstance
|
Author?: AuthorInstance
|
||||||
Tags?: VideoTagInstance[]
|
Tags?: TagInstance[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
|
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
|
||||||
|
@ -157,6 +145,8 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In
|
||||||
toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
|
toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
|
||||||
toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
|
toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
|
||||||
transcodeVideofile: VideoMethods.TranscodeVideofile
|
transcodeVideofile: VideoMethods.TranscodeVideofile
|
||||||
|
|
||||||
|
setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
|
export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
|
||||||
import {
|
import {
|
||||||
VideoTagClass,
|
|
||||||
VideoTagInstance,
|
VideoTagInstance,
|
||||||
VideoTagAttributes,
|
VideoTagAttributes
|
||||||
|
|
||||||
VideoTagMethods
|
|
||||||
} from './video-tag-interface'
|
} from './video-tag-interface'
|
||||||
|
|
||||||
let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
|
let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import * as safeBuffer from 'safe-buffer'
|
import * as safeBuffer from 'safe-buffer'
|
||||||
const Buffer = safeBuffer.Buffer
|
const Buffer = safeBuffer.Buffer
|
||||||
import * as createTorrent from 'create-torrent'
|
|
||||||
import * as ffmpeg from 'fluent-ffmpeg'
|
import * as ffmpeg from 'fluent-ffmpeg'
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import { map, values } from 'lodash'
|
import { map, values } from 'lodash'
|
||||||
import { parallel, series } from 'async'
|
|
||||||
import * as parseTorrent from 'parse-torrent'
|
import * as parseTorrent from 'parse-torrent'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { VideoTagInstance } from './video-tag-interface'
|
import { TagInstance } from './tag-interface'
|
||||||
import {
|
import {
|
||||||
logger,
|
logger,
|
||||||
isVideoNameValid,
|
isVideoNameValid,
|
||||||
|
@ -21,7 +19,12 @@ import {
|
||||||
isVideoNSFWValid,
|
isVideoNSFWValid,
|
||||||
isVideoDescriptionValid,
|
isVideoDescriptionValid,
|
||||||
isVideoInfoHashValid,
|
isVideoInfoHashValid,
|
||||||
isVideoDurationValid
|
isVideoDurationValid,
|
||||||
|
readFileBufferPromise,
|
||||||
|
unlinkPromise,
|
||||||
|
renamePromise,
|
||||||
|
writeFilePromise,
|
||||||
|
createTorrentPromise
|
||||||
} from '../../helpers'
|
} from '../../helpers'
|
||||||
import {
|
import {
|
||||||
CONSTRAINTS_FIELDS,
|
CONSTRAINTS_FIELDS,
|
||||||
|
@ -37,7 +40,6 @@ import { JobScheduler, removeVideoToFriends } from '../../lib'
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import {
|
import {
|
||||||
VideoClass,
|
|
||||||
VideoInstance,
|
VideoInstance,
|
||||||
VideoAttributes,
|
VideoAttributes,
|
||||||
|
|
||||||
|
@ -260,7 +262,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
toFormatedJSON,
|
toFormatedJSON,
|
||||||
toAddRemoteJSON,
|
toAddRemoteJSON,
|
||||||
toUpdateRemoteJSON,
|
toUpdateRemoteJSON,
|
||||||
transcodeVideofile,
|
transcodeVideofile
|
||||||
]
|
]
|
||||||
addMethodsToModel(Video, classMethods, instanceMethods)
|
addMethodsToModel(Video, classMethods, instanceMethods)
|
||||||
|
|
||||||
|
@ -276,91 +278,53 @@ function beforeValidate (video: VideoInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
|
function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
const tasks = []
|
|
||||||
|
|
||||||
if (video.isOwned()) {
|
if (video.isOwned()) {
|
||||||
const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
|
const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
tasks.push(
|
tasks.push(
|
||||||
function createVideoTorrent (callback) {
|
createTorrentFromVideo(video, videoPath),
|
||||||
createTorrentFromVideo(video, videoPath, callback)
|
createThumbnail(video, videoPath),
|
||||||
},
|
createPreview(video, videoPath)
|
||||||
|
|
||||||
function createVideoThumbnail (callback) {
|
|
||||||
createThumbnail(video, videoPath, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function createVideoPreview (callback) {
|
|
||||||
createPreview(video, videoPath, callback)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CONFIG.TRANSCODING.ENABLED === true) {
|
if (CONFIG.TRANSCODING.ENABLED === true) {
|
||||||
tasks.push(
|
|
||||||
function createVideoTranscoderJob (callback) {
|
|
||||||
const dataInput = {
|
const dataInput = {
|
||||||
id: video.id
|
id: video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback)
|
tasks.push(
|
||||||
}
|
JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parallel(tasks, function (err) {
|
return Promise.all(tasks)
|
||||||
if (err) return reject(err)
|
|
||||||
|
|
||||||
return resolve()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve()
|
return Promise.resolve()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterDestroy (video: VideoInstance) {
|
function afterDestroy (video: VideoInstance) {
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
const tasks = []
|
const tasks = []
|
||||||
|
|
||||||
tasks.push(
|
tasks.push(
|
||||||
function (callback) {
|
removeThumbnail(video)
|
||||||
removeThumbnail(video, callback)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (video.isOwned()) {
|
if (video.isOwned()) {
|
||||||
tasks.push(
|
const removeVideoToFriendsParams = {
|
||||||
function removeVideoFile (callback) {
|
|
||||||
removeFile(video, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function removeVideoTorrent (callback) {
|
|
||||||
removeTorrent(video, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function removeVideoPreview (callback) {
|
|
||||||
removePreview(video, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function notifyFriends (callback) {
|
|
||||||
const params = {
|
|
||||||
remoteId: video.id
|
remoteId: video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
removeVideoToFriends(params)
|
tasks.push(
|
||||||
|
removeFile(video),
|
||||||
return callback()
|
removeTorrent(video),
|
||||||
}
|
removePreview(video),
|
||||||
|
removeVideoToFriends(removeVideoToFriendsParams)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
parallel(tasks, function (err) {
|
return Promise.all(tasks)
|
||||||
if (err) return reject(err)
|
|
||||||
|
|
||||||
return resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ METHODS ------------------------------
|
// ------------------------------ METHODS ------------------------------
|
||||||
|
@ -488,7 +452,7 @@ toFormatedJSON = function (this: VideoInstance) {
|
||||||
views: this.views,
|
views: this.views,
|
||||||
likes: this.likes,
|
likes: this.likes,
|
||||||
dislikes: this.dislikes,
|
dislikes: this.dislikes,
|
||||||
tags: map<VideoTagInstance, string>(this.Tags, 'name'),
|
tags: map<TagInstance, string>(this.Tags, 'name'),
|
||||||
thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
|
thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt
|
updatedAt: this.updatedAt
|
||||||
|
@ -497,15 +461,11 @@ toFormatedJSON = function (this: VideoInstance) {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRemoteJSONCallback) {
|
toAddRemoteJSON = function (this: VideoInstance) {
|
||||||
// Get thumbnail data to send to the other pod
|
// Get thumbnail data to send to the other pod
|
||||||
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
|
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 = {
|
const remoteVideo = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
category: this.category,
|
category: this.category,
|
||||||
|
@ -518,7 +478,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
|
||||||
author: this.Author.name,
|
author: this.Author.name,
|
||||||
duration: this.duration,
|
duration: this.duration,
|
||||||
thumbnailData: thumbnailData.toString('binary'),
|
thumbnailData: thumbnailData.toString('binary'),
|
||||||
tags: map<VideoTagInstance, string>(this.Tags, 'name'),
|
tags: map<TagInstance, string>(this.Tags, 'name'),
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt,
|
||||||
extname: this.extname,
|
extname: this.extname,
|
||||||
|
@ -527,7 +487,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
|
||||||
dislikes: this.dislikes
|
dislikes: this.dislikes
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, remoteVideo)
|
return remoteVideo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +503,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
|
||||||
remoteId: this.id,
|
remoteId: this.id,
|
||||||
author: this.Author.name,
|
author: this.Author.name,
|
||||||
duration: this.duration,
|
duration: this.duration,
|
||||||
tags: map<VideoTagInstance, string>(this.Tags, 'name'),
|
tags: map<TagInstance, string>(this.Tags, 'name'),
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt,
|
||||||
extname: this.extname,
|
extname: this.extname,
|
||||||
|
@ -555,7 +515,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.TranscodeVideofileCallback) {
|
transcodeVideofile = function (this: VideoInstance) {
|
||||||
const video = this
|
const video = this
|
||||||
|
|
||||||
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
|
@ -563,78 +523,73 @@ transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.
|
||||||
const videoInputPath = join(videosDirectory, video.getVideoFilename())
|
const videoInputPath = join(videosDirectory, video.getVideoFilename())
|
||||||
const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
|
const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
|
||||||
|
|
||||||
|
return new Promise<void>((res, rej) => {
|
||||||
ffmpeg(videoInputPath)
|
ffmpeg(videoInputPath)
|
||||||
.output(videoOutputPath)
|
.output(videoOutputPath)
|
||||||
.videoCodec('libx264')
|
.videoCodec('libx264')
|
||||||
.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
|
.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
|
||||||
.outputOption('-movflags faststart')
|
.outputOption('-movflags faststart')
|
||||||
.on('error', finalCallback)
|
.on('error', rej)
|
||||||
.on('end', function () {
|
.on('end', () => {
|
||||||
series([
|
|
||||||
function removeOldFile (callback) {
|
|
||||||
fs.unlink(videoInputPath, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function moveNewFile (callback) {
|
return unlinkPromise(videoInputPath)
|
||||||
|
.then(() => {
|
||||||
// Important to do this before getVideoFilename() to take in account the new file extension
|
// Important to do this before getVideoFilename() to take in account the new file extension
|
||||||
video.set('extname', newExtname)
|
video.set('extname', newExtname)
|
||||||
|
|
||||||
const newVideoPath = join(videosDirectory, video.getVideoFilename())
|
const newVideoPath = join(videosDirectory, video.getVideoFilename())
|
||||||
fs.rename(videoOutputPath, newVideoPath, callback)
|
return renamePromise(videoOutputPath, newVideoPath)
|
||||||
},
|
})
|
||||||
|
.then(() => {
|
||||||
function torrent (callback) {
|
|
||||||
const newVideoPath = join(videosDirectory, video.getVideoFilename())
|
const newVideoPath = join(videosDirectory, video.getVideoFilename())
|
||||||
createTorrentFromVideo(video, newVideoPath, callback)
|
return createTorrentFromVideo(video, newVideoPath)
|
||||||
},
|
})
|
||||||
|
.then(() => {
|
||||||
function videoExtension (callback) {
|
return video.save()
|
||||||
video.save().asCallback(callback)
|
})
|
||||||
}
|
.then(() => {
|
||||||
|
return res()
|
||||||
], function (err: Error) {
|
})
|
||||||
if (err) {
|
.catch(err => {
|
||||||
// Autodesctruction...
|
// Autodesctruction...
|
||||||
video.destroy().asCallback(function (err) {
|
video.destroy().asCallback(function (err) {
|
||||||
if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
|
if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
|
||||||
})
|
})
|
||||||
|
|
||||||
return finalCallback(err)
|
return rej(err)
|
||||||
}
|
|
||||||
|
|
||||||
return finalCallback(null)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.run()
|
.run()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ STATICS ------------------------------
|
// ------------------------------ STATICS ------------------------------
|
||||||
|
|
||||||
generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string, callback: VideoMethods.GenerateThumbnailFromDataCallback) {
|
generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string) {
|
||||||
// Creating the thumbnail for a remote video
|
// Creating the thumbnail for a remote video
|
||||||
|
|
||||||
const thumbnailName = video.getThumbnailName()
|
const thumbnailName = video.getThumbnailName()
|
||||||
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
|
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
|
||||||
fs.writeFile(thumbnailPath, Buffer.from(thumbnailData, 'binary'), function (err) {
|
return writeFilePromise(thumbnailPath, Buffer.from(thumbnailData, 'binary')).then(() => {
|
||||||
if (err) return callback(err)
|
return thumbnailName
|
||||||
|
|
||||||
return callback(null, thumbnailName)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getDurationFromFile = function (videoPath: string, callback: VideoMethods.GetDurationFromFileCallback) {
|
getDurationFromFile = function (videoPath: string) {
|
||||||
|
return new Promise<number>((res, rej) => {
|
||||||
ffmpeg.ffprobe(videoPath, function (err, metadata) {
|
ffmpeg.ffprobe(videoPath, function (err, metadata) {
|
||||||
if (err) return callback(err)
|
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) {
|
list = function () {
|
||||||
return Video.findAll().asCallback(callback)
|
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
|
// Exclude Blakclisted videos from the list
|
||||||
const query = {
|
const query = {
|
||||||
distinct: true,
|
distinct: true,
|
||||||
|
@ -652,14 +607,15 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
|
||||||
where: createBaseVideosWhere()
|
where: createBaseVideosWhere()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Video.findAndCountAll(query).asCallback(function (err, result) {
|
return Video.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
if (err) return callback(err)
|
return {
|
||||||
|
data: rows,
|
||||||
return callback(null, result.rows, result.count)
|
total: count
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback: VideoMethods.LoadByHostAndRemoteIdCallback) {
|
loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
remoteId: remoteId
|
remoteId: remoteId
|
||||||
|
@ -680,10 +636,10 @@ loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// If remoteId is null this is *our* video
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
|
@ -692,10 +648,10 @@ listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAn
|
||||||
include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
|
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 = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
remoteId: null
|
remoteId: null
|
||||||
|
@ -710,22 +666,22 @@ listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedBy
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Video.findAll(query).asCallback(callback)
|
return Video.findAll(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
load = function (id: string, callback: VideoMethods.LoadCallback) {
|
load = function (id: string) {
|
||||||
return Video.findById(id).asCallback(callback)
|
return Video.findById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAndPopulateAuthor = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorCallback) {
|
loadAndPopulateAuthor = function (id: string) {
|
||||||
const options = {
|
const options = {
|
||||||
include: [ Video['sequelize'].models.Author ]
|
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 = {
|
const options = {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -736,17 +692,10 @@ loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethod
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Video.findById(id, options).asCallback(callback)
|
return Video.findById(id, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
searchAndPopulateAuthorAndPodAndTags = function (
|
searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
|
||||||
value: string,
|
|
||||||
field: string,
|
|
||||||
start: number,
|
|
||||||
count: number,
|
|
||||||
sort: string,
|
|
||||||
callback: VideoMethods.SearchAndPopulateAuthorAndPodAndTagsCallback
|
|
||||||
) {
|
|
||||||
const podInclude: any = {
|
const podInclude: any = {
|
||||||
model: Video['sequelize'].models.Pod,
|
model: Video['sequelize'].models.Pod,
|
||||||
required: false
|
required: false
|
||||||
|
@ -778,7 +727,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
|
||||||
} else if (field === 'tags') {
|
} else if (field === 'tags') {
|
||||||
const escapedValue = Video['sequelize'].escape('%' + value + '%')
|
const escapedValue = Video['sequelize'].escape('%' + value + '%')
|
||||||
query.where.id.$in = Video['sequelize'].literal(
|
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') {
|
} else if (field === 'host') {
|
||||||
// FIXME: Include our pod? (not stored in the database)
|
// FIXME: Include our pod? (not stored in the database)
|
||||||
|
@ -810,10 +763,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
|
||||||
// query.include.push([ Video['sequelize'].models.Tag ])
|
// query.include.push([ Video['sequelize'].models.Tag ])
|
||||||
}
|
}
|
||||||
|
|
||||||
return Video.findAndCountAll(query).asCallback(function (err, result) {
|
return Video.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
if (err) return callback(err)
|
return {
|
||||||
|
data: rows,
|
||||||
return callback(null, result.rows, result.count)
|
total: count
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,27 +783,27 @@ function createBaseVideosWhere () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeThumbnail (video: VideoInstance, callback: (err: Error) => void) {
|
function removeThumbnail (video: VideoInstance) {
|
||||||
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName())
|
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())
|
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())
|
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
|
// 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 = {
|
const options = {
|
||||||
announceList: [
|
announceList: [
|
||||||
[ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
|
[ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
|
||||||
|
@ -859,30 +813,27 @@ function createTorrentFromVideo (video: VideoInstance, videoPath: string, callba
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
createTorrent(videoPath, options, function (err, torrent) {
|
return createTorrentPromise(videoPath, options)
|
||||||
if (err) return callback(err)
|
.then(torrent => {
|
||||||
|
|
||||||
const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
|
const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
|
||||||
fs.writeFile(filePath, torrent, function (err) {
|
return writeFilePromise(filePath, torrent).then(() => torrent)
|
||||||
if (err) return callback(err)
|
})
|
||||||
|
.then(torrent => {
|
||||||
const parsedTorrent = parseTorrent(torrent)
|
const parsedTorrent = parseTorrent(torrent)
|
||||||
video.set('infoHash', parsedTorrent.infoHash)
|
video.set('infoHash', parsedTorrent.infoHash)
|
||||||
video.validate().asCallback(callback)
|
return video.validate()
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPreview (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
|
function createPreview (video: VideoInstance, videoPath: string) {
|
||||||
generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null, callback)
|
return generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createThumbnail (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
|
function createThumbnail (video: VideoInstance, videoPath: string) {
|
||||||
generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE, callback)
|
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) {
|
||||||
function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string, callback?: GenerateImageCallback) {
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
filename: imageName,
|
filename: imageName,
|
||||||
count: 1,
|
count: 1,
|
||||||
|
@ -893,29 +844,25 @@ function generateImage (video: VideoInstance, videoPath: string, folder: string,
|
||||||
options.size = size
|
options.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Promise<string>((res, rej) => {
|
||||||
ffmpeg(videoPath)
|
ffmpeg(videoPath)
|
||||||
.on('error', callback)
|
.on('error', rej)
|
||||||
.on('end', function () {
|
.on('end', function () {
|
||||||
callback(null, imageName)
|
return res(imageName)
|
||||||
})
|
})
|
||||||
.thumbnail(options)
|
.thumbnail(options)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFromBlacklist (video: VideoInstance, callback: (err: Error) => void) {
|
function removeFromBlacklist (video: VideoInstance) {
|
||||||
// Find the blacklisted video
|
// Find the blacklisted video
|
||||||
db.BlacklistedVideo.loadByVideoId(video.id, function (err, video) {
|
return db.BlacklistedVideo.loadByVideoId(video.id).then(video => {
|
||||||
// If an error occured, stop here
|
// Not found the video, skip
|
||||||
if (err) {
|
if (!video) {
|
||||||
logger.error('Error when fetching video from blacklist.', { error: err })
|
return null
|
||||||
return callback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found the video, remove it from the blacklist
|
// If we found the video, remove it from the blacklist
|
||||||
if (video) {
|
return video.destroy()
|
||||||
video.destroy().asCallback(callback)
|
|
||||||
} else {
|
|
||||||
// If haven't found it, simply ignore it and do nothing
|
|
||||||
return callback(null)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,23 @@ describe('Test advanced friends', function () {
|
||||||
function (next) {
|
function (next) {
|
||||||
setTimeout(next, 22000)
|
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
|
// Rerun server 4
|
||||||
function (next) {
|
function (next) {
|
||||||
serversUtils.runServer(4, function (server) {
|
serversUtils.runServer(4, function (server) {
|
||||||
|
@ -187,7 +204,7 @@ describe('Test advanced friends', function () {
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// Pod 6 ask pod 1, 2 and 3
|
// Pod 6 asks pod 1, 2 and 3
|
||||||
function (next) {
|
function (next) {
|
||||||
makeFriends(6, next)
|
makeFriends(6, next)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export * from './job.model'
|
export * from './job.model'
|
||||||
export * from './oauth-client-local.model'
|
export * from './oauth-client-local.model'
|
||||||
export * from './pod.model'
|
export * from './pod.model'
|
||||||
|
export * from './result-list.model'
|
||||||
export * from './request-scheduler.model'
|
export * from './request-scheduler.model'
|
||||||
export * from './user-video-rate.model'
|
export * from './user-video-rate.model'
|
||||||
export * from './user.model'
|
export * from './user.model'
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface ResultList<T> {
|
||||||
|
total: number
|
||||||
|
data: T[]
|
||||||
|
}
|
14
yarn.lock
14
yarn.lock
|
@ -84,9 +84,9 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/express" "*"
|
"@types/express" "*"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@^7.0.18":
|
"@types/node@*", "@types/node@^8.0.3":
|
||||||
version "7.0.32"
|
version "8.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.32.tgz#6afe6c66520a4c316623a14aef123908d01b4bba"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.3.tgz#fca61c26f83e5f453166114f57d53a47feb36d45"
|
||||||
|
|
||||||
"@types/request@^0.0.44":
|
"@types/request@^0.0.44":
|
||||||
version "0.0.44"
|
version "0.0.44"
|
||||||
|
@ -453,7 +453,7 @@ bluebird@^2.10.0, bluebird@^2.9.13:
|
||||||
version "2.11.0"
|
version "2.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
|
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"
|
version "3.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
|
||||||
|
|
||||||
|
@ -3909,9 +3909,9 @@ typedarray@^0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
|
|
||||||
typescript@^2.3.4:
|
typescript@^2.4.1:
|
||||||
version "2.4.0"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc"
|
||||||
|
|
||||||
uid-number@^0.0.6, uid-number@~0.0.6:
|
uid-number@^0.0.6, uid-number@~0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
|
|
Loading…
Reference in New Issue