From feb4bdfd9b46e87aadfa7c0d5338cde887d1f58c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 11 Dec 2016 21:50:51 +0100 Subject: First version with PostgreSQL --- server/controllers/api/clients.js | 8 ++-- server/controllers/api/pods.js | 15 +++--- server/controllers/api/remote.js | 95 ++++++++++++++++++++++++++++++++------ server/controllers/api/requests.js | 10 ++-- server/controllers/api/users.js | 28 ++++++----- server/controllers/api/videos.js | 63 ++++++++++++++++--------- server/controllers/client.js | 11 ++--- 7 files changed, 153 insertions(+), 77 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/clients.js b/server/controllers/api/clients.js index 7755f6c2b..cf83cb835 100644 --- a/server/controllers/api/clients.js +++ b/server/controllers/api/clients.js @@ -1,13 +1,11 @@ 'use strict' const express = require('express') -const mongoose = require('mongoose') const constants = require('../../initializers/constants') +const db = require('../../initializers/database') const logger = require('../../helpers/logger') -const Client = mongoose.model('OAuthClient') - const router = express.Router() router.get('/local', getLocalClient) @@ -27,12 +25,12 @@ function getLocalClient (req, res, next) { return res.type('json').status(403).end() } - Client.loadFirstClient(function (err, client) { + db.OAuthClient.loadFirstClient(function (err, client) { if (err) return next(err) if (!client) return next(new Error('No client available.')) res.json({ - client_id: client._id, + client_id: client.clientId, client_secret: client.clientSecret }) }) diff --git a/server/controllers/api/pods.js b/server/controllers/api/pods.js index 7857fcee0..79f3f9d8d 100644 --- a/server/controllers/api/pods.js +++ b/server/controllers/api/pods.js @@ -1,9 +1,9 @@ 'use strict' const express = require('express') -const mongoose = require('mongoose') const waterfall = require('async/waterfall') +const db = require('../../initializers/database') const logger = require('../../helpers/logger') const friends = require('../../lib/friends') const middlewares = require('../../middlewares') @@ -15,7 +15,6 @@ const validators = middlewares.validators.pods const signatureValidator = middlewares.validators.remote.signature const router = express.Router() -const Pod = mongoose.model('Pod') router.get('/', listPods) router.post('/', @@ -53,15 +52,15 @@ function addPods (req, res, next) { waterfall([ function addPod (callback) { - const pod = new Pod(informations) - pod.save(function (err, podCreated) { + const pod = db.Pod.build(informations) + pod.save().asCallback(function (err, podCreated) { // Be sure about the number of parameters for the callback return callback(err, podCreated) }) }, function sendMyVideos (podCreated, callback) { - friends.sendOwnedVideosToPod(podCreated._id) + friends.sendOwnedVideosToPod(podCreated.id) callback(null) }, @@ -84,7 +83,7 @@ function addPods (req, res, next) { } function listPods (req, res, next) { - Pod.list(function (err, podsList) { + db.Pod.list(function (err, podsList) { if (err) return next(err) res.json(getFormatedPods(podsList)) @@ -111,11 +110,11 @@ function removePods (req, res, next) { waterfall([ function loadPod (callback) { - Pod.loadByHost(host, callback) + db.Pod.loadByHost(host, callback) }, function removePod (pod, callback) { - pod.remove(callback) + pod.destroy().asCallback(callback) } ], function (err) { if (err) return next(err) diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index f1046c534..d856576a9 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -3,15 +3,15 @@ const each = require('async/each') const eachSeries = require('async/eachSeries') const express = require('express') -const mongoose = require('mongoose') +const waterfall = require('async/waterfall') +const db = require('../../initializers/database') const middlewares = require('../../middlewares') const secureMiddleware = middlewares.secure const validators = middlewares.validators.remote const logger = require('../../helpers/logger') const router = express.Router() -const Video = mongoose.model('Video') router.post('/videos', validators.signature, @@ -53,34 +53,99 @@ function remoteVideos (req, res, next) { function addRemoteVideo (videoToCreateData, fromHost, callback) { logger.debug('Adding remote video "%s".', videoToCreateData.name) - const video = new Video(videoToCreateData) - video.podHost = fromHost - Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { - if (err) { - logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) - return callback(err) + waterfall([ + + function findOrCreatePod (callback) { + fromHost + + const query = { + where: { + host: fromHost + }, + defaults: { + host: fromHost + } + } + + db.Pod.findOrCreate(query).asCallback(function (err, result) { + // [ instance, wasCreated ] + return callback(err, result[0]) + }) + }, + + function findOrCreateAuthor (pod, callback) { + const username = videoToCreateData.author + + const query = { + where: { + name: username, + podId: pod.id + }, + defaults: { + name: username, + podId: pod.id + } + } + + db.Author.findOrCreate(query).asCallback(function (err, result) { + // [ instance, wasCreated ] + return callback(err, result[0]) + }) + }, + + function createVideoObject (author, callback) { + const videoData = { + name: videoToCreateData.name, + remoteId: videoToCreateData.remoteId, + extname: videoToCreateData.extname, + infoHash: videoToCreateData.infoHash, + description: videoToCreateData.description, + authorId: author.id, + duration: videoToCreateData.duration, + tags: videoToCreateData.tags + } + + const video = db.Video.build(videoData) + + return callback(null, video) + }, + + function generateThumbnail (video, callback) { + db.Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { + if (err) { + logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) + return callback(err) + } + + video.save().asCallback(callback) + }) + }, + + function insertIntoDB (video, callback) { + video.save().asCallback(callback) } - video.save(callback) - }) + ], callback) } function removeRemoteVideo (videoToRemoveData, fromHost, callback) { + // TODO: use bulkDestroy? + // We need the list because we have to remove some other stuffs (thumbnail etc) - Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { + db.Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { if (err) { - logger.error('Cannot list videos from host and magnets.', { error: err }) + logger.error('Cannot list videos from host and remote id.', { error: err.message }) return callback(err) } if (videosList.length === 0) { - logger.error('No remote video was found for this pod.', { magnetUri: videoToRemoveData.magnetUri, podHost: fromHost }) + logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromHost }) } each(videosList, function (video, callbackEach) { - logger.debug('Removing remote video %s.', video.magnetUri) + logger.debug('Removing remote video %s.', video.remoteId) - video.remove(callbackEach) + video.destroy().asCallback(callbackEach) }, callback) }) } diff --git a/server/controllers/api/requests.js b/server/controllers/api/requests.js index 52aad6997..1f9193fc8 100644 --- a/server/controllers/api/requests.js +++ b/server/controllers/api/requests.js @@ -1,15 +1,13 @@ 'use strict' const express = require('express') -const mongoose = require('mongoose') const constants = require('../../initializers/constants') +const db = require('../../initializers/database') const middlewares = require('../../middlewares') const admin = middlewares.admin const oAuth = middlewares.oauth -const Request = mongoose.model('Request') - const router = express.Router() router.get('/stats', @@ -25,13 +23,13 @@ module.exports = router // --------------------------------------------------------------------------- function getStatsRequests (req, res, next) { - Request.list(function (err, requests) { + db.Request.countTotalRequests(function (err, totalRequests) { if (err) return next(err) return res.json({ - requests: requests, + totalRequests: totalRequests, maxRequestsInParallel: constants.REQUESTS_IN_PARALLEL, - remainingMilliSeconds: Request.remainingMilliSeconds(), + remainingMilliSeconds: db.Request.remainingMilliSeconds(), milliSecondsInterval: constants.REQUESTS_INTERVAL }) }) diff --git a/server/controllers/api/users.js b/server/controllers/api/users.js index b4d687312..890028b36 100644 --- a/server/controllers/api/users.js +++ b/server/controllers/api/users.js @@ -2,10 +2,10 @@ const each = require('async/each') const express = require('express') -const mongoose = require('mongoose') const waterfall = require('async/waterfall') const constants = require('../../initializers/constants') +const db = require('../../initializers/database') const friends = require('../../lib/friends') const logger = require('../../helpers/logger') const middlewares = require('../../middlewares') @@ -17,9 +17,6 @@ const validatorsPagination = middlewares.validators.pagination const validatorsSort = middlewares.validators.sort const validatorsUsers = middlewares.validators.users -const User = mongoose.model('User') -const Video = mongoose.model('Video') - const router = express.Router() router.get('/me', oAuth.authenticate, getUserInformation) @@ -62,13 +59,13 @@ module.exports = router // --------------------------------------------------------------------------- function createUser (req, res, next) { - const user = new User({ + const user = db.User.build({ username: req.body.username, password: req.body.password, role: constants.USER_ROLES.USER }) - user.save(function (err, createdUser) { + user.save().asCallback(function (err, createdUser) { if (err) return next(err) return res.type('json').status(204).end() @@ -76,7 +73,7 @@ function createUser (req, res, next) { } function getUserInformation (req, res, next) { - User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { + db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { if (err) return next(err) return res.json(user.toFormatedJSON()) @@ -84,7 +81,7 @@ function getUserInformation (req, res, next) { } function listUsers (req, res, next) { - 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, function (err, usersList, usersTotal) { if (err) return next(err) res.json(getFormatedUsers(usersList, usersTotal)) @@ -94,18 +91,19 @@ function listUsers (req, res, next) { function removeUser (req, res, next) { waterfall([ function getUser (callback) { - User.loadById(req.params.id, callback) + db.User.loadById(req.params.id, callback) }, + // TODO: use foreignkey? function getVideos (user, callback) { - Video.listOwnedByAuthor(user.username, function (err, videos) { + db.Video.listOwnedByAuthor(user.username, function (err, videos) { return callback(err, user, videos) }) }, function removeVideosFromDB (user, videos, callback) { each(videos, function (video, callbackEach) { - video.remove(callbackEach) + video.destroy().asCallback(callbackEach) }, function (err) { return callback(err, user, videos) }) @@ -115,7 +113,7 @@ function removeUser (req, res, next) { videos.forEach(function (video) { const params = { name: video.name, - magnetUri: video.magnetUri + remoteId: video.id } friends.removeVideoToFriends(params) @@ -125,7 +123,7 @@ function removeUser (req, res, next) { }, function removeUserFromDB (user, callback) { - user.remove(callback) + user.destroy().asCallback(callback) } ], function andFinally (err) { if (err) { @@ -138,11 +136,11 @@ function removeUser (req, res, next) { } function updateUser (req, res, next) { - User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { + db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { if (err) return next(err) user.password = req.body.password - user.save(function (err) { + user.save().asCallback(function (err) { if (err) return next(err) return res.sendStatus(204) diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index daf452573..a61f2b2c9 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -2,12 +2,12 @@ const express = require('express') const fs = require('fs') -const mongoose = require('mongoose') const multer = require('multer') const path = require('path') const waterfall = require('async/waterfall') const constants = require('../../initializers/constants') +const db = require('../../initializers/database') const logger = require('../../helpers/logger') const friends = require('../../lib/friends') const middlewares = require('../../middlewares') @@ -22,7 +22,6 @@ const sort = middlewares.sort const utils = require('../../helpers/utils') const router = express.Router() -const Video = mongoose.model('Video') // multer configuration const storage = multer.diskStorage({ @@ -87,40 +86,60 @@ function addVideo (req, res, next) { const videoInfos = req.body waterfall([ - function createVideoObject (callback) { - const id = mongoose.Types.ObjectId() + function findOrCreateAuthor (callback) { + const username = res.locals.oauth.token.user.username + + const query = { + where: { + name: username, + podId: null + }, + defaults: { + name: username, + podId: null // null because it is OUR pod + } + } + + db.Author.findOrCreate(query).asCallback(function (err, result) { + // [ instance, wasCreated ] + return callback(err, result[0]) + }) + }, + + function createVideoObject (author, callback) { const videoData = { - _id: id, name: videoInfos.name, remoteId: null, extname: path.extname(videoFile.filename), description: videoInfos.description, - author: res.locals.oauth.token.user.username, duration: videoFile.duration, - tags: videoInfos.tags + tags: videoInfos.tags, + authorId: author.id } - const video = new Video(videoData) + const video = db.Video.build(videoData) - return callback(null, video) + return callback(null, author, video) }, - // Set the videoname the same as the MongoDB id - function renameVideoFile (video, callback) { + // Set the videoname the same as the id + function renameVideoFile (author, video, callback) { const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR const source = path.join(videoDir, videoFile.filename) const destination = path.join(videoDir, video.getVideoFilename()) fs.rename(source, destination, function (err) { - return callback(err, video) + return callback(err, author, video) }) }, - function insertIntoDB (video, callback) { - video.save(function (err, video) { - // Assert there are only one argument sent to the next function (video) - return callback(err, video) + function insertIntoDB (author, video, callback) { + video.save().asCallback(function (err, videoCreated) { + // Do not forget to add Author informations to the created video + videoCreated.Author = author + + return callback(err, videoCreated) }) }, @@ -147,7 +166,7 @@ function addVideo (req, res, next) { } function getVideo (req, res, next) { - Video.load(req.params.id, function (err, video) { + db.Video.loadAndPopulateAuthorAndPod(req.params.id, function (err, video) { if (err) return next(err) if (!video) { @@ -159,7 +178,7 @@ function getVideo (req, res, next) { } function listVideos (req, res, next) { - 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, function (err, videosList, videosTotal) { if (err) return next(err) res.json(getFormatedVideos(videosList, videosTotal)) @@ -171,11 +190,11 @@ function removeVideo (req, res, next) { waterfall([ function getVideo (callback) { - Video.load(videoId, callback) + db.Video.load(videoId, callback) }, function removeFromDB (video, callback) { - video.remove(function (err) { + video.destroy().asCallback(function (err) { if (err) return callback(err) return callback(null, video) @@ -185,7 +204,7 @@ function removeVideo (req, res, next) { function sendInformationToFriends (video, callback) { const params = { name: video.name, - remoteId: video._id + remoteId: video.id } friends.removeVideoToFriends(params) @@ -203,7 +222,7 @@ function removeVideo (req, res, next) { } function searchVideos (req, res, next) { - Video.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, + db.Video.searchAndPopulateAuthorAndPod(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { if (err) return next(err) diff --git a/server/controllers/client.js b/server/controllers/client.js index 572db6133..a5fac5626 100644 --- a/server/controllers/client.js +++ b/server/controllers/client.js @@ -3,13 +3,12 @@ const parallel = require('async/parallel') const express = require('express') const fs = require('fs') -const mongoose = require('mongoose') const path = require('path') const validator = require('express-validator').validator const constants = require('../initializers/constants') +const db = require('../initializers/database') -const Video = mongoose.model('Video') const router = express.Router() const opengraphComment = '' @@ -45,14 +44,14 @@ function addOpenGraphTags (htmlStringPage, video) { if (video.isOwned()) { basePreviewUrlHttp = constants.CONFIG.WEBSERVER.URL } else { - basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.podHost + basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.Author.Pod.host } // We fetch the remote preview (bigger than the thumbnail) // This should not overhead the remote server since social websites put in a cache the OpenGraph tags // We can't use the thumbnail because these social websites want bigger images (> 200x200 for Facebook for example) const previewUrl = basePreviewUrlHttp + constants.STATIC_PATHS.PREVIEWS + video.getPreviewName() - const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video._id + const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video.id const metaTags = { 'og:type': 'video', @@ -86,7 +85,7 @@ function generateWatchHtmlPage (req, res, next) { const videoId = req.params.id // Let Angular application handle errors - if (!validator.isMongoId(videoId)) return res.sendFile(indexPath) + if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath) parallel({ file: function (callback) { @@ -94,7 +93,7 @@ function generateWatchHtmlPage (req, res, next) { }, video: function (callback) { - Video.load(videoId, callback) + db.Video.loadAndPopulateAuthorAndPod(videoId, callback) } }, function (err, results) { if (err) return next(err) -- cgit v1.2.3 From 3897209f46f4c4581be2b8963bf9acc28ca5032b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 19 Dec 2016 21:50:20 +0100 Subject: Server: rename Pods -> Pod --- server/controllers/api/remote.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index d856576a9..a6753a2b0 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -56,8 +56,6 @@ function addRemoteVideo (videoToCreateData, fromHost, callback) { waterfall([ function findOrCreatePod (callback) { - fromHost - const query = { where: { host: fromHost -- cgit v1.2.3 From 7920c273a204e2469416a30b752b12ccd3160102 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sat, 24 Dec 2016 16:59:17 +0100 Subject: Move tags in another table --- server/controllers/api/remote.js | 95 +++++++++++++++++++++++++++++++------- server/controllers/api/videos.js | 99 ++++++++++++++++++++++++++++++++-------- server/controllers/client.js | 2 +- 3 files changed, 159 insertions(+), 37 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index a6753a2b0..c7a5658e8 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -50,28 +50,35 @@ function remoteVideos (req, res, next) { return res.type('json').status(204).end() } -function addRemoteVideo (videoToCreateData, fromHost, callback) { +function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { logger.debug('Adding remote video "%s".', videoToCreateData.name) waterfall([ - function findOrCreatePod (callback) { + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findOrCreatePod (t, callback) { const query = { where: { host: fromHost }, defaults: { host: fromHost - } + }, + transaction: t } db.Pod.findOrCreate(query).asCallback(function (err, result) { // [ instance, wasCreated ] - return callback(err, result[0]) + return callback(err, t, result[0]) }) }, - function findOrCreateAuthor (pod, callback) { + function findOrCreateAuthor (t, pod, callback) { const username = videoToCreateData.author const query = { @@ -82,16 +89,45 @@ function addRemoteVideo (videoToCreateData, fromHost, callback) { defaults: { name: username, podId: pod.id - } + }, + transaction: t } db.Author.findOrCreate(query).asCallback(function (err, result) { // [ instance, wasCreated ] - return callback(err, result[0]) + return callback(err, t, result[0]) }) }, - function createVideoObject (author, callback) { + function findOrCreateTags (t, author, callback) { + const tags = videoToCreateData.tags + const tagInstances = [] + + each(tags, function (tag, callbackEach) { + const query = { + where: { + name: tag + }, + defaults: { + name: tag + }, + transaction: t + } + + db.Tag.findOrCreate(query).asCallback(function (err, res) { + if (err) return callbackEach(err) + + // res = [ tag, isCreated ] + const tag = res[0] + tagInstances.push(tag) + return callbackEach() + }) + }, function (err) { + return callback(err, t, author, tagInstances) + }) + }, + + function createVideoObject (t, author, tagInstances, callback) { const videoData = { name: videoToCreateData.name, remoteId: videoToCreateData.remoteId, @@ -99,31 +135,58 @@ function addRemoteVideo (videoToCreateData, fromHost, callback) { infoHash: videoToCreateData.infoHash, description: videoToCreateData.description, authorId: author.id, - duration: videoToCreateData.duration, - tags: videoToCreateData.tags + duration: videoToCreateData.duration } const video = db.Video.build(videoData) - return callback(null, video) + return callback(null, t, tagInstances, video) }, - function generateThumbnail (video, callback) { + function generateThumbnail (t, tagInstances, video, callback) { db.Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { if (err) { logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) return callback(err) } - video.save().asCallback(callback) + return callback(err, t, tagInstances, video) }) }, - function insertIntoDB (video, callback) { - video.save().asCallback(callback) + function insertVideoIntoDB (t, tagInstances, video, callback) { + const options = { + transaction: t + } + + video.save(options).asCallback(function (err, videoCreated) { + return callback(err, t, tagInstances, videoCreated) + }) + }, + + function associateTagsToVideo (t, tagInstances, video, callback) { + const options = { transaction: t } + + video.setTags(tagInstances, options).asCallback(function (err) { + return callback(err, t) + }) } - ], callback) + ], function (err, t) { + if (err) { + logger.error('Cannot insert the remote video.') + + // Abort transaction? + if (t) t.rollback() + + return finalCallback(err) + } + + // Commit transaction + t.commit() + + return finalCallback() + }) } function removeRemoteVideo (videoToRemoveData, fromHost, callback) { diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index a61f2b2c9..992f03db0 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -1,5 +1,6 @@ 'use strict' +const each = require('async/each') const express = require('express') const fs = require('fs') const multer = require('multer') @@ -87,7 +88,13 @@ function addVideo (req, res, next) { waterfall([ - function findOrCreateAuthor (callback) { + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findOrCreateAuthor (t, callback) { const username = res.locals.oauth.token.user.username const query = { @@ -98,75 +105,125 @@ function addVideo (req, res, next) { defaults: { name: username, podId: null // null because it is OUR pod - } + }, + transaction: t } db.Author.findOrCreate(query).asCallback(function (err, result) { // [ instance, wasCreated ] - return callback(err, result[0]) + return callback(err, t, result[0]) + }) + }, + + function findOrCreateTags (t, author, callback) { + const tags = videoInfos.tags + const tagInstances = [] + + each(tags, function (tag, callbackEach) { + const query = { + where: { + name: tag + }, + defaults: { + name: tag + }, + transaction: t + } + + db.Tag.findOrCreate(query).asCallback(function (err, res) { + if (err) return callbackEach(err) + + // res = [ tag, isCreated ] + const tag = res[0] + tagInstances.push(tag) + return callbackEach() + }) + }, function (err) { + return callback(err, t, author, tagInstances) }) }, - function createVideoObject (author, callback) { + function createVideoObject (t, author, tagInstances, callback) { const videoData = { name: videoInfos.name, remoteId: null, extname: path.extname(videoFile.filename), description: videoInfos.description, duration: videoFile.duration, - tags: videoInfos.tags, authorId: author.id } const video = db.Video.build(videoData) - return callback(null, author, video) + return callback(null, t, author, tagInstances, video) }, // Set the videoname the same as the id - function renameVideoFile (author, video, callback) { + function renameVideoFile (t, author, tagInstances, video, callback) { const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR const source = path.join(videoDir, videoFile.filename) const destination = path.join(videoDir, video.getVideoFilename()) fs.rename(source, destination, function (err) { - return callback(err, author, video) + return callback(err, t, author, tagInstances, video) }) }, - function insertIntoDB (author, video, callback) { - video.save().asCallback(function (err, videoCreated) { + function insertVideoIntoDB (t, author, tagInstances, video, callback) { + const options = { transaction: t } + + // Add tags association + video.save(options).asCallback(function (err, videoCreated) { + if (err) return callback(err) + // Do not forget to add Author informations to the created video videoCreated.Author = author - return callback(err, videoCreated) + return callback(err, t, tagInstances, videoCreated) }) }, - function sendToFriends (video, callback) { + function associateTagsToVideo (t, tagInstances, video, callback) { + const options = { transaction: t } + + video.setTags(tagInstances, options).asCallback(function (err) { + video.Tags = tagInstances + + return callback(err, t, video) + }) + }, + + function sendToFriends (t, video, callback) { video.toRemoteJSON(function (err, remoteVideo) { if (err) return callback(err) // Now we'll add the video's meta data to our friends friends.addVideoToFriends(remoteVideo) - return callback(null) + return callback(null, t) }) } - ], function andFinally (err) { + ], function andFinally (err, t) { if (err) { logger.error('Cannot insert the video.') + + // Abort transaction? + if (t) t.rollback() + return next(err) } + // Commit transaction + t.commit() + // TODO : include Location of the new video -> 201 return res.type('json').status(204).end() }) } function getVideo (req, res, next) { - db.Video.loadAndPopulateAuthorAndPod(req.params.id, function (err, video) { + db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) { if (err) return next(err) if (!video) { @@ -222,12 +279,14 @@ function removeVideo (req, res, next) { } function searchVideos (req, res, next) { - db.Video.searchAndPopulateAuthorAndPod(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, - function (err, videosList, videosTotal) { - if (err) return next(err) + db.Video.searchAndPopulateAuthorAndPodAndTags( + req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, + function (err, videosList, videosTotal) { + if (err) return next(err) - res.json(getFormatedVideos(videosList, videosTotal)) - }) + res.json(getFormatedVideos(videosList, videosTotal)) + } + ) } // --------------------------------------------------------------------------- diff --git a/server/controllers/client.js b/server/controllers/client.js index a5fac5626..8c242af07 100644 --- a/server/controllers/client.js +++ b/server/controllers/client.js @@ -93,7 +93,7 @@ function generateWatchHtmlPage (req, res, next) { }, video: function (callback) { - db.Video.loadAndPopulateAuthorAndPod(videoId, callback) + db.Video.loadAndPopulateAuthorAndPodAndTags(videoId, callback) } }, function (err, results) { if (err) return next(err) -- cgit v1.2.3 From 124648d7fcfc3c9a91fe702cbc40c317429c05bd Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 25 Dec 2016 12:06:08 +0100 Subject: Server: add createdAt from remote video in database --- server/controllers/api/remote.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index c7a5658e8..2cf916ff3 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -135,7 +135,8 @@ function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { infoHash: videoToCreateData.infoHash, description: videoToCreateData.description, authorId: author.id, - duration: videoToCreateData.duration + duration: videoToCreateData.duration, + createdAt: videoToCreateData.createdAt } const video = db.Video.build(videoData) -- cgit v1.2.3 From 4712081f2a5f48749cf125d729e78b926ab28d6d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 29 Dec 2016 10:33:36 +0100 Subject: Server: add association between author and user --- server/controllers/api/remote.js | 6 ++++-- server/controllers/api/videos.js | 17 ++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index 2cf916ff3..94d6e740e 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -84,11 +84,13 @@ function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { const query = { where: { name: username, - podId: pod.id + podId: pod.id, + userId: null }, defaults: { name: username, - podId: pod.id + podId: pod.id, + userId: null }, transaction: t } diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 992f03db0..170224634 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -95,23 +95,26 @@ function addVideo (req, res, next) { }, function findOrCreateAuthor (t, callback) { - const username = res.locals.oauth.token.user.username + const user = res.locals.oauth.token.User const query = { where: { - name: username, - podId: null + name: user.username, + podId: null, + userId: user.id }, defaults: { - name: username, - podId: null // null because it is OUR pod + name: user.username, + podId: null, // null because it is OUR pod + userId: user.id }, transaction: t } db.Author.findOrCreate(query).asCallback(function (err, result) { - // [ instance, wasCreated ] - return callback(err, t, result[0]) + const authorInstance = result[0] + + return callback(err, t, authorInstance) }) }, -- cgit v1.2.3 From 98ac898a03ed7bbb4edec74fe823b3f2d6d4904a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 29 Dec 2016 11:17:11 +0100 Subject: Server: use video hook to send information to other pods when a video is deleted --- server/controllers/api/pods.js | 2 +- server/controllers/api/users.js | 32 ++------------------------------ server/controllers/api/videos.js | 24 ++++++------------------ 3 files changed, 9 insertions(+), 49 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/pods.js b/server/controllers/api/pods.js index 79f3f9d8d..d9279f1d9 100644 --- a/server/controllers/api/pods.js +++ b/server/controllers/api/pods.js @@ -113,7 +113,7 @@ function removePods (req, res, next) { db.Pod.loadByHost(host, callback) }, - function removePod (pod, callback) { + function deletePod (pod, callback) { pod.destroy().asCallback(callback) } ], function (err) { diff --git a/server/controllers/api/users.js b/server/controllers/api/users.js index 890028b36..e4423680c 100644 --- a/server/controllers/api/users.js +++ b/server/controllers/api/users.js @@ -90,39 +90,11 @@ function listUsers (req, res, next) { function removeUser (req, res, next) { waterfall([ - function getUser (callback) { + function loadUser (callback) { db.User.loadById(req.params.id, callback) }, - // TODO: use foreignkey? - function getVideos (user, callback) { - db.Video.listOwnedByAuthor(user.username, function (err, videos) { - return callback(err, user, videos) - }) - }, - - function removeVideosFromDB (user, videos, callback) { - each(videos, function (video, callbackEach) { - video.destroy().asCallback(callbackEach) - }, function (err) { - return callback(err, user, videos) - }) - }, - - function sendInformationToFriends (user, videos, callback) { - videos.forEach(function (video) { - const params = { - name: video.name, - remoteId: video.id - } - - friends.removeVideoToFriends(params) - }) - - return callback(null, user) - }, - - function removeUserFromDB (user, callback) { + function deleteUser (user, callback) { user.destroy().asCallback(callback) } ], function andFinally (err) { diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 170224634..ddf85d77d 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -249,27 +249,15 @@ function removeVideo (req, res, next) { const videoId = req.params.id waterfall([ - function getVideo (callback) { - db.Video.load(videoId, callback) - }, - - function removeFromDB (video, callback) { - video.destroy().asCallback(function (err) { - if (err) return callback(err) - - return callback(null, video) + function loadVideo (callback) { + db.Video.load(videoId, function (err, video) { + return callback(err, video) }) }, - function sendInformationToFriends (video, callback) { - const params = { - name: video.name, - remoteId: video.id - } - - friends.removeVideoToFriends(params) - - return callback(null) + function deleteVideo (video, callback) { + // Informations to other pods will be sent by the afterDestroy video hook + video.destroy().asCallback(callback) } ], function andFinally (err) { if (err) { -- cgit v1.2.3 From 4d32448895ad29ef694bcf790d59253249ad5939 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 29 Dec 2016 12:13:19 +0100 Subject: Server: use binary data instead of base64 to send thumbnails --- server/controllers/api/remote.js | 4 ++-- server/controllers/api/users.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index 94d6e740e..ac850c2d2 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -147,9 +147,9 @@ function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { }, function generateThumbnail (t, tagInstances, video, callback) { - db.Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { + db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) { if (err) { - logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) + logger.error('Cannot generate thumbnail from data.', { error: err }) return callback(err) } diff --git a/server/controllers/api/users.js b/server/controllers/api/users.js index e4423680c..53bf56790 100644 --- a/server/controllers/api/users.js +++ b/server/controllers/api/users.js @@ -1,12 +1,10 @@ 'use strict' -const each = require('async/each') const express = require('express') const waterfall = require('async/waterfall') const constants = require('../../initializers/constants') const db = require('../../initializers/database') -const friends = require('../../lib/friends') const logger = require('../../helpers/logger') const middlewares = require('../../middlewares') const admin = middlewares.admin -- cgit v1.2.3 From 4ff0d86208dafbdd07beb6286fd93c795db8a95f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 29 Dec 2016 18:02:03 +0100 Subject: Server: little refractoring --- server/controllers/api/remote.js | 79 ++++++++-------------------------------- server/controllers/api/videos.js | 46 ++++------------------- 2 files changed, 22 insertions(+), 103 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index ac850c2d2..929ade555 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -28,7 +28,7 @@ module.exports = router function remoteVideos (req, res, next) { const requests = req.body.data - const fromHost = req.body.signature.host + const fromPod = res.locals.secure.pod // We need to process in the same order to keep consistency // TODO: optimization @@ -36,9 +36,9 @@ function remoteVideos (req, res, next) { const videoData = request.data if (request.type === 'add') { - addRemoteVideo(videoData, fromHost, callbackEach) + addRemoteVideo(videoData, fromPod, callbackEach) } else if (request.type === 'remove') { - removeRemoteVideo(videoData, fromHost, callbackEach) + removeRemoteVideo(videoData, fromPod, callbackEach) } else { logger.error('Unkown remote request type %s.', request.type) } @@ -50,7 +50,7 @@ function remoteVideos (req, res, next) { return res.type('json').status(204).end() } -function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { +function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { logger.debug('Adding remote video "%s".', videoToCreateData.name) waterfall([ @@ -61,70 +61,21 @@ function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { }) }, - function findOrCreatePod (t, callback) { - const query = { - where: { - host: fromHost - }, - defaults: { - host: fromHost - }, - transaction: t - } - - db.Pod.findOrCreate(query).asCallback(function (err, result) { - // [ instance, wasCreated ] - return callback(err, t, result[0]) - }) - }, + function findOrCreateAuthor (t, callback) { + const name = videoToCreateData.author + const podId = fromPod.id + // This author is from another pod so we do not associate a user + const userId = null - function findOrCreateAuthor (t, pod, callback) { - const username = videoToCreateData.author - - const query = { - where: { - name: username, - podId: pod.id, - userId: null - }, - defaults: { - name: username, - podId: pod.id, - userId: null - }, - transaction: t - } - - db.Author.findOrCreate(query).asCallback(function (err, result) { - // [ instance, wasCreated ] - return callback(err, t, result[0]) + db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { + return callback(err, t, authorInstance) }) }, function findOrCreateTags (t, author, callback) { const tags = videoToCreateData.tags - const tagInstances = [] - - each(tags, function (tag, callbackEach) { - const query = { - where: { - name: tag - }, - defaults: { - name: tag - }, - transaction: t - } - - db.Tag.findOrCreate(query).asCallback(function (err, res) { - if (err) return callbackEach(err) - // res = [ tag, isCreated ] - const tag = res[0] - tagInstances.push(tag) - return callbackEach() - }) - }, function (err) { + db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { return callback(err, t, author, tagInstances) }) }, @@ -192,18 +143,18 @@ function addRemoteVideo (videoToCreateData, fromHost, finalCallback) { }) } -function removeRemoteVideo (videoToRemoveData, fromHost, callback) { +function removeRemoteVideo (videoToRemoveData, fromPod, callback) { // TODO: use bulkDestroy? // We need the list because we have to remove some other stuffs (thumbnail etc) - db.Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { + db.Video.listByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, videosList) { if (err) { logger.error('Cannot list videos from host and remote id.', { error: err.message }) return callback(err) } if (videosList.length === 0) { - logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromHost }) + logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromPod.host }) } each(videosList, function (video, callbackEach) { diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index ddf85d77d..f29edac74 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -1,6 +1,5 @@ 'use strict' -const each = require('async/each') const express = require('express') const fs = require('fs') const multer = require('multer') @@ -97,51 +96,20 @@ function addVideo (req, res, next) { function findOrCreateAuthor (t, callback) { const user = res.locals.oauth.token.User - const query = { - where: { - name: user.username, - podId: null, - userId: user.id - }, - defaults: { - name: user.username, - podId: null, // null because it is OUR pod - userId: user.id - }, - transaction: t - } - - db.Author.findOrCreate(query).asCallback(function (err, result) { - const authorInstance = result[0] + const name = user.username + // null because it is OUR pod + const podId = null + const userId = user.id + db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { return callback(err, t, authorInstance) }) }, function findOrCreateTags (t, author, callback) { const tags = videoInfos.tags - const tagInstances = [] - - each(tags, function (tag, callbackEach) { - const query = { - where: { - name: tag - }, - defaults: { - name: tag - }, - transaction: t - } - - db.Tag.findOrCreate(query).asCallback(function (err, res) { - if (err) return callbackEach(err) - - // res = [ tag, isCreated ] - const tag = res[0] - tagInstances.push(tag) - return callbackEach() - }) - }, function (err) { + + db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { return callback(err, t, author, tagInstances) }) }, -- cgit v1.2.3 From 7b1f49de22c40ae121ddb3c399b2540ba56fd414 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 29 Dec 2016 19:07:05 +0100 Subject: Server: add ability to update a video --- server/controllers/api/videos.js | 85 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index f29edac74..1b306d1cf 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -50,6 +50,12 @@ router.get('/', pagination.setPagination, listVideos ) +router.put('/:id', + oAuth.authenticate, + reqFiles, + validatorsVideos.videosUpdate, + updateVideo +) router.post('/', oAuth.authenticate, reqFiles, @@ -165,7 +171,7 @@ function addVideo (req, res, next) { }, function sendToFriends (t, video, callback) { - video.toRemoteJSON(function (err, remoteVideo) { + video.toAddRemoteJSON(function (err, remoteVideo) { if (err) return callback(err) // Now we'll add the video's meta data to our friends @@ -193,6 +199,83 @@ function addVideo (req, res, next) { }) } +function updateVideo (req, res, next) { + let videoInstance = res.locals.video + const videoInfosToUpdate = req.body + + waterfall([ + + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findOrCreateTags (t, callback) { + if (videoInfosToUpdate.tags) { + db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) { + return callback(err, t, tagInstances) + }) + } else { + return callback(null, t, null) + } + }, + + function updateVideoIntoDB (t, tagInstances, callback) { + const options = { transaction: t } + + if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name) + if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description) + + // Add tags association + videoInstance.save(options).asCallback(function (err) { + if (err) return callback(err) + + return callback(err, t, tagInstances) + }) + }, + + function associateTagsToVideo (t, tagInstances, callback) { + if (tagInstances) { + const options = { transaction: t } + + videoInstance.setTags(tagInstances, options).asCallback(function (err) { + videoInstance.Tags = tagInstances + + return callback(err, t) + }) + } else { + return callback(null, t) + } + }, + + function sendToFriends (t, callback) { + const json = videoInstance.toUpdateRemoteJSON() + + // Now we'll update the video's meta data to our friends + friends.updateVideoToFriends(json) + + return callback(null, t) + } + + ], function andFinally (err, t) { + if (err) { + logger.error('Cannot insert the video.') + + // Abort transaction? + if (t) t.rollback() + + return next(err) + } + + // Commit transaction + t.commit() + + // TODO : include Location of the new video -> 201 + return res.type('json').status(204).end() + }) +} + function getVideo (req, res, next) { db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) { if (err) return next(err) -- cgit v1.2.3 From 3d118fb501f576a298f6bb059167e4c7f4dd8dcc Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Dec 2016 11:27:42 +0100 Subject: Server: propagate video update to other pods --- server/controllers/api/remote.js | 108 ++++++++++++++++++++++++++++++++------- server/controllers/api/videos.js | 2 - 2 files changed, 89 insertions(+), 21 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index 929ade555..254ae56d5 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -35,12 +35,21 @@ function remoteVideos (req, res, next) { eachSeries(requests, function (request, callbackEach) { const videoData = request.data - if (request.type === 'add') { - addRemoteVideo(videoData, fromPod, callbackEach) - } else if (request.type === 'remove') { - removeRemoteVideo(videoData, fromPod, callbackEach) - } else { - logger.error('Unkown remote request type %s.', request.type) + switch (request.type) { + case 'add': + addRemoteVideo(videoData, fromPod, callbackEach) + break + + case 'update': + updateRemoteVideo(videoData, fromPod, callbackEach) + break + + case 'remove': + removeRemoteVideo(videoData, fromPod, callbackEach) + break + + default: + logger.error('Unkown remote request type %s.', request.type) } }, function (err) { if (err) logger.error('Error managing remote videos.', { error: err }) @@ -143,24 +152,85 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { }) } -function removeRemoteVideo (videoToRemoveData, fromPod, callback) { - // TODO: use bulkDestroy? +function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { + logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name) - // We need the list because we have to remove some other stuffs (thumbnail etc) - db.Video.listByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, videosList) { - if (err) { - logger.error('Cannot list videos from host and remote id.', { error: err.message }) - return callback(err) + waterfall([ + + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findVideo (t, callback) { + db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) { + if (err || !videoInstance) { + logger.error('Cannot load video from host and remote id.', { error: err.message }) + return callback(err) + } + + return callback(null, t, videoInstance) + }) + }, + + function findOrCreateTags (t, videoInstance, callback) { + const tags = videoAttributesToUpdate.tags + + db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { + return callback(err, t, videoInstance, tagInstances) + }) + }, + + function updateVideoIntoDB (t, videoInstance, tagInstances, callback) { + const options = { transaction: t } + + videoInstance.set('name', videoAttributesToUpdate.name) + videoInstance.set('description', videoAttributesToUpdate.description) + videoInstance.set('infoHash', videoAttributesToUpdate.infoHash) + videoInstance.set('duration', videoAttributesToUpdate.duration) + videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) + videoInstance.set('extname', videoAttributesToUpdate.extname) + + videoInstance.save(options).asCallback(function (err) { + return callback(err, t, videoInstance, tagInstances) + }) + }, + + function associateTagsToVideo (t, videoInstance, tagInstances, callback) { + const options = { transaction: t } + + videoInstance.setTags(tagInstances, options).asCallback(function (err) { + return callback(err, t) + }) } - if (videosList.length === 0) { - logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromPod.host }) + ], function (err, t) { + if (err) { + logger.error('Cannot update the remote video.') + + // Abort transaction? + if (t) t.rollback() + + return finalCallback(err) } - each(videosList, function (video, callbackEach) { - logger.debug('Removing remote video %s.', video.remoteId) + // Commit transaction + t.commit() + + return finalCallback() + }) +} + +function removeRemoteVideo (videoToRemoveData, fromPod, callback) { + // We need the instance because we have to remove some other stuffs (thumbnail etc) + db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) { + if (err || !video) { + logger.error('Cannot load video from host and remote id.', { error: err.message }) + return callback(err) + } - video.destroy().asCallback(callbackEach) - }, callback) + logger.debug('Removing remote video %s.', video.remoteId) + video.destroy().asCallback(callback) }) } diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 1b306d1cf..e5c52a87b 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -229,8 +229,6 @@ function updateVideo (req, res, next) { // Add tags association videoInstance.save(options).asCallback(function (err) { - if (err) return callback(err) - return callback(err, t, tagInstances) }) }, -- cgit v1.2.3 From 79066fdf33f79d2d41394f10881e2c226ca26b49 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Dec 2016 11:45:00 +0100 Subject: Server: add updatedAt attribute to videos --- server/controllers/api/remote.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index 254ae56d5..a36c31c38 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -98,7 +98,8 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { description: videoToCreateData.description, authorId: author.id, duration: videoToCreateData.duration, - createdAt: videoToCreateData.createdAt + createdAt: videoToCreateData.createdAt, + updatedAt: videoToCreateData.updatedAt } const video = db.Video.build(videoData) @@ -190,6 +191,7 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { videoInstance.set('infoHash', videoAttributesToUpdate.infoHash) videoInstance.set('duration', videoAttributesToUpdate.duration) videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) + videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) videoInstance.set('extname', videoAttributesToUpdate.extname) videoInstance.save(options).asCallback(function (err) { -- cgit v1.2.3 From 818f7987eba27c59793e2103168b26129c9404f2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Dec 2016 11:51:08 +0100 Subject: Server: optimization for videoGet and videoRemove --- server/controllers/api/videos.js | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index e5c52a87b..35d6979e5 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -200,7 +200,7 @@ function addVideo (req, res, next) { } function updateVideo (req, res, next) { - let videoInstance = res.locals.video + const videoInstance = res.locals.video const videoInfosToUpdate = req.body waterfall([ @@ -275,15 +275,8 @@ function updateVideo (req, res, next) { } function getVideo (req, res, next) { - db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) { - if (err) return next(err) - - if (!video) { - return res.type('json').status(204).end() - } - - res.json(video.toFormatedJSON()) - }) + const videoInstance = res.locals.video + res.json(videoInstance.toFormatedJSON()) } function listVideos (req, res, next) { @@ -295,20 +288,9 @@ function listVideos (req, res, next) { } function removeVideo (req, res, next) { - const videoId = req.params.id + const videoInstance = res.locals.video - waterfall([ - function loadVideo (callback) { - db.Video.load(videoId, function (err, video) { - return callback(err, video) - }) - }, - - function deleteVideo (video, callback) { - // Informations to other pods will be sent by the afterDestroy video hook - video.destroy().asCallback(callback) - } - ], function andFinally (err) { + videoInstance.destroy().asCallback(function (err) { if (err) { logger.error('Errors when removed the video.', { error: err }) return next(err) -- cgit v1.2.3 From efe923bcdaf15b47593ad8583df09a92c715ac6c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Dec 2016 12:23:53 +0100 Subject: Server: split check params tests --- server/controllers/api/remote.js | 1 - 1 file changed, 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js index a36c31c38..be5e6dc98 100644 --- a/server/controllers/api/remote.js +++ b/server/controllers/api/remote.js @@ -1,6 +1,5 @@ 'use strict' -const each = require('async/each') const eachSeries = require('async/eachSeries') const express = require('express') const waterfall = require('async/waterfall') -- cgit v1.2.3 From a6fd2b30bf717eec14972a2175354781f5f43e77 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Dec 2016 12:53:41 +0100 Subject: Server: move remote routes in their own directory --- server/controllers/api/index.js | 8 +- server/controllers/api/remote.js | 237 -------------------------------- server/controllers/api/remote/index.js | 16 +++ server/controllers/api/remote/videos.js | 237 ++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+), 242 deletions(-) delete mode 100644 server/controllers/api/remote.js create mode 100644 server/controllers/api/remote/index.js create mode 100644 server/controllers/api/remote/videos.js (limited to 'server/controllers') diff --git a/server/controllers/api/index.js b/server/controllers/api/index.js index 4cb65ed55..f13ff922c 100644 --- a/server/controllers/api/index.js +++ b/server/controllers/api/index.js @@ -2,6 +2,8 @@ const express = require('express') +const utils = require('../../helpers/utils') + const router = express.Router() const clientsController = require('./clients') @@ -18,7 +20,7 @@ router.use('/requests', requestsController) router.use('/users', usersController) router.use('/videos', videosController) router.use('/ping', pong) -router.use('/*', badRequest) +router.use('/*', utils.badRequest) // --------------------------------------------------------------------------- @@ -29,7 +31,3 @@ module.exports = router function pong (req, res, next) { return res.send('pong').status(200).end() } - -function badRequest (req, res, next) { - res.type('json').status(400).end() -} diff --git a/server/controllers/api/remote.js b/server/controllers/api/remote.js deleted file mode 100644 index be5e6dc98..000000000 --- a/server/controllers/api/remote.js +++ /dev/null @@ -1,237 +0,0 @@ -'use strict' - -const eachSeries = require('async/eachSeries') -const express = require('express') -const waterfall = require('async/waterfall') - -const db = require('../../initializers/database') -const middlewares = require('../../middlewares') -const secureMiddleware = middlewares.secure -const validators = middlewares.validators.remote -const logger = require('../../helpers/logger') - -const router = express.Router() - -router.post('/videos', - validators.signature, - secureMiddleware.checkSignature, - validators.remoteVideos, - remoteVideos -) - -// --------------------------------------------------------------------------- - -module.exports = router - -// --------------------------------------------------------------------------- - -function remoteVideos (req, res, next) { - const requests = req.body.data - const fromPod = res.locals.secure.pod - - // We need to process in the same order to keep consistency - // TODO: optimization - eachSeries(requests, function (request, callbackEach) { - const videoData = request.data - - switch (request.type) { - case 'add': - addRemoteVideo(videoData, fromPod, callbackEach) - break - - case 'update': - updateRemoteVideo(videoData, fromPod, callbackEach) - break - - case 'remove': - removeRemoteVideo(videoData, fromPod, callbackEach) - break - - default: - logger.error('Unkown remote request type %s.', request.type) - } - }, function (err) { - if (err) logger.error('Error managing remote videos.', { error: err }) - }) - - // We don't need to keep the other pod waiting - return res.type('json').status(204).end() -} - -function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { - logger.debug('Adding remote video "%s".', videoToCreateData.name) - - waterfall([ - - function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { - return callback(err, t) - }) - }, - - function findOrCreateAuthor (t, callback) { - const name = videoToCreateData.author - const podId = fromPod.id - // This author is from another pod so we do not associate a user - const userId = null - - db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { - return callback(err, t, authorInstance) - }) - }, - - function findOrCreateTags (t, author, callback) { - const tags = videoToCreateData.tags - - db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { - return callback(err, t, author, tagInstances) - }) - }, - - function createVideoObject (t, author, tagInstances, callback) { - const videoData = { - name: videoToCreateData.name, - remoteId: videoToCreateData.remoteId, - extname: videoToCreateData.extname, - infoHash: videoToCreateData.infoHash, - description: videoToCreateData.description, - authorId: author.id, - duration: videoToCreateData.duration, - createdAt: videoToCreateData.createdAt, - updatedAt: videoToCreateData.updatedAt - } - - const video = db.Video.build(videoData) - - return callback(null, t, tagInstances, video) - }, - - function generateThumbnail (t, tagInstances, video, callback) { - db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) { - if (err) { - logger.error('Cannot generate thumbnail from data.', { error: err }) - return callback(err) - } - - return callback(err, t, tagInstances, video) - }) - }, - - function insertVideoIntoDB (t, tagInstances, video, callback) { - const options = { - transaction: t - } - - video.save(options).asCallback(function (err, videoCreated) { - return callback(err, t, tagInstances, videoCreated) - }) - }, - - function associateTagsToVideo (t, tagInstances, video, callback) { - const options = { transaction: t } - - video.setTags(tagInstances, options).asCallback(function (err) { - return callback(err, t) - }) - } - - ], function (err, t) { - if (err) { - logger.error('Cannot insert the remote video.') - - // Abort transaction? - if (t) t.rollback() - - return finalCallback(err) - } - - // Commit transaction - t.commit() - - return finalCallback() - }) -} - -function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { - logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name) - - waterfall([ - - function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { - return callback(err, t) - }) - }, - - function findVideo (t, callback) { - db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) { - if (err || !videoInstance) { - logger.error('Cannot load video from host and remote id.', { error: err.message }) - return callback(err) - } - - return callback(null, t, videoInstance) - }) - }, - - function findOrCreateTags (t, videoInstance, callback) { - const tags = videoAttributesToUpdate.tags - - db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { - return callback(err, t, videoInstance, tagInstances) - }) - }, - - function updateVideoIntoDB (t, videoInstance, tagInstances, callback) { - const options = { transaction: t } - - videoInstance.set('name', videoAttributesToUpdate.name) - videoInstance.set('description', videoAttributesToUpdate.description) - videoInstance.set('infoHash', videoAttributesToUpdate.infoHash) - videoInstance.set('duration', videoAttributesToUpdate.duration) - videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) - videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) - videoInstance.set('extname', videoAttributesToUpdate.extname) - - videoInstance.save(options).asCallback(function (err) { - return callback(err, t, videoInstance, tagInstances) - }) - }, - - function associateTagsToVideo (t, videoInstance, tagInstances, callback) { - const options = { transaction: t } - - videoInstance.setTags(tagInstances, options).asCallback(function (err) { - return callback(err, t) - }) - } - - ], function (err, t) { - if (err) { - logger.error('Cannot update the remote video.') - - // Abort transaction? - if (t) t.rollback() - - return finalCallback(err) - } - - // Commit transaction - t.commit() - - return finalCallback() - }) -} - -function removeRemoteVideo (videoToRemoveData, fromPod, callback) { - // We need the instance because we have to remove some other stuffs (thumbnail etc) - db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) { - if (err || !video) { - logger.error('Cannot load video from host and remote id.', { error: err.message }) - return callback(err) - } - - logger.debug('Removing remote video %s.', video.remoteId) - video.destroy().asCallback(callback) - }) -} diff --git a/server/controllers/api/remote/index.js b/server/controllers/api/remote/index.js new file mode 100644 index 000000000..2947632d5 --- /dev/null +++ b/server/controllers/api/remote/index.js @@ -0,0 +1,16 @@ +'use strict' + +const express = require('express') + +const utils = require('../../../helpers/utils') + +const router = express.Router() + +const videosRemoteController = require('./videos') + +router.use('/videos', videosRemoteController) +router.use('/*', utils.badRequest) + +// --------------------------------------------------------------------------- + +module.exports = router diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js new file mode 100644 index 000000000..87c49bff9 --- /dev/null +++ b/server/controllers/api/remote/videos.js @@ -0,0 +1,237 @@ +'use strict' + +const eachSeries = require('async/eachSeries') +const express = require('express') +const waterfall = require('async/waterfall') + +const db = require('../../../initializers/database') +const middlewares = require('../../../middlewares') +const secureMiddleware = middlewares.secure +const validators = middlewares.validators.remote +const logger = require('../../../helpers/logger') + +const router = express.Router() + +router.post('/', + validators.signature, + secureMiddleware.checkSignature, + validators.remoteVideos, + remoteVideos +) + +// --------------------------------------------------------------------------- + +module.exports = router + +// --------------------------------------------------------------------------- + +function remoteVideos (req, res, next) { + const requests = req.body.data + const fromPod = res.locals.secure.pod + + // We need to process in the same order to keep consistency + // TODO: optimization + eachSeries(requests, function (request, callbackEach) { + const videoData = request.data + + switch (request.type) { + case 'add': + addRemoteVideo(videoData, fromPod, callbackEach) + break + + case 'update': + updateRemoteVideo(videoData, fromPod, callbackEach) + break + + case 'remove': + removeRemoteVideo(videoData, fromPod, callbackEach) + break + + default: + logger.error('Unkown remote request type %s.', request.type) + } + }, function (err) { + if (err) logger.error('Error managing remote videos.', { error: err }) + }) + + // We don't need to keep the other pod waiting + return res.type('json').status(204).end() +} + +function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { + logger.debug('Adding remote video "%s".', videoToCreateData.name) + + waterfall([ + + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findOrCreateAuthor (t, callback) { + const name = videoToCreateData.author + const podId = fromPod.id + // This author is from another pod so we do not associate a user + const userId = null + + db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { + return callback(err, t, authorInstance) + }) + }, + + function findOrCreateTags (t, author, callback) { + const tags = videoToCreateData.tags + + db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { + return callback(err, t, author, tagInstances) + }) + }, + + function createVideoObject (t, author, tagInstances, callback) { + const videoData = { + name: videoToCreateData.name, + remoteId: videoToCreateData.remoteId, + extname: videoToCreateData.extname, + infoHash: videoToCreateData.infoHash, + description: videoToCreateData.description, + authorId: author.id, + duration: videoToCreateData.duration, + createdAt: videoToCreateData.createdAt, + updatedAt: videoToCreateData.updatedAt + } + + const video = db.Video.build(videoData) + + return callback(null, t, tagInstances, video) + }, + + function generateThumbnail (t, tagInstances, video, callback) { + db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) { + if (err) { + logger.error('Cannot generate thumbnail from data.', { error: err }) + return callback(err) + } + + return callback(err, t, tagInstances, video) + }) + }, + + function insertVideoIntoDB (t, tagInstances, video, callback) { + const options = { + transaction: t + } + + video.save(options).asCallback(function (err, videoCreated) { + return callback(err, t, tagInstances, videoCreated) + }) + }, + + function associateTagsToVideo (t, tagInstances, video, callback) { + const options = { transaction: t } + + video.setTags(tagInstances, options).asCallback(function (err) { + return callback(err, t) + }) + } + + ], function (err, t) { + if (err) { + logger.error('Cannot insert the remote video.') + + // Abort transaction? + if (t) t.rollback() + + return finalCallback(err) + } + + // Commit transaction + t.commit() + + return finalCallback() + }) +} + +function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { + logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name) + + waterfall([ + + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function findVideo (t, callback) { + db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) { + if (err || !videoInstance) { + logger.error('Cannot load video from host and remote id.', { error: err.message }) + return callback(err) + } + + return callback(null, t, videoInstance) + }) + }, + + function findOrCreateTags (t, videoInstance, callback) { + const tags = videoAttributesToUpdate.tags + + db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { + return callback(err, t, videoInstance, tagInstances) + }) + }, + + function updateVideoIntoDB (t, videoInstance, tagInstances, callback) { + const options = { transaction: t } + + videoInstance.set('name', videoAttributesToUpdate.name) + videoInstance.set('description', videoAttributesToUpdate.description) + videoInstance.set('infoHash', videoAttributesToUpdate.infoHash) + videoInstance.set('duration', videoAttributesToUpdate.duration) + videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) + videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) + videoInstance.set('extname', videoAttributesToUpdate.extname) + + videoInstance.save(options).asCallback(function (err) { + return callback(err, t, videoInstance, tagInstances) + }) + }, + + function associateTagsToVideo (t, videoInstance, tagInstances, callback) { + const options = { transaction: t } + + videoInstance.setTags(tagInstances, options).asCallback(function (err) { + return callback(err, t) + }) + } + + ], function (err, t) { + if (err) { + logger.error('Cannot update the remote video.') + + // Abort transaction? + if (t) t.rollback() + + return finalCallback(err) + } + + // Commit transaction + t.commit() + + return finalCallback() + }) +} + +function removeRemoteVideo (videoToRemoveData, fromPod, callback) { + // We need the instance because we have to remove some other stuffs (thumbnail etc) + db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) { + if (err || !video) { + logger.error('Cannot load video from host and remote id.', { error: err.message }) + return callback(err) + } + + logger.debug('Removing remote video %s.', video.remoteId) + video.destroy().asCallback(callback) + }) +} -- cgit v1.2.3 From 55fa55a9be566cca2ba95322f2ae23b434aed62a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 4 Jan 2017 20:59:23 +0100 Subject: Server: add video abuse support --- server/controllers/api/pods.js | 17 ++------- server/controllers/api/remote/videos.js | 68 ++++++++++++++++++++++++--------- server/controllers/api/users.js | 18 +-------- server/controllers/api/videos.js | 61 +++++++++++++++++++++++------ 4 files changed, 105 insertions(+), 59 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/pods.js b/server/controllers/api/pods.js index d9279f1d9..38702face 100644 --- a/server/controllers/api/pods.js +++ b/server/controllers/api/pods.js @@ -5,6 +5,7 @@ const waterfall = require('async/waterfall') const db = require('../../initializers/database') const logger = require('../../helpers/logger') +const utils = require('../../helpers/utils') const friends = require('../../lib/friends') const middlewares = require('../../middlewares') const admin = middlewares.admin @@ -36,7 +37,7 @@ router.get('/quitfriends', ) // Post because this is a secured request router.post('/remove', - signatureValidator, + signatureValidator.signature, checkSignature, removePods ) @@ -86,7 +87,7 @@ function listPods (req, res, next) { db.Pod.list(function (err, podsList) { if (err) return next(err) - res.json(getFormatedPods(podsList)) + res.json(utils.getFormatedObjects(podsList, podsList.length)) }) } @@ -130,15 +131,3 @@ function quitFriends (req, res, next) { res.type('json').status(204).end() }) } - -// --------------------------------------------------------------------------- - -function getFormatedPods (pods) { - const formatedPods = [] - - pods.forEach(function (pod) { - formatedPods.push(pod.toFormatedJSON()) - }) - - return formatedPods -} diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index 87c49bff9..d02da4463 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js @@ -7,15 +7,16 @@ const waterfall = require('async/waterfall') const db = require('../../../initializers/database') const middlewares = require('../../../middlewares') const secureMiddleware = middlewares.secure -const validators = middlewares.validators.remote +const videosValidators = middlewares.validators.remote.videos +const signatureValidators = middlewares.validators.remote.signature const logger = require('../../../helpers/logger') const router = express.Router() router.post('/', - validators.signature, + signatureValidators.signature, secureMiddleware.checkSignature, - validators.remoteVideos, + videosValidators.remoteVideos, remoteVideos ) @@ -32,19 +33,23 @@ function remoteVideos (req, res, next) { // We need to process in the same order to keep consistency // TODO: optimization eachSeries(requests, function (request, callbackEach) { - const videoData = request.data + const data = request.data switch (request.type) { case 'add': - addRemoteVideo(videoData, fromPod, callbackEach) + addRemoteVideo(data, fromPod, callbackEach) break case 'update': - updateRemoteVideo(videoData, fromPod, callbackEach) + updateRemoteVideo(data, fromPod, callbackEach) break case 'remove': - removeRemoteVideo(videoData, fromPod, callbackEach) + removeRemoteVideo(data, fromPod, callbackEach) + break + + case 'report-abuse': + reportAbuseRemoteVideo(data, fromPod, callbackEach) break default: @@ -164,13 +169,8 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { }, function findVideo (t, callback) { - db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) { - if (err || !videoInstance) { - logger.error('Cannot load video from host and remote id.', { error: err.message }) - return callback(err) - } - - return callback(null, t, videoInstance) + fetchVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) { + return callback(err, t, videoInstance) }) }, @@ -225,13 +225,45 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { function removeRemoteVideo (videoToRemoveData, fromPod, callback) { // We need the instance because we have to remove some other stuffs (thumbnail etc) - db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) { + fetchVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) { + if (err) return callback(err) + + logger.debug('Removing remote video %s.', video.remoteId) + video.destroy().asCallback(callback) + }) +} + +function reportAbuseRemoteVideo (reportData, fromPod, callback) { + db.Video.load(reportData.videoRemoteId, function (err, video) { if (err || !video) { - logger.error('Cannot load video from host and remote id.', { error: err.message }) + if (!err) err = new Error('video not found') + + logger.error('Cannot load video from host and remote id.', { error: err }) return callback(err) } - logger.debug('Removing remote video %s.', video.remoteId) - video.destroy().asCallback(callback) + logger.debug('Reporting remote abuse for video %s.', video.id) + + const videoAbuseData = { + reporterUsername: reportData.reporterUsername, + reason: reportData.reportReason, + reporterPodId: fromPod.id, + videoId: video.id + } + + db.VideoAbuse.create(videoAbuseData).asCallback(callback) + }) +} + +function fetchVideo (podHost, remoteId, callback) { + db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) { + if (err || !video) { + if (!err) err = new Error('video not found') + + logger.error('Cannot load video from host and remote id.', { error: err }) + return callback(err) + } + + return callback(null, video) }) } diff --git a/server/controllers/api/users.js b/server/controllers/api/users.js index 53bf56790..6cd0e84f7 100644 --- a/server/controllers/api/users.js +++ b/server/controllers/api/users.js @@ -6,6 +6,7 @@ const waterfall = require('async/waterfall') const constants = require('../../initializers/constants') const db = require('../../initializers/database') const logger = require('../../helpers/logger') +const utils = require('../../helpers/utils') const middlewares = require('../../middlewares') const admin = middlewares.admin const oAuth = middlewares.oauth @@ -82,7 +83,7 @@ function listUsers (req, res, next) { db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) { if (err) return next(err) - res.json(getFormatedUsers(usersList, usersTotal)) + res.json(utils.getFormatedObjects(usersList, usersTotal)) }) } @@ -121,18 +122,3 @@ function updateUser (req, res, next) { function success (req, res, next) { res.end() } - -// --------------------------------------------------------------------------- - -function getFormatedUsers (users, usersTotal) { - const formatedUsers = [] - - users.forEach(function (user) { - formatedUsers.push(user.toFormatedJSON()) - }) - - return { - total: usersTotal, - data: formatedUsers - } -} diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 35d6979e5..6829804ec 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -11,6 +11,7 @@ const db = require('../../initializers/database') const logger = require('../../helpers/logger') const friends = require('../../lib/friends') const middlewares = require('../../middlewares') +const admin = middlewares.admin const oAuth = middlewares.oauth const pagination = middlewares.pagination const validators = middlewares.validators @@ -43,6 +44,21 @@ const storage = multer.diskStorage({ const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }]) +router.get('/abuse', + oAuth.authenticate, + admin.ensureIsAdmin, + validatorsPagination.pagination, + validatorsSort.videoAbusesSort, + sort.setVideoAbusesSort, + pagination.setPagination, + listVideoAbuses +) +router.post('/:id/abuse', + oAuth.authenticate, + validatorsVideos.videoAbuseReport, + reportVideoAbuse +) + router.get('/', validatorsPagination.pagination, validatorsSort.videosSort, @@ -283,7 +299,7 @@ function listVideos (req, res, next) { db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { if (err) return next(err) - res.json(getFormatedVideos(videosList, videosTotal)) + res.json(utils.getFormatedObjects(videosList, videosTotal)) }) } @@ -306,22 +322,45 @@ function searchVideos (req, res, next) { function (err, videosList, videosTotal) { if (err) return next(err) - res.json(getFormatedVideos(videosList, videosTotal)) + res.json(utils.getFormatedObjects(videosList, videosTotal)) } ) } -// --------------------------------------------------------------------------- - -function getFormatedVideos (videos, videosTotal) { - const formatedVideos = [] +function listVideoAbuses (req, res, next) { + db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) { + if (err) return next(err) - videos.forEach(function (video) { - formatedVideos.push(video.toFormatedJSON()) + res.json(utils.getFormatedObjects(abusesList, abusesTotal)) }) +} - return { - total: videosTotal, - data: formatedVideos +function reportVideoAbuse (req, res, next) { + const videoInstance = res.locals.video + const reporterUsername = res.locals.oauth.token.User.username + + const abuse = { + reporterUsername, + reason: req.body.reason, + videoId: videoInstance.id, + reporterPodId: null // This is our pod that reported this abuse } + + db.VideoAbuse.create(abuse).asCallback(function (err) { + if (err) return next(err) + + // We send the information to the destination pod + if (videoInstance.isOwned() === false) { + const reportData = { + reporterUsername, + reportReason: abuse.reason, + videoRemoteId: videoInstance.remoteId + } + + friends.reportAbuseVideoToFriend(reportData, videoInstance) + } + + return res.type('json').status(204).end() + }) } + -- cgit v1.2.3 From ed04d94f6d7132055f97a2f757b85c03c5f2a0b6 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 6 Jan 2017 23:24:47 +0100 Subject: Server: try to have a better video integrity --- server/controllers/api/remote/videos.js | 56 +++++++++++++--- server/controllers/api/videos.js | 114 +++++++++++++++++++++----------- 2 files changed, 124 insertions(+), 46 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index d02da4463..6d768eae8 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js @@ -10,6 +10,7 @@ const secureMiddleware = middlewares.secure const videosValidators = middlewares.validators.remote.videos const signatureValidators = middlewares.validators.remote.signature const logger = require('../../../helpers/logger') +const utils = require('../../../helpers/utils') const router = express.Router() @@ -37,11 +38,11 @@ function remoteVideos (req, res, next) { switch (request.type) { case 'add': - addRemoteVideo(data, fromPod, callbackEach) + addRemoteVideoRetryWrapper(data, fromPod, callbackEach) break case 'update': - updateRemoteVideo(data, fromPod, callbackEach) + updateRemoteVideoRetryWrapper(data, fromPod, callbackEach) break case 'remove': @@ -63,13 +64,30 @@ function remoteVideos (req, res, next) { return res.type('json').status(204).end() } +// Handle retries on fail +function addRemoteVideoRetryWrapper (videoToCreateData, fromPod, finalCallback) { + utils.transactionRetryer( + function (callback) { + return addRemoteVideo(videoToCreateData, fromPod, callback) + }, + function (err) { + if (err) { + logger.error('Cannot insert the remote video with many retries.', { error: err }) + return finalCallback(err) + } + + return finalCallback() + } + ) +} + function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { - logger.debug('Adding remote video "%s".', videoToCreateData.name) + logger.debug('Adding remote video "%s".', videoToCreateData.remoteId) waterfall([ function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { + db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { return callback(err, t) }) }, @@ -103,6 +121,7 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { authorId: author.id, duration: videoToCreateData.duration, createdAt: videoToCreateData.createdAt, + // FIXME: updatedAt does not seems to be considered by Sequelize updatedAt: videoToCreateData.updatedAt } @@ -142,7 +161,8 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { ], function (err, t) { if (err) { - logger.error('Cannot insert the remote video.') + // This is just a debug because we will retry the insert + logger.debug('Cannot insert the remote video.', { error: err }) // Abort transaction? if (t) t.rollback() @@ -157,8 +177,25 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { }) } +// Handle retries on fail +function updateRemoteVideoRetryWrapper (videoAttributesToUpdate, fromPod, finalCallback) { + utils.transactionRetryer( + function (callback) { + return updateRemoteVideo(videoAttributesToUpdate, fromPod, callback) + }, + function (err) { + if (err) { + logger.error('Cannot update the remote video with many retries.', { error: err }) + return finalCallback(err) + } + + return finalCallback() + } + ) +} + function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { - logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name) + logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId) waterfall([ @@ -208,7 +245,8 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { ], function (err, t) { if (err) { - logger.error('Cannot update the remote video.') + // This is just a debug because we will retry the insert + logger.debug('Cannot update the remote video.', { error: err }) // Abort transaction? if (t) t.rollback() @@ -238,7 +276,7 @@ function reportAbuseRemoteVideo (reportData, fromPod, callback) { if (err || !video) { if (!err) err = new Error('video not found') - logger.error('Cannot load video from host and remote id.', { error: err }) + logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId }) return callback(err) } @@ -260,7 +298,7 @@ function fetchVideo (podHost, remoteId, callback) { if (err || !video) { if (!err) err = new Error('video not found') - logger.error('Cannot load video from host and remote id.', { error: err }) + logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId }) return callback(err) } diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 6829804ec..4d45c11c0 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -70,13 +70,13 @@ router.put('/:id', oAuth.authenticate, reqFiles, validatorsVideos.videosUpdate, - updateVideo + updateVideoRetryWrapper ) router.post('/', oAuth.authenticate, reqFiles, validatorsVideos.videosAdd, - addVideo + addVideoRetryWrapper ) router.get('/:id', validatorsVideos.videosGet, @@ -103,19 +103,37 @@ module.exports = router // --------------------------------------------------------------------------- -function addVideo (req, res, next) { - const videoFile = req.files.videofile[0] +// Wrapper to video add that retry the function if there is a database error +// We need this because we run the transaction in SERIALIZABLE isolation that can fail +function addVideoRetryWrapper (req, res, next) { + utils.transactionRetryer( + function (callback) { + return addVideo(req, res, req.files.videofile[0], callback) + }, + function (err) { + if (err) { + logger.error('Cannot insert the video with many retries.', { error: err }) + return next(err) + } + + // TODO : include Location of the new video -> 201 + return res.type('json').status(204).end() + } + ) +} + +function addVideo (req, res, videoFile, callback) { const videoInfos = req.body waterfall([ - function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { - return callback(err, t) + function startTransaction (callbackWaterfall) { + db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { + return callbackWaterfall(err, t) }) }, - function findOrCreateAuthor (t, callback) { + function findOrCreateAuthor (t, callbackWaterfall) { const user = res.locals.oauth.token.User const name = user.username @@ -124,19 +142,19 @@ function addVideo (req, res, next) { const userId = user.id db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { - return callback(err, t, authorInstance) + return callbackWaterfall(err, t, authorInstance) }) }, - function findOrCreateTags (t, author, callback) { + function findOrCreateTags (t, author, callbackWaterfall) { const tags = videoInfos.tags db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { - return callback(err, t, author, tagInstances) + return callbackWaterfall(err, t, author, tagInstances) }) }, - function createVideoObject (t, author, tagInstances, callback) { + function createVideoObject (t, author, tagInstances, callbackWaterfall) { const videoData = { name: videoInfos.name, remoteId: null, @@ -148,74 +166,97 @@ function addVideo (req, res, next) { const video = db.Video.build(videoData) - return callback(null, t, author, tagInstances, video) + return callbackWaterfall(null, t, author, tagInstances, video) }, // Set the videoname the same as the id - function renameVideoFile (t, author, tagInstances, video, callback) { + function renameVideoFile (t, author, tagInstances, video, callbackWaterfall) { const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR const source = path.join(videoDir, videoFile.filename) const destination = path.join(videoDir, video.getVideoFilename()) fs.rename(source, destination, function (err) { - return callback(err, t, author, tagInstances, video) + if (err) return callbackWaterfall(err) + + // This is important in case if there is another attempt + videoFile.filename = video.getVideoFilename() + return callbackWaterfall(null, t, author, tagInstances, video) }) }, - function insertVideoIntoDB (t, author, tagInstances, video, callback) { + function insertVideoIntoDB (t, author, tagInstances, video, callbackWaterfall) { const options = { transaction: t } // Add tags association video.save(options).asCallback(function (err, videoCreated) { - if (err) return callback(err) + if (err) return callbackWaterfall(err) // Do not forget to add Author informations to the created video videoCreated.Author = author - return callback(err, t, tagInstances, videoCreated) + return callbackWaterfall(err, t, tagInstances, videoCreated) }) }, - function associateTagsToVideo (t, tagInstances, video, callback) { + function associateTagsToVideo (t, tagInstances, video, callbackWaterfall) { const options = { transaction: t } video.setTags(tagInstances, options).asCallback(function (err) { video.Tags = tagInstances - return callback(err, t, video) + return callbackWaterfall(err, t, video) }) }, - function sendToFriends (t, video, callback) { + function sendToFriends (t, video, callbackWaterfall) { video.toAddRemoteJSON(function (err, remoteVideo) { - if (err) return callback(err) + if (err) return callbackWaterfall(err) // Now we'll add the video's meta data to our friends - friends.addVideoToFriends(remoteVideo) - - return callback(null, t) + friends.addVideoToFriends(remoteVideo, t, function (err) { + return callbackWaterfall(err, t) + }) }) } ], function andFinally (err, t) { if (err) { - logger.error('Cannot insert the video.') + // This is just a debug because we will retry the insert + logger.debug('Cannot insert the video.', { error: err }) // Abort transaction? if (t) t.rollback() - return next(err) + return callback(err) } // Commit transaction t.commit() - // TODO : include Location of the new video -> 201 - return res.type('json').status(204).end() + logger.info('Video with name %s created.', videoInfos.name) + + return callback(null) }) } -function updateVideo (req, res, next) { +function updateVideoRetryWrapper (req, res, next) { + utils.transactionRetryer( + function (callback) { + return updateVideo(req, res, callback) + }, + function (err) { + if (err) { + logger.error('Cannot update the video with many retries.', { error: err }) + return next(err) + } + + // TODO : include Location of the new video -> 201 + return res.type('json').status(204).end() + } + ) +} + +function updateVideo (req, res, finalCallback) { const videoInstance = res.locals.video const videoInfosToUpdate = req.body @@ -267,26 +308,25 @@ function updateVideo (req, res, next) { const json = videoInstance.toUpdateRemoteJSON() // Now we'll update the video's meta data to our friends - friends.updateVideoToFriends(json) - - return callback(null, t) + friends.updateVideoToFriends(json, t, function (err) { + return callback(err, t) + }) } ], function andFinally (err, t) { if (err) { - logger.error('Cannot insert the video.') + logger.debug('Cannot update the video.', { error: err }) // Abort transaction? if (t) t.rollback() - return next(err) + return finalCallback(err) } // Commit transaction t.commit() - // TODO : include Location of the new video -> 201 - return res.type('json').status(204).end() + return finalCallback(null) }) } -- cgit v1.2.3 From bf4ff8fe0be63422c05d42e12f25b454bb95d1a5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 10 Jan 2017 22:33:00 +0100 Subject: Server: retry video abuse requests too --- server/controllers/api/videos.js | 68 +++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 4d45c11c0..6573b1210 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -56,7 +56,7 @@ router.get('/abuse', router.post('/:id/abuse', oAuth.authenticate, validatorsVideos.videoAbuseReport, - reportVideoAbuse + reportVideoAbuseRetryWrapper ) router.get('/', @@ -375,7 +375,23 @@ function listVideoAbuses (req, res, next) { }) } -function reportVideoAbuse (req, res, next) { +function reportVideoAbuseRetryWrapper (req, res, next) { + utils.transactionRetryer( + function (callback) { + return reportVideoAbuse(req, res, callback) + }, + function (err) { + if (err) { + logger.error('Cannot report abuse to the video with many retries.', { error: err }) + return next(err) + } + + return res.type('json').status(204).end() + } + ) +} + +function reportVideoAbuse (req, res, finalCallback) { const videoInstance = res.locals.video const reporterUsername = res.locals.oauth.token.User.username @@ -386,21 +402,49 @@ function reportVideoAbuse (req, res, next) { reporterPodId: null // This is our pod that reported this abuse } - db.VideoAbuse.create(abuse).asCallback(function (err) { - if (err) return next(err) + waterfall([ + + function startTransaction (callback) { + db.sequelize.transaction().asCallback(function (err, t) { + return callback(err, t) + }) + }, + + function createAbuse (t, callback) { + db.VideoAbuse.create(abuse).asCallback(function (err, abuse) { + return callback(err, t, abuse) + }) + }, + + function sendToFriendsIfNeeded (t, abuse, callback) { + // We send the information to the destination pod + if (videoInstance.isOwned() === false) { + const reportData = { + reporterUsername, + reportReason: abuse.reason, + videoRemoteId: videoInstance.remoteId + } - // We send the information to the destination pod - if (videoInstance.isOwned() === false) { - const reportData = { - reporterUsername, - reportReason: abuse.reason, - videoRemoteId: videoInstance.remoteId + friends.reportAbuseVideoToFriend(reportData, videoInstance) } - friends.reportAbuseVideoToFriend(reportData, videoInstance) + return callback(null, t) } - return res.type('json').status(204).end() + ], function andFinally (err, t) { + if (err) { + logger.debug('Cannot update the video.', { error: err }) + + // Abort transaction? + if (t) t.rollback() + + return finalCallback(err) + } + + // Commit transaction + t.commit() + + return finalCallback(null) }) } -- cgit v1.2.3 From dea32aacde362a5fbd62a88cd32487768b788468 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 11 Jan 2017 16:22:50 +0100 Subject: Server: always check commit result --- server/controllers/api/remote/videos.js | 16 +++++++++++----- server/controllers/api/videos.js | 23 +++++++++++++++-------- 2 files changed, 26 insertions(+), 13 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index 6d768eae8..17bdce019 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js @@ -171,9 +171,12 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { } // Commit transaction - t.commit() + t.commit().asCallback(function (err) { + if (err) return finalCallback(err) - return finalCallback() + logger.info('Remote video %s inserted.', videoToCreateData.videoToCreateData.name) + return finalCallback(null) + }) }) } @@ -254,10 +257,13 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { return finalCallback(err) } - // Commit transaction - t.commit() + // Commit transaction + t.commit().asCallback(function (err) { + if (err) return finalCallback(err) - return finalCallback() + logger.info('Remote video %s updated', videoAttributesToUpdate.name) + return finalCallback(null) + }) }) } diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 6573b1210..df068f961 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -231,11 +231,12 @@ function addVideo (req, res, videoFile, callback) { } // Commit transaction - t.commit() + t.commit().asCallback(function (err) { + if (err) return callback(err) - logger.info('Video with name %s created.', videoInfos.name) - - return callback(null) + logger.info('Video with name %s created.', videoInfos.name) + return callback(null) + }) }) } @@ -324,9 +325,12 @@ function updateVideo (req, res, finalCallback) { } // Commit transaction - t.commit() + t.commit().asCallback(function (err) { + if (err) return finalCallback(err) - return finalCallback(null) + logger.info('Video with name %s updated.', videoInfosToUpdate.name) + return finalCallback(null) + }) }) } @@ -442,9 +446,12 @@ function reportVideoAbuse (req, res, finalCallback) { } // Commit transaction - t.commit() + t.commit().asCallback(function (err) { + if (err) return finalCallback(err) - return finalCallback(null) + logger.info('Abuse report for video %s created.', videoInstance.name) + return finalCallback(null) + }) }) } -- cgit v1.2.3 From d8cc063e9775688a1631eda9203411a2dba0333c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 11 Jan 2017 18:06:51 +0100 Subject: Server: do not break remote videos processing on error --- server/controllers/api/remote/videos.js | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index 17bdce019..b9494f602 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js @@ -73,10 +73,10 @@ function addRemoteVideoRetryWrapper (videoToCreateData, fromPod, finalCallback) function (err) { if (err) { logger.error('Cannot insert the remote video with many retries.', { error: err }) - return finalCallback(err) } - return finalCallback() + // Do not return the error, continue the process + return finalCallback(null) } ) } @@ -174,7 +174,7 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { t.commit().asCallback(function (err) { if (err) return finalCallback(err) - logger.info('Remote video %s inserted.', videoToCreateData.videoToCreateData.name) + logger.info('Remote video %s inserted.', videoToCreateData.name) return finalCallback(null) }) }) @@ -189,10 +189,10 @@ function updateRemoteVideoRetryWrapper (videoAttributesToUpdate, fromPod, finalC function (err) { if (err) { logger.error('Cannot update the remote video with many retries.', { error: err }) - return finalCallback(err) } - return finalCallback() + // Do not return the error, continue the process + return finalCallback(null) } ) } @@ -270,10 +270,18 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { function removeRemoteVideo (videoToRemoveData, fromPod, callback) { // We need the instance because we have to remove some other stuffs (thumbnail etc) fetchVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) { - if (err) return callback(err) + // Do not return the error, continue the process + if (err) return callback(null) logger.debug('Removing remote video %s.', video.remoteId) - video.destroy().asCallback(callback) + video.destroy().asCallback(function (err) { + // Do not return the error, continue the process + if (err) { + logger.error('Cannot remove remote video with id %s.', videoToRemoveData.remoteId, { error: err }) + } + + return callback(null) + }) }) } @@ -283,7 +291,8 @@ function reportAbuseRemoteVideo (reportData, fromPod, callback) { if (!err) err = new Error('video not found') logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId }) - return callback(err) + // Do not return the error, continue the process + return callback(null) } logger.debug('Reporting remote abuse for video %s.', video.id) @@ -295,7 +304,13 @@ function reportAbuseRemoteVideo (reportData, fromPod, callback) { videoId: video.id } - db.VideoAbuse.create(videoAbuseData).asCallback(callback) + db.VideoAbuse.create(videoAbuseData).asCallback(function (err) { + if (err) { + logger.error('Cannot create remote abuse video.', { error: err }) + } + + return callback(null) + }) }) } -- cgit v1.2.3 From edc5e86006bf5e4a2819c380bb65734fe9caa87e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 11 Jan 2017 18:41:40 +0100 Subject: Server: transaction serializable for videos --- server/controllers/api/remote/videos.js | 2 +- server/controllers/api/videos.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index b9494f602..c45a86dbb 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js @@ -203,7 +203,7 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { waterfall([ function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { + db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { return callback(err, t) }) }, diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index df068f961..55d671f5b 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -264,7 +264,7 @@ function updateVideo (req, res, finalCallback) { waterfall([ function startTransaction (callback) { - db.sequelize.transaction().asCallback(function (err, t) { + db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { return callback(err, t) }) }, -- cgit v1.2.3 From 7f4e7c36373217b8e92cf227c71999a0ce9a15d9 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 12 Jan 2017 09:47:21 +0100 Subject: Server: fix update remote video infohash --- server/controllers/api/videos.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 55d671f5b..2c4af520e 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js @@ -259,6 +259,7 @@ function updateVideoRetryWrapper (req, res, next) { function updateVideo (req, res, finalCallback) { const videoInstance = res.locals.video + const videoFieldsSave = videoInstance.toJSON() const videoInfosToUpdate = req.body waterfall([ @@ -280,12 +281,13 @@ function updateVideo (req, res, finalCallback) { }, function updateVideoIntoDB (t, tagInstances, callback) { - const options = { transaction: t } + const options = { + transaction: t + } if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name) if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description) - // Add tags association videoInstance.save(options).asCallback(function (err) { return callback(err, t, tagInstances) }) @@ -321,6 +323,14 @@ function updateVideo (req, res, finalCallback) { // Abort transaction? if (t) t.rollback() + // Force fields we want to update + // If the transaction is retried, sequelize will think the object has not changed + // So it will skip the SQL request, even if the last one was ROLLBACKed! + Object.keys(videoFieldsSave).forEach(function (key) { + const value = videoFieldsSave[key] + videoInstance.set(key, value) + }) + return finalCallback(err) } -- cgit v1.2.3