diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2016-12-11 21:50:51 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2016-12-19 21:22:28 +0100 |
commit | feb4bdfd9b46e87aadfa7c0d5338cde887d1f58c (patch) | |
tree | 2abc9fbc9569760e218fd52835850b757344b420 /server | |
parent | 108626609eda75e4ecc0a83a650a4d53c46220e0 (diff) | |
download | PeerTube-feb4bdfd9b46e87aadfa7c0d5338cde887d1f58c.tar.gz PeerTube-feb4bdfd9b46e87aadfa7c0d5338cde887d1f58c.tar.zst PeerTube-feb4bdfd9b46e87aadfa7c0d5338cde887d1f58c.zip |
First version with PostgreSQL
Diffstat (limited to 'server')
39 files changed, 1113 insertions, 669 deletions
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 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const express = require('express') | 3 | const express = require('express') |
4 | const mongoose = require('mongoose') | ||
5 | 4 | ||
6 | const constants = require('../../initializers/constants') | 5 | const constants = require('../../initializers/constants') |
6 | const db = require('../../initializers/database') | ||
7 | const logger = require('../../helpers/logger') | 7 | const logger = require('../../helpers/logger') |
8 | 8 | ||
9 | const Client = mongoose.model('OAuthClient') | ||
10 | |||
11 | const router = express.Router() | 9 | const router = express.Router() |
12 | 10 | ||
13 | router.get('/local', getLocalClient) | 11 | router.get('/local', getLocalClient) |
@@ -27,12 +25,12 @@ function getLocalClient (req, res, next) { | |||
27 | return res.type('json').status(403).end() | 25 | return res.type('json').status(403).end() |
28 | } | 26 | } |
29 | 27 | ||
30 | Client.loadFirstClient(function (err, client) { | 28 | db.OAuthClient.loadFirstClient(function (err, client) { |
31 | if (err) return next(err) | 29 | if (err) return next(err) |
32 | if (!client) return next(new Error('No client available.')) | 30 | if (!client) return next(new Error('No client available.')) |
33 | 31 | ||
34 | res.json({ | 32 | res.json({ |
35 | client_id: client._id, | 33 | client_id: client.clientId, |
36 | client_secret: client.clientSecret | 34 | client_secret: client.clientSecret |
37 | }) | 35 | }) |
38 | }) | 36 | }) |
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 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const express = require('express') | 3 | const express = require('express') |
4 | const mongoose = require('mongoose') | ||
5 | const waterfall = require('async/waterfall') | 4 | const waterfall = require('async/waterfall') |
6 | 5 | ||
6 | const db = require('../../initializers/database') | ||
7 | const logger = require('../../helpers/logger') | 7 | const logger = require('../../helpers/logger') |
8 | const friends = require('../../lib/friends') | 8 | const friends = require('../../lib/friends') |
9 | const middlewares = require('../../middlewares') | 9 | const middlewares = require('../../middlewares') |
@@ -15,7 +15,6 @@ const validators = middlewares.validators.pods | |||
15 | const signatureValidator = middlewares.validators.remote.signature | 15 | const signatureValidator = middlewares.validators.remote.signature |
16 | 16 | ||
17 | const router = express.Router() | 17 | const router = express.Router() |
18 | const Pod = mongoose.model('Pod') | ||
19 | 18 | ||
20 | router.get('/', listPods) | 19 | router.get('/', listPods) |
21 | router.post('/', | 20 | router.post('/', |
@@ -53,15 +52,15 @@ function addPods (req, res, next) { | |||
53 | 52 | ||
54 | waterfall([ | 53 | waterfall([ |
55 | function addPod (callback) { | 54 | function addPod (callback) { |
56 | const pod = new Pod(informations) | 55 | const pod = db.Pod.build(informations) |
57 | pod.save(function (err, podCreated) { | 56 | pod.save().asCallback(function (err, podCreated) { |
58 | // Be sure about the number of parameters for the callback | 57 | // Be sure about the number of parameters for the callback |
59 | return callback(err, podCreated) | 58 | return callback(err, podCreated) |
60 | }) | 59 | }) |
61 | }, | 60 | }, |
62 | 61 | ||
63 | function sendMyVideos (podCreated, callback) { | 62 | function sendMyVideos (podCreated, callback) { |
64 | friends.sendOwnedVideosToPod(podCreated._id) | 63 | friends.sendOwnedVideosToPod(podCreated.id) |
65 | 64 | ||
66 | callback(null) | 65 | callback(null) |
67 | }, | 66 | }, |
@@ -84,7 +83,7 @@ function addPods (req, res, next) { | |||
84 | } | 83 | } |
85 | 84 | ||
86 | function listPods (req, res, next) { | 85 | function listPods (req, res, next) { |
87 | Pod.list(function (err, podsList) { | 86 | db.Pod.list(function (err, podsList) { |
88 | if (err) return next(err) | 87 | if (err) return next(err) |
89 | 88 | ||
90 | res.json(getFormatedPods(podsList)) | 89 | res.json(getFormatedPods(podsList)) |
@@ -111,11 +110,11 @@ function removePods (req, res, next) { | |||
111 | 110 | ||
112 | waterfall([ | 111 | waterfall([ |
113 | function loadPod (callback) { | 112 | function loadPod (callback) { |
114 | Pod.loadByHost(host, callback) | 113 | db.Pod.loadByHost(host, callback) |
115 | }, | 114 | }, |
116 | 115 | ||
117 | function removePod (pod, callback) { | 116 | function removePod (pod, callback) { |
118 | pod.remove(callback) | 117 | pod.destroy().asCallback(callback) |
119 | } | 118 | } |
120 | ], function (err) { | 119 | ], function (err) { |
121 | if (err) return next(err) | 120 | 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 @@ | |||
3 | const each = require('async/each') | 3 | const each = require('async/each') |
4 | const eachSeries = require('async/eachSeries') | 4 | const eachSeries = require('async/eachSeries') |
5 | const express = require('express') | 5 | const express = require('express') |
6 | const mongoose = require('mongoose') | 6 | const waterfall = require('async/waterfall') |
7 | 7 | ||
8 | const db = require('../../initializers/database') | ||
8 | const middlewares = require('../../middlewares') | 9 | const middlewares = require('../../middlewares') |
9 | const secureMiddleware = middlewares.secure | 10 | const secureMiddleware = middlewares.secure |
10 | const validators = middlewares.validators.remote | 11 | const validators = middlewares.validators.remote |
11 | const logger = require('../../helpers/logger') | 12 | const logger = require('../../helpers/logger') |
12 | 13 | ||
13 | const router = express.Router() | 14 | const router = express.Router() |
14 | const Video = mongoose.model('Video') | ||
15 | 15 | ||
16 | router.post('/videos', | 16 | router.post('/videos', |
17 | validators.signature, | 17 | validators.signature, |
@@ -53,34 +53,99 @@ function remoteVideos (req, res, next) { | |||
53 | function addRemoteVideo (videoToCreateData, fromHost, callback) { | 53 | function addRemoteVideo (videoToCreateData, fromHost, callback) { |
54 | logger.debug('Adding remote video "%s".', videoToCreateData.name) | 54 | logger.debug('Adding remote video "%s".', videoToCreateData.name) |
55 | 55 | ||
56 | const video = new Video(videoToCreateData) | 56 | waterfall([ |
57 | video.podHost = fromHost | 57 | |
58 | Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { | 58 | function findOrCreatePod (callback) { |
59 | if (err) { | 59 | fromHost |
60 | logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) | 60 | |
61 | return callback(err) | 61 | const query = { |
62 | where: { | ||
63 | host: fromHost | ||
64 | }, | ||
65 | defaults: { | ||
66 | host: fromHost | ||
67 | } | ||
68 | } | ||
69 | |||
70 | db.Pod.findOrCreate(query).asCallback(function (err, result) { | ||
71 | // [ instance, wasCreated ] | ||
72 | return callback(err, result[0]) | ||
73 | }) | ||
74 | }, | ||
75 | |||
76 | function findOrCreateAuthor (pod, callback) { | ||
77 | const username = videoToCreateData.author | ||
78 | |||
79 | const query = { | ||
80 | where: { | ||
81 | name: username, | ||
82 | podId: pod.id | ||
83 | }, | ||
84 | defaults: { | ||
85 | name: username, | ||
86 | podId: pod.id | ||
87 | } | ||
88 | } | ||
89 | |||
90 | db.Author.findOrCreate(query).asCallback(function (err, result) { | ||
91 | // [ instance, wasCreated ] | ||
92 | return callback(err, result[0]) | ||
93 | }) | ||
94 | }, | ||
95 | |||
96 | function createVideoObject (author, callback) { | ||
97 | const videoData = { | ||
98 | name: videoToCreateData.name, | ||
99 | remoteId: videoToCreateData.remoteId, | ||
100 | extname: videoToCreateData.extname, | ||
101 | infoHash: videoToCreateData.infoHash, | ||
102 | description: videoToCreateData.description, | ||
103 | authorId: author.id, | ||
104 | duration: videoToCreateData.duration, | ||
105 | tags: videoToCreateData.tags | ||
106 | } | ||
107 | |||
108 | const video = db.Video.build(videoData) | ||
109 | |||
110 | return callback(null, video) | ||
111 | }, | ||
112 | |||
113 | function generateThumbnail (video, callback) { | ||
114 | db.Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { | ||
115 | if (err) { | ||
116 | logger.error('Cannot generate thumbnail from base 64 data.', { error: err }) | ||
117 | return callback(err) | ||
118 | } | ||
119 | |||
120 | video.save().asCallback(callback) | ||
121 | }) | ||
122 | }, | ||
123 | |||
124 | function insertIntoDB (video, callback) { | ||
125 | video.save().asCallback(callback) | ||
62 | } | 126 | } |
63 | 127 | ||
64 | video.save(callback) | 128 | ], callback) |
65 | }) | ||
66 | } | 129 | } |
67 | 130 | ||
68 | function removeRemoteVideo (videoToRemoveData, fromHost, callback) { | 131 | function removeRemoteVideo (videoToRemoveData, fromHost, callback) { |
132 | // TODO: use bulkDestroy? | ||
133 | |||
69 | // We need the list because we have to remove some other stuffs (thumbnail etc) | 134 | // We need the list because we have to remove some other stuffs (thumbnail etc) |
70 | Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { | 135 | db.Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { |
71 | if (err) { | 136 | if (err) { |
72 | logger.error('Cannot list videos from host and magnets.', { error: err }) | 137 | logger.error('Cannot list videos from host and remote id.', { error: err.message }) |
73 | return callback(err) | 138 | return callback(err) |
74 | } | 139 | } |
75 | 140 | ||
76 | if (videosList.length === 0) { | 141 | if (videosList.length === 0) { |
77 | logger.error('No remote video was found for this pod.', { magnetUri: videoToRemoveData.magnetUri, podHost: fromHost }) | 142 | logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromHost }) |
78 | } | 143 | } |
79 | 144 | ||
80 | each(videosList, function (video, callbackEach) { | 145 | each(videosList, function (video, callbackEach) { |
81 | logger.debug('Removing remote video %s.', video.magnetUri) | 146 | logger.debug('Removing remote video %s.', video.remoteId) |
82 | 147 | ||
83 | video.remove(callbackEach) | 148 | video.destroy().asCallback(callbackEach) |
84 | }, callback) | 149 | }, callback) |
85 | }) | 150 | }) |
86 | } | 151 | } |
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 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const express = require('express') | 3 | const express = require('express') |
4 | const mongoose = require('mongoose') | ||
5 | 4 | ||
6 | const constants = require('../../initializers/constants') | 5 | const constants = require('../../initializers/constants') |
6 | const db = require('../../initializers/database') | ||
7 | const middlewares = require('../../middlewares') | 7 | const middlewares = require('../../middlewares') |
8 | const admin = middlewares.admin | 8 | const admin = middlewares.admin |
9 | const oAuth = middlewares.oauth | 9 | const oAuth = middlewares.oauth |
10 | 10 | ||
11 | const Request = mongoose.model('Request') | ||
12 | |||
13 | const router = express.Router() | 11 | const router = express.Router() |
14 | 12 | ||
15 | router.get('/stats', | 13 | router.get('/stats', |
@@ -25,13 +23,13 @@ module.exports = router | |||
25 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
26 | 24 | ||
27 | function getStatsRequests (req, res, next) { | 25 | function getStatsRequests (req, res, next) { |
28 | Request.list(function (err, requests) { | 26 | db.Request.countTotalRequests(function (err, totalRequests) { |
29 | if (err) return next(err) | 27 | if (err) return next(err) |
30 | 28 | ||
31 | return res.json({ | 29 | return res.json({ |
32 | requests: requests, | 30 | totalRequests: totalRequests, |
33 | maxRequestsInParallel: constants.REQUESTS_IN_PARALLEL, | 31 | maxRequestsInParallel: constants.REQUESTS_IN_PARALLEL, |
34 | remainingMilliSeconds: Request.remainingMilliSeconds(), | 32 | remainingMilliSeconds: db.Request.remainingMilliSeconds(), |
35 | milliSecondsInterval: constants.REQUESTS_INTERVAL | 33 | milliSecondsInterval: constants.REQUESTS_INTERVAL |
36 | }) | 34 | }) |
37 | }) | 35 | }) |
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 @@ | |||
2 | 2 | ||
3 | const each = require('async/each') | 3 | const each = require('async/each') |
4 | const express = require('express') | 4 | const express = require('express') |
5 | const mongoose = require('mongoose') | ||
6 | const waterfall = require('async/waterfall') | 5 | const waterfall = require('async/waterfall') |
7 | 6 | ||
8 | const constants = require('../../initializers/constants') | 7 | const constants = require('../../initializers/constants') |
8 | const db = require('../../initializers/database') | ||
9 | const friends = require('../../lib/friends') | 9 | const friends = require('../../lib/friends') |
10 | const logger = require('../../helpers/logger') | 10 | const logger = require('../../helpers/logger') |
11 | const middlewares = require('../../middlewares') | 11 | const middlewares = require('../../middlewares') |
@@ -17,9 +17,6 @@ const validatorsPagination = middlewares.validators.pagination | |||
17 | const validatorsSort = middlewares.validators.sort | 17 | const validatorsSort = middlewares.validators.sort |
18 | const validatorsUsers = middlewares.validators.users | 18 | const validatorsUsers = middlewares.validators.users |
19 | 19 | ||
20 | const User = mongoose.model('User') | ||
21 | const Video = mongoose.model('Video') | ||
22 | |||
23 | const router = express.Router() | 20 | const router = express.Router() |
24 | 21 | ||
25 | router.get('/me', oAuth.authenticate, getUserInformation) | 22 | router.get('/me', oAuth.authenticate, getUserInformation) |
@@ -62,13 +59,13 @@ module.exports = router | |||
62 | // --------------------------------------------------------------------------- | 59 | // --------------------------------------------------------------------------- |
63 | 60 | ||
64 | function createUser (req, res, next) { | 61 | function createUser (req, res, next) { |
65 | const user = new User({ | 62 | const user = db.User.build({ |
66 | username: req.body.username, | 63 | username: req.body.username, |
67 | password: req.body.password, | 64 | password: req.body.password, |
68 | role: constants.USER_ROLES.USER | 65 | role: constants.USER_ROLES.USER |
69 | }) | 66 | }) |
70 | 67 | ||
71 | user.save(function (err, createdUser) { | 68 | user.save().asCallback(function (err, createdUser) { |
72 | if (err) return next(err) | 69 | if (err) return next(err) |
73 | 70 | ||
74 | return res.type('json').status(204).end() | 71 | return res.type('json').status(204).end() |
@@ -76,7 +73,7 @@ function createUser (req, res, next) { | |||
76 | } | 73 | } |
77 | 74 | ||
78 | function getUserInformation (req, res, next) { | 75 | function getUserInformation (req, res, next) { |
79 | User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { | 76 | db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { |
80 | if (err) return next(err) | 77 | if (err) return next(err) |
81 | 78 | ||
82 | return res.json(user.toFormatedJSON()) | 79 | return res.json(user.toFormatedJSON()) |
@@ -84,7 +81,7 @@ function getUserInformation (req, res, next) { | |||
84 | } | 81 | } |
85 | 82 | ||
86 | function listUsers (req, res, next) { | 83 | function listUsers (req, res, next) { |
87 | User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) { | 84 | db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) { |
88 | if (err) return next(err) | 85 | if (err) return next(err) |
89 | 86 | ||
90 | res.json(getFormatedUsers(usersList, usersTotal)) | 87 | res.json(getFormatedUsers(usersList, usersTotal)) |
@@ -94,18 +91,19 @@ function listUsers (req, res, next) { | |||
94 | function removeUser (req, res, next) { | 91 | function removeUser (req, res, next) { |
95 | waterfall([ | 92 | waterfall([ |
96 | function getUser (callback) { | 93 | function getUser (callback) { |
97 | User.loadById(req.params.id, callback) | 94 | db.User.loadById(req.params.id, callback) |
98 | }, | 95 | }, |
99 | 96 | ||
97 | // TODO: use foreignkey? | ||
100 | function getVideos (user, callback) { | 98 | function getVideos (user, callback) { |
101 | Video.listOwnedByAuthor(user.username, function (err, videos) { | 99 | db.Video.listOwnedByAuthor(user.username, function (err, videos) { |
102 | return callback(err, user, videos) | 100 | return callback(err, user, videos) |
103 | }) | 101 | }) |
104 | }, | 102 | }, |
105 | 103 | ||
106 | function removeVideosFromDB (user, videos, callback) { | 104 | function removeVideosFromDB (user, videos, callback) { |
107 | each(videos, function (video, callbackEach) { | 105 | each(videos, function (video, callbackEach) { |
108 | video.remove(callbackEach) | 106 | video.destroy().asCallback(callbackEach) |
109 | }, function (err) { | 107 | }, function (err) { |
110 | return callback(err, user, videos) | 108 | return callback(err, user, videos) |
111 | }) | 109 | }) |
@@ -115,7 +113,7 @@ function removeUser (req, res, next) { | |||
115 | videos.forEach(function (video) { | 113 | videos.forEach(function (video) { |
116 | const params = { | 114 | const params = { |
117 | name: video.name, | 115 | name: video.name, |
118 | magnetUri: video.magnetUri | 116 | remoteId: video.id |
119 | } | 117 | } |
120 | 118 | ||
121 | friends.removeVideoToFriends(params) | 119 | friends.removeVideoToFriends(params) |
@@ -125,7 +123,7 @@ function removeUser (req, res, next) { | |||
125 | }, | 123 | }, |
126 | 124 | ||
127 | function removeUserFromDB (user, callback) { | 125 | function removeUserFromDB (user, callback) { |
128 | user.remove(callback) | 126 | user.destroy().asCallback(callback) |
129 | } | 127 | } |
130 | ], function andFinally (err) { | 128 | ], function andFinally (err) { |
131 | if (err) { | 129 | if (err) { |
@@ -138,11 +136,11 @@ function removeUser (req, res, next) { | |||
138 | } | 136 | } |
139 | 137 | ||
140 | function updateUser (req, res, next) { | 138 | function updateUser (req, res, next) { |
141 | User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { | 139 | db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { |
142 | if (err) return next(err) | 140 | if (err) return next(err) |
143 | 141 | ||
144 | user.password = req.body.password | 142 | user.password = req.body.password |
145 | user.save(function (err) { | 143 | user.save().asCallback(function (err) { |
146 | if (err) return next(err) | 144 | if (err) return next(err) |
147 | 145 | ||
148 | return res.sendStatus(204) | 146 | 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 @@ | |||
2 | 2 | ||
3 | const express = require('express') | 3 | const express = require('express') |
4 | const fs = require('fs') | 4 | const fs = require('fs') |
5 | const mongoose = require('mongoose') | ||
6 | const multer = require('multer') | 5 | const multer = require('multer') |
7 | const path = require('path') | 6 | const path = require('path') |
8 | const waterfall = require('async/waterfall') | 7 | const waterfall = require('async/waterfall') |
9 | 8 | ||
10 | const constants = require('../../initializers/constants') | 9 | const constants = require('../../initializers/constants') |
10 | const db = require('../../initializers/database') | ||
11 | const logger = require('../../helpers/logger') | 11 | const logger = require('../../helpers/logger') |
12 | const friends = require('../../lib/friends') | 12 | const friends = require('../../lib/friends') |
13 | const middlewares = require('../../middlewares') | 13 | const middlewares = require('../../middlewares') |
@@ -22,7 +22,6 @@ const sort = middlewares.sort | |||
22 | const utils = require('../../helpers/utils') | 22 | const utils = require('../../helpers/utils') |
23 | 23 | ||
24 | const router = express.Router() | 24 | const router = express.Router() |
25 | const Video = mongoose.model('Video') | ||
26 | 25 | ||
27 | // multer configuration | 26 | // multer configuration |
28 | const storage = multer.diskStorage({ | 27 | const storage = multer.diskStorage({ |
@@ -87,40 +86,60 @@ function addVideo (req, res, next) { | |||
87 | const videoInfos = req.body | 86 | const videoInfos = req.body |
88 | 87 | ||
89 | waterfall([ | 88 | waterfall([ |
90 | function createVideoObject (callback) { | ||
91 | const id = mongoose.Types.ObjectId() | ||
92 | 89 | ||
90 | function findOrCreateAuthor (callback) { | ||
91 | const username = res.locals.oauth.token.user.username | ||
92 | |||
93 | const query = { | ||
94 | where: { | ||
95 | name: username, | ||
96 | podId: null | ||
97 | }, | ||
98 | defaults: { | ||
99 | name: username, | ||
100 | podId: null // null because it is OUR pod | ||
101 | } | ||
102 | } | ||
103 | |||
104 | db.Author.findOrCreate(query).asCallback(function (err, result) { | ||
105 | // [ instance, wasCreated ] | ||
106 | return callback(err, result[0]) | ||
107 | }) | ||
108 | }, | ||
109 | |||
110 | function createVideoObject (author, callback) { | ||
93 | const videoData = { | 111 | const videoData = { |
94 | _id: id, | ||
95 | name: videoInfos.name, | 112 | name: videoInfos.name, |
96 | remoteId: null, | 113 | remoteId: null, |
97 | extname: path.extname(videoFile.filename), | 114 | extname: path.extname(videoFile.filename), |
98 | description: videoInfos.description, | 115 | description: videoInfos.description, |
99 | author: res.locals.oauth.token.user.username, | ||
100 | duration: videoFile.duration, | 116 | duration: videoFile.duration, |
101 | tags: videoInfos.tags | 117 | tags: videoInfos.tags, |
118 | authorId: author.id | ||
102 | } | 119 | } |
103 | 120 | ||
104 | const video = new Video(videoData) | 121 | const video = db.Video.build(videoData) |
105 | 122 | ||
106 | return callback(null, video) | 123 | return callback(null, author, video) |
107 | }, | 124 | }, |
108 | 125 | ||
109 | // Set the videoname the same as the MongoDB id | 126 | // Set the videoname the same as the id |
110 | function renameVideoFile (video, callback) { | 127 | function renameVideoFile (author, video, callback) { |
111 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR | 128 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR |
112 | const source = path.join(videoDir, videoFile.filename) | 129 | const source = path.join(videoDir, videoFile.filename) |
113 | const destination = path.join(videoDir, video.getVideoFilename()) | 130 | const destination = path.join(videoDir, video.getVideoFilename()) |
114 | 131 | ||
115 | fs.rename(source, destination, function (err) { | 132 | fs.rename(source, destination, function (err) { |
116 | return callback(err, video) | 133 | return callback(err, author, video) |
117 | }) | 134 | }) |
118 | }, | 135 | }, |
119 | 136 | ||
120 | function insertIntoDB (video, callback) { | 137 | function insertIntoDB (author, video, callback) { |
121 | video.save(function (err, video) { | 138 | video.save().asCallback(function (err, videoCreated) { |
122 | // Assert there are only one argument sent to the next function (video) | 139 | // Do not forget to add Author informations to the created video |
123 | return callback(err, video) | 140 | videoCreated.Author = author |
141 | |||
142 | return callback(err, videoCreated) | ||
124 | }) | 143 | }) |
125 | }, | 144 | }, |
126 | 145 | ||
@@ -147,7 +166,7 @@ function addVideo (req, res, next) { | |||
147 | } | 166 | } |
148 | 167 | ||
149 | function getVideo (req, res, next) { | 168 | function getVideo (req, res, next) { |
150 | Video.load(req.params.id, function (err, video) { | 169 | db.Video.loadAndPopulateAuthorAndPod(req.params.id, function (err, video) { |
151 | if (err) return next(err) | 170 | if (err) return next(err) |
152 | 171 | ||
153 | if (!video) { | 172 | if (!video) { |
@@ -159,7 +178,7 @@ function getVideo (req, res, next) { | |||
159 | } | 178 | } |
160 | 179 | ||
161 | function listVideos (req, res, next) { | 180 | function listVideos (req, res, next) { |
162 | Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { | 181 | db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { |
163 | if (err) return next(err) | 182 | if (err) return next(err) |
164 | 183 | ||
165 | res.json(getFormatedVideos(videosList, videosTotal)) | 184 | res.json(getFormatedVideos(videosList, videosTotal)) |
@@ -171,11 +190,11 @@ function removeVideo (req, res, next) { | |||
171 | 190 | ||
172 | waterfall([ | 191 | waterfall([ |
173 | function getVideo (callback) { | 192 | function getVideo (callback) { |
174 | Video.load(videoId, callback) | 193 | db.Video.load(videoId, callback) |
175 | }, | 194 | }, |
176 | 195 | ||
177 | function removeFromDB (video, callback) { | 196 | function removeFromDB (video, callback) { |
178 | video.remove(function (err) { | 197 | video.destroy().asCallback(function (err) { |
179 | if (err) return callback(err) | 198 | if (err) return callback(err) |
180 | 199 | ||
181 | return callback(null, video) | 200 | return callback(null, video) |
@@ -185,7 +204,7 @@ function removeVideo (req, res, next) { | |||
185 | function sendInformationToFriends (video, callback) { | 204 | function sendInformationToFriends (video, callback) { |
186 | const params = { | 205 | const params = { |
187 | name: video.name, | 206 | name: video.name, |
188 | remoteId: video._id | 207 | remoteId: video.id |
189 | } | 208 | } |
190 | 209 | ||
191 | friends.removeVideoToFriends(params) | 210 | friends.removeVideoToFriends(params) |
@@ -203,7 +222,7 @@ function removeVideo (req, res, next) { | |||
203 | } | 222 | } |
204 | 223 | ||
205 | function searchVideos (req, res, next) { | 224 | function searchVideos (req, res, next) { |
206 | Video.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, | 225 | db.Video.searchAndPopulateAuthorAndPod(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, |
207 | function (err, videosList, videosTotal) { | 226 | function (err, videosList, videosTotal) { |
208 | if (err) return next(err) | 227 | if (err) return next(err) |
209 | 228 | ||
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 @@ | |||
3 | const parallel = require('async/parallel') | 3 | const parallel = require('async/parallel') |
4 | const express = require('express') | 4 | const express = require('express') |
5 | const fs = require('fs') | 5 | const fs = require('fs') |
6 | const mongoose = require('mongoose') | ||
7 | const path = require('path') | 6 | const path = require('path') |
8 | const validator = require('express-validator').validator | 7 | const validator = require('express-validator').validator |
9 | 8 | ||
10 | const constants = require('../initializers/constants') | 9 | const constants = require('../initializers/constants') |
10 | const db = require('../initializers/database') | ||
11 | 11 | ||
12 | const Video = mongoose.model('Video') | ||
13 | const router = express.Router() | 12 | const router = express.Router() |
14 | 13 | ||
15 | const opengraphComment = '<!-- opengraph tags -->' | 14 | const opengraphComment = '<!-- opengraph tags -->' |
@@ -45,14 +44,14 @@ function addOpenGraphTags (htmlStringPage, video) { | |||
45 | if (video.isOwned()) { | 44 | if (video.isOwned()) { |
46 | basePreviewUrlHttp = constants.CONFIG.WEBSERVER.URL | 45 | basePreviewUrlHttp = constants.CONFIG.WEBSERVER.URL |
47 | } else { | 46 | } else { |
48 | basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.podHost | 47 | basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.Author.Pod.host |
49 | } | 48 | } |
50 | 49 | ||
51 | // We fetch the remote preview (bigger than the thumbnail) | 50 | // We fetch the remote preview (bigger than the thumbnail) |
52 | // This should not overhead the remote server since social websites put in a cache the OpenGraph tags | 51 | // This should not overhead the remote server since social websites put in a cache the OpenGraph tags |
53 | // We can't use the thumbnail because these social websites want bigger images (> 200x200 for Facebook for example) | 52 | // We can't use the thumbnail because these social websites want bigger images (> 200x200 for Facebook for example) |
54 | const previewUrl = basePreviewUrlHttp + constants.STATIC_PATHS.PREVIEWS + video.getPreviewName() | 53 | const previewUrl = basePreviewUrlHttp + constants.STATIC_PATHS.PREVIEWS + video.getPreviewName() |
55 | const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video._id | 54 | const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video.id |
56 | 55 | ||
57 | const metaTags = { | 56 | const metaTags = { |
58 | 'og:type': 'video', | 57 | 'og:type': 'video', |
@@ -86,7 +85,7 @@ function generateWatchHtmlPage (req, res, next) { | |||
86 | const videoId = req.params.id | 85 | const videoId = req.params.id |
87 | 86 | ||
88 | // Let Angular application handle errors | 87 | // Let Angular application handle errors |
89 | if (!validator.isMongoId(videoId)) return res.sendFile(indexPath) | 88 | if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath) |
90 | 89 | ||
91 | parallel({ | 90 | parallel({ |
92 | file: function (callback) { | 91 | file: function (callback) { |
@@ -94,7 +93,7 @@ function generateWatchHtmlPage (req, res, next) { | |||
94 | }, | 93 | }, |
95 | 94 | ||
96 | video: function (callback) { | 95 | video: function (callback) { |
97 | Video.load(videoId, callback) | 96 | db.Video.loadAndPopulateAuthorAndPod(videoId, callback) |
98 | } | 97 | } |
99 | }, function (err, results) { | 98 | }, function (err, results) { |
100 | if (err) return next(err) | 99 | if (err) return next(err) |
diff --git a/server/helpers/custom-validators/videos.js b/server/helpers/custom-validators/videos.js index 1a7753265..be8256a80 100644 --- a/server/helpers/custom-validators/videos.js +++ b/server/helpers/custom-validators/videos.js | |||
@@ -13,7 +13,7 @@ const videosValidators = { | |||
13 | isVideoDateValid, | 13 | isVideoDateValid, |
14 | isVideoDescriptionValid, | 14 | isVideoDescriptionValid, |
15 | isVideoDurationValid, | 15 | isVideoDurationValid, |
16 | isVideoMagnetValid, | 16 | isVideoInfoHashValid, |
17 | isVideoNameValid, | 17 | isVideoNameValid, |
18 | isVideoPodHostValid, | 18 | isVideoPodHostValid, |
19 | isVideoTagsValid, | 19 | isVideoTagsValid, |
@@ -28,14 +28,15 @@ function isEachRemoteVideosValid (requests) { | |||
28 | return ( | 28 | return ( |
29 | isRequestTypeAddValid(request.type) && | 29 | isRequestTypeAddValid(request.type) && |
30 | isVideoAuthorValid(video.author) && | 30 | isVideoAuthorValid(video.author) && |
31 | isVideoDateValid(video.createdDate) && | 31 | isVideoDateValid(video.createdAt) && |
32 | isVideoDescriptionValid(video.description) && | 32 | isVideoDescriptionValid(video.description) && |
33 | isVideoDurationValid(video.duration) && | 33 | isVideoDurationValid(video.duration) && |
34 | isVideoMagnetValid(video.magnet) && | 34 | isVideoInfoHashValid(video.infoHash) && |
35 | isVideoNameValid(video.name) && | 35 | isVideoNameValid(video.name) && |
36 | isVideoTagsValid(video.tags) && | 36 | isVideoTagsValid(video.tags) && |
37 | isVideoThumbnail64Valid(video.thumbnailBase64) && | 37 | isVideoThumbnail64Valid(video.thumbnailBase64) && |
38 | isVideoRemoteIdValid(video.remoteId) | 38 | isVideoRemoteIdValid(video.remoteId) && |
39 | isVideoExtnameValid(video.extname) | ||
39 | ) || | 40 | ) || |
40 | ( | 41 | ( |
41 | isRequestTypeRemoveValid(request.type) && | 42 | isRequestTypeRemoveValid(request.type) && |
@@ -61,8 +62,12 @@ function isVideoDurationValid (value) { | |||
61 | return validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) | 62 | return validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) |
62 | } | 63 | } |
63 | 64 | ||
64 | function isVideoMagnetValid (value) { | 65 | function isVideoExtnameValid (value) { |
65 | return validator.isLength(value.infoHash, VIDEOS_CONSTRAINTS_FIELDS.MAGNET.INFO_HASH) | 66 | return VIDEOS_CONSTRAINTS_FIELDS.EXTNAME.indexOf(value) !== -1 |
67 | } | ||
68 | |||
69 | function isVideoInfoHashValid (value) { | ||
70 | return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH) | ||
66 | } | 71 | } |
67 | 72 | ||
68 | function isVideoNameValid (value) { | 73 | function isVideoNameValid (value) { |
@@ -93,7 +98,7 @@ function isVideoThumbnail64Valid (value) { | |||
93 | } | 98 | } |
94 | 99 | ||
95 | function isVideoRemoteIdValid (value) { | 100 | function isVideoRemoteIdValid (value) { |
96 | return validator.isMongoId(value) | 101 | return validator.isUUID(value, 4) |
97 | } | 102 | } |
98 | 103 | ||
99 | // --------------------------------------------------------------------------- | 104 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/logger.js b/server/helpers/logger.js index fcc1789fd..281acedb8 100644 --- a/server/helpers/logger.js +++ b/server/helpers/logger.js | |||
@@ -22,7 +22,8 @@ const logger = new winston.Logger({ | |||
22 | json: true, | 22 | json: true, |
23 | maxsize: 5242880, | 23 | maxsize: 5242880, |
24 | maxFiles: 5, | 24 | maxFiles: 5, |
25 | colorize: false | 25 | colorize: false, |
26 | prettyPrint: true | ||
26 | }), | 27 | }), |
27 | new winston.transports.Console({ | 28 | new winston.transports.Console({ |
28 | level: 'debug', | 29 | level: 'debug', |
@@ -30,7 +31,8 @@ const logger = new winston.Logger({ | |||
30 | handleExceptions: true, | 31 | handleExceptions: true, |
31 | humanReadableUnhandledException: true, | 32 | humanReadableUnhandledException: true, |
32 | json: false, | 33 | json: false, |
33 | colorize: true | 34 | colorize: true, |
35 | prettyPrint: true | ||
34 | }) | 36 | }) |
35 | ], | 37 | ], |
36 | exitOnError: true | 38 | exitOnError: true |
diff --git a/server/initializers/checker.js b/server/initializers/checker.js index aea013fa9..7b402de82 100644 --- a/server/initializers/checker.js +++ b/server/initializers/checker.js | |||
@@ -1,10 +1,8 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const config = require('config') | 3 | const config = require('config') |
4 | const mongoose = require('mongoose') | ||
5 | 4 | ||
6 | const Client = mongoose.model('OAuthClient') | 5 | const db = require('./database') |
7 | const User = mongoose.model('User') | ||
8 | 6 | ||
9 | const checker = { | 7 | const checker = { |
10 | checkConfig, | 8 | checkConfig, |
@@ -44,7 +42,7 @@ function checkMissedConfig () { | |||
44 | } | 42 | } |
45 | 43 | ||
46 | function clientsExist (callback) { | 44 | function clientsExist (callback) { |
47 | Client.list(function (err, clients) { | 45 | db.OAuthClient.list(function (err, clients) { |
48 | if (err) return callback(err) | 46 | if (err) return callback(err) |
49 | 47 | ||
50 | return callback(null, clients.length !== 0) | 48 | return callback(null, clients.length !== 0) |
@@ -52,7 +50,7 @@ function clientsExist (callback) { | |||
52 | } | 50 | } |
53 | 51 | ||
54 | function usersExist (callback) { | 52 | function usersExist (callback) { |
55 | User.countTotal(function (err, totalUsers) { | 53 | db.User.countTotal(function (err, totalUsers) { |
56 | if (err) return callback(err) | 54 | if (err) return callback(err) |
57 | 55 | ||
58 | return callback(null, totalUsers !== 0) | 56 | return callback(null, totalUsers !== 0) |
diff --git a/server/initializers/constants.js b/server/initializers/constants.js index 3ddf87454..1ad0c82a0 100644 --- a/server/initializers/constants.js +++ b/server/initializers/constants.js | |||
@@ -14,13 +14,13 @@ const PAGINATION_COUNT_DEFAULT = 15 | |||
14 | 14 | ||
15 | // Sortable columns per schema | 15 | // Sortable columns per schema |
16 | const SEARCHABLE_COLUMNS = { | 16 | const SEARCHABLE_COLUMNS = { |
17 | VIDEOS: [ 'name', 'magnetUri', 'podHost', 'author', 'tags' ] | 17 | VIDEOS: [ 'name', 'magnetUri', 'host', 'author', 'tags' ] |
18 | } | 18 | } |
19 | 19 | ||
20 | // Sortable columns per schema | 20 | // Sortable columns per schema |
21 | const SORTABLE_COLUMNS = { | 21 | const SORTABLE_COLUMNS = { |
22 | USERS: [ 'username', '-username', 'createdDate', '-createdDate' ], | 22 | USERS: [ 'username', '-username', 'createdAt', '-createdAt' ], |
23 | VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ] | 23 | VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdAt', '-createdAt' ] |
24 | } | 24 | } |
25 | 25 | ||
26 | const OAUTH_LIFETIME = { | 26 | const OAUTH_LIFETIME = { |
@@ -67,9 +67,8 @@ const CONSTRAINTS_FIELDS = { | |||
67 | VIDEOS: { | 67 | VIDEOS: { |
68 | NAME: { min: 3, max: 50 }, // Length | 68 | NAME: { min: 3, max: 50 }, // Length |
69 | DESCRIPTION: { min: 3, max: 250 }, // Length | 69 | DESCRIPTION: { min: 3, max: 250 }, // Length |
70 | MAGNET: { | 70 | EXTNAME: [ '.mp4', '.ogv', '.webm' ], |
71 | INFO_HASH: { min: 10, max: 50 } // Length | 71 | INFO_HASH: { min: 10, max: 50 }, // Length |
72 | }, | ||
73 | DURATION: { min: 1, max: 7200 }, // Number | 72 | DURATION: { min: 1, max: 7200 }, // Number |
74 | TAGS: { min: 1, max: 3 }, // Number of total tags | 73 | TAGS: { min: 1, max: 3 }, // Number of total tags |
75 | TAG: { min: 2, max: 10 }, // Length | 74 | TAG: { min: 2, max: 10 }, // Length |
@@ -88,7 +87,7 @@ const FRIEND_SCORE = { | |||
88 | 87 | ||
89 | // --------------------------------------------------------------------------- | 88 | // --------------------------------------------------------------------------- |
90 | 89 | ||
91 | const MONGO_MIGRATION_SCRIPTS = [ | 90 | const MIGRATION_SCRIPTS = [ |
92 | { | 91 | { |
93 | script: '0005-create-application', | 92 | script: '0005-create-application', |
94 | version: 5 | 93 | version: 5 |
@@ -122,7 +121,7 @@ const MONGO_MIGRATION_SCRIPTS = [ | |||
122 | version: 40 | 121 | version: 40 |
123 | } | 122 | } |
124 | ] | 123 | ] |
125 | const LAST_MONGO_SCHEMA_VERSION = (maxBy(MONGO_MIGRATION_SCRIPTS, 'version'))['version'] | 124 | const LAST_SQL_SCHEMA_VERSION = (maxBy(MIGRATION_SCRIPTS, 'version'))['version'] |
126 | 125 | ||
127 | // --------------------------------------------------------------------------- | 126 | // --------------------------------------------------------------------------- |
128 | 127 | ||
@@ -198,8 +197,8 @@ module.exports = { | |||
198 | CONFIG, | 197 | CONFIG, |
199 | CONSTRAINTS_FIELDS, | 198 | CONSTRAINTS_FIELDS, |
200 | FRIEND_SCORE, | 199 | FRIEND_SCORE, |
201 | LAST_MONGO_SCHEMA_VERSION, | 200 | LAST_SQL_SCHEMA_VERSION, |
202 | MONGO_MIGRATION_SCRIPTS, | 201 | MIGRATION_SCRIPTS, |
203 | OAUTH_LIFETIME, | 202 | OAUTH_LIFETIME, |
204 | PAGINATION_COUNT_DEFAULT, | 203 | PAGINATION_COUNT_DEFAULT, |
205 | PODS_SCORE, | 204 | PODS_SCORE, |
diff --git a/server/initializers/database.js b/server/initializers/database.js index 0564e4e77..cc6f59b63 100644 --- a/server/initializers/database.js +++ b/server/initializers/database.js | |||
@@ -1,36 +1,46 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const mongoose = require('mongoose') | 3 | const fs = require('fs') |
4 | const path = require('path') | ||
5 | const Sequelize = require('sequelize') | ||
4 | 6 | ||
5 | const constants = require('../initializers/constants') | 7 | const constants = require('../initializers/constants') |
6 | const logger = require('../helpers/logger') | 8 | const logger = require('../helpers/logger') |
7 | 9 | ||
8 | // Bootstrap models | 10 | const database = {} |
9 | require('../models/application') | 11 | |
10 | require('../models/oauth-token') | 12 | const sequelize = new Sequelize(constants.CONFIG.DATABASE.DBNAME, 'peertube', 'peertube', { |
11 | require('../models/user') | 13 | dialect: 'postgres', |
12 | require('../models/oauth-client') | 14 | host: constants.CONFIG.DATABASE.HOSTNAME, |
13 | require('../models/video') | 15 | port: constants.CONFIG.DATABASE.PORT |
14 | // Request model needs Video model | 16 | }) |
15 | require('../models/pods') | 17 | |
16 | // Request model needs Pod model | 18 | const modelDirectory = path.join(__dirname, '..', 'models') |
17 | require('../models/request') | 19 | fs.readdir(modelDirectory, function (err, files) { |
18 | 20 | if (err) throw err | |
19 | const database = { | 21 | |
20 | connect: connect | 22 | files.filter(function (file) { |
21 | } | 23 | if (file === 'utils.js') return false |
22 | 24 | ||
23 | function connect () { | 25 | return true |
24 | mongoose.Promise = global.Promise | ||
25 | mongoose.connect('mongodb://' + constants.CONFIG.DATABASE.HOSTNAME + ':' + constants.CONFIG.DATABASE.PORT + '/' + constants.CONFIG.DATABASE.DBNAME) | ||
26 | mongoose.connection.on('error', function () { | ||
27 | throw new Error('Mongodb connection error.') | ||
28 | }) | 26 | }) |
27 | .forEach(function (file) { | ||
28 | const model = sequelize.import(path.join(modelDirectory, file)) | ||
29 | 29 | ||
30 | mongoose.connection.on('open', function () { | 30 | database[model.name] = model |
31 | logger.info('Connected to mongodb.') | ||
32 | }) | 31 | }) |
33 | } | 32 | |
33 | Object.keys(database).forEach(function (modelName) { | ||
34 | if ('associate' in database[modelName]) { | ||
35 | database[modelName].associate(database) | ||
36 | } | ||
37 | }) | ||
38 | |||
39 | logger.info('Database is ready.') | ||
40 | }) | ||
41 | |||
42 | database.sequelize = sequelize | ||
43 | database.Sequelize = Sequelize | ||
34 | 44 | ||
35 | // --------------------------------------------------------------------------- | 45 | // --------------------------------------------------------------------------- |
36 | 46 | ||
diff --git a/server/initializers/installer.js b/server/initializers/installer.js index 1df300ba8..4823bc8c8 100644 --- a/server/initializers/installer.js +++ b/server/initializers/installer.js | |||
@@ -3,26 +3,27 @@ | |||
3 | const config = require('config') | 3 | const config = require('config') |
4 | const each = require('async/each') | 4 | const each = require('async/each') |
5 | const mkdirp = require('mkdirp') | 5 | const mkdirp = require('mkdirp') |
6 | const mongoose = require('mongoose') | ||
7 | const passwordGenerator = require('password-generator') | 6 | const passwordGenerator = require('password-generator') |
8 | const path = require('path') | 7 | const path = require('path') |
9 | const series = require('async/series') | 8 | const series = require('async/series') |
10 | 9 | ||
11 | const checker = require('./checker') | 10 | const checker = require('./checker') |
12 | const constants = require('./constants') | 11 | const constants = require('./constants') |
12 | const db = require('./database') | ||
13 | const logger = require('../helpers/logger') | 13 | const logger = require('../helpers/logger') |
14 | const peertubeCrypto = require('../helpers/peertube-crypto') | 14 | const peertubeCrypto = require('../helpers/peertube-crypto') |
15 | 15 | ||
16 | const Application = mongoose.model('Application') | ||
17 | const Client = mongoose.model('OAuthClient') | ||
18 | const User = mongoose.model('User') | ||
19 | |||
20 | const installer = { | 16 | const installer = { |
21 | installApplication | 17 | installApplication |
22 | } | 18 | } |
23 | 19 | ||
24 | function installApplication (callback) { | 20 | function installApplication (callback) { |
25 | series([ | 21 | series([ |
22 | function createDatabase (callbackAsync) { | ||
23 | db.sequelize.sync().asCallback(callbackAsync) | ||
24 | // db.sequelize.sync({ force: true }).asCallback(callbackAsync) | ||
25 | }, | ||
26 | |||
26 | function createDirectories (callbackAsync) { | 27 | function createDirectories (callbackAsync) { |
27 | createDirectoriesIfNotExist(callbackAsync) | 28 | createDirectoriesIfNotExist(callbackAsync) |
28 | }, | 29 | }, |
@@ -65,16 +66,18 @@ function createOAuthClientIfNotExist (callback) { | |||
65 | 66 | ||
66 | logger.info('Creating a default OAuth Client.') | 67 | logger.info('Creating a default OAuth Client.') |
67 | 68 | ||
68 | const secret = passwordGenerator(32, false) | 69 | const id = passwordGenerator(32, false, /[a-z0-9]/) |
69 | const client = new Client({ | 70 | const secret = passwordGenerator(32, false, /[a-zA-Z0-9]/) |
71 | const client = db.OAuthClient.build({ | ||
72 | clientId: id, | ||
70 | clientSecret: secret, | 73 | clientSecret: secret, |
71 | grants: [ 'password', 'refresh_token' ] | 74 | grants: [ 'password', 'refresh_token' ] |
72 | }) | 75 | }) |
73 | 76 | ||
74 | client.save(function (err, createdClient) { | 77 | client.save().asCallback(function (err, createdClient) { |
75 | if (err) return callback(err) | 78 | if (err) return callback(err) |
76 | 79 | ||
77 | logger.info('Client id: ' + createdClient._id) | 80 | logger.info('Client id: ' + createdClient.clientId) |
78 | logger.info('Client secret: ' + createdClient.clientSecret) | 81 | logger.info('Client secret: ' + createdClient.clientSecret) |
79 | 82 | ||
80 | return callback(null) | 83 | return callback(null) |
@@ -106,21 +109,21 @@ function createOAuthAdminIfNotExist (callback) { | |||
106 | password = passwordGenerator(8, true) | 109 | password = passwordGenerator(8, true) |
107 | } | 110 | } |
108 | 111 | ||
109 | const user = new User({ | 112 | const user = db.User.build({ |
110 | username, | 113 | username, |
111 | password, | 114 | password, |
112 | role | 115 | role |
113 | }) | 116 | }) |
114 | 117 | ||
115 | user.save(function (err, createdUser) { | 118 | user.save().asCallback(function (err, createdUser) { |
116 | if (err) return callback(err) | 119 | if (err) return callback(err) |
117 | 120 | ||
118 | logger.info('Username: ' + username) | 121 | logger.info('Username: ' + username) |
119 | logger.info('User password: ' + password) | 122 | logger.info('User password: ' + password) |
120 | 123 | ||
121 | logger.info('Creating Application collection.') | 124 | logger.info('Creating Application collection.') |
122 | const application = new Application({ mongoSchemaVersion: constants.LAST_MONGO_SCHEMA_VERSION }) | 125 | const application = db.Application.build({ sqlSchemaVersion: constants.LAST_SQL_SCHEMA_VERSION }) |
123 | application.save(callback) | 126 | application.save().asCallback(callback) |
124 | }) | 127 | }) |
125 | }) | 128 | }) |
126 | } | 129 | } |
diff --git a/server/initializers/migrator.js b/server/initializers/migrator.js index 6b31d994f..9e5350e60 100644 --- a/server/initializers/migrator.js +++ b/server/initializers/migrator.js | |||
@@ -1,24 +1,22 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const eachSeries = require('async/eachSeries') | 3 | const eachSeries = require('async/eachSeries') |
4 | const mongoose = require('mongoose') | ||
5 | const path = require('path') | 4 | const path = require('path') |
6 | 5 | ||
7 | const constants = require('./constants') | 6 | const constants = require('./constants') |
7 | const db = require('./database') | ||
8 | const logger = require('../helpers/logger') | 8 | const logger = require('../helpers/logger') |
9 | 9 | ||
10 | const Application = mongoose.model('Application') | ||
11 | |||
12 | const migrator = { | 10 | const migrator = { |
13 | migrate: migrate | 11 | migrate: migrate |
14 | } | 12 | } |
15 | 13 | ||
16 | function migrate (callback) { | 14 | function migrate (callback) { |
17 | Application.loadMongoSchemaVersion(function (err, actualVersion) { | 15 | db.Application.loadSqlSchemaVersion(function (err, actualVersion) { |
18 | if (err) return callback(err) | 16 | if (err) return callback(err) |
19 | 17 | ||
20 | // If there are a new mongo schemas | 18 | // If there are a new mongo schemas |
21 | if (!actualVersion || actualVersion < constants.LAST_MONGO_SCHEMA_VERSION) { | 19 | if (!actualVersion || actualVersion < constants.LAST_SQL_SCHEMA_VERSION) { |
22 | logger.info('Begin migrations.') | 20 | logger.info('Begin migrations.') |
23 | 21 | ||
24 | eachSeries(constants.MONGO_MIGRATION_SCRIPTS, function (entity, callbackEach) { | 22 | eachSeries(constants.MONGO_MIGRATION_SCRIPTS, function (entity, callbackEach) { |
@@ -36,12 +34,12 @@ function migrate (callback) { | |||
36 | if (err) return callbackEach(err) | 34 | if (err) return callbackEach(err) |
37 | 35 | ||
38 | // Update the new mongo version schema | 36 | // Update the new mongo version schema |
39 | Application.updateMongoSchemaVersion(versionScript, callbackEach) | 37 | db.Application.updateSqlSchemaVersion(versionScript, callbackEach) |
40 | }) | 38 | }) |
41 | }, function (err) { | 39 | }, function (err) { |
42 | if (err) return callback(err) | 40 | if (err) return callback(err) |
43 | 41 | ||
44 | logger.info('Migrations finished. New mongo version schema: %s', constants.LAST_MONGO_SCHEMA_VERSION) | 42 | logger.info('Migrations finished. New SQL version schema: %s', constants.LAST_SQL_SCHEMA_VERSION) |
45 | return callback(null) | 43 | return callback(null) |
46 | }) | 44 | }) |
47 | } else { | 45 | } else { |
diff --git a/server/lib/friends.js b/server/lib/friends.js index eaea040ca..3ed29f651 100644 --- a/server/lib/friends.js +++ b/server/lib/friends.js | |||
@@ -4,18 +4,14 @@ const each = require('async/each') | |||
4 | const eachLimit = require('async/eachLimit') | 4 | const eachLimit = require('async/eachLimit') |
5 | const eachSeries = require('async/eachSeries') | 5 | const eachSeries = require('async/eachSeries') |
6 | const fs = require('fs') | 6 | const fs = require('fs') |
7 | const mongoose = require('mongoose') | ||
8 | const request = require('request') | 7 | const request = require('request') |
9 | const waterfall = require('async/waterfall') | 8 | const waterfall = require('async/waterfall') |
10 | 9 | ||
11 | const constants = require('../initializers/constants') | 10 | const constants = require('../initializers/constants') |
11 | const db = require('../initializers/database') | ||
12 | const logger = require('../helpers/logger') | 12 | const logger = require('../helpers/logger') |
13 | const requests = require('../helpers/requests') | 13 | const requests = require('../helpers/requests') |
14 | 14 | ||
15 | const Pod = mongoose.model('Pod') | ||
16 | const Request = mongoose.model('Request') | ||
17 | const Video = mongoose.model('Video') | ||
18 | |||
19 | const friends = { | 15 | const friends = { |
20 | addVideoToFriends, | 16 | addVideoToFriends, |
21 | hasFriends, | 17 | hasFriends, |
@@ -31,7 +27,7 @@ function addVideoToFriends (video) { | |||
31 | } | 27 | } |
32 | 28 | ||
33 | function hasFriends (callback) { | 29 | function hasFriends (callback) { |
34 | Pod.countAll(function (err, count) { | 30 | db.Pod.countAll(function (err, count) { |
35 | if (err) return callback(err) | 31 | if (err) return callback(err) |
36 | 32 | ||
37 | const hasFriends = (count !== 0) | 33 | const hasFriends = (count !== 0) |
@@ -69,13 +65,13 @@ function makeFriends (hosts, callback) { | |||
69 | 65 | ||
70 | function quitFriends (callback) { | 66 | function quitFriends (callback) { |
71 | // Stop pool requests | 67 | // Stop pool requests |
72 | Request.deactivate() | 68 | db.Request.deactivate() |
73 | // Flush pool requests | 69 | // Flush pool requests |
74 | Request.flush() | 70 | db.Request.flush() |
75 | 71 | ||
76 | waterfall([ | 72 | waterfall([ |
77 | function getPodsList (callbackAsync) { | 73 | function getPodsList (callbackAsync) { |
78 | return Pod.list(callbackAsync) | 74 | return db.Pod.list(callbackAsync) |
79 | }, | 75 | }, |
80 | 76 | ||
81 | function announceIQuitMyFriends (pods, callbackAsync) { | 77 | function announceIQuitMyFriends (pods, callbackAsync) { |
@@ -103,12 +99,12 @@ function quitFriends (callback) { | |||
103 | 99 | ||
104 | function removePodsFromDB (pods, callbackAsync) { | 100 | function removePodsFromDB (pods, callbackAsync) { |
105 | each(pods, function (pod, callbackEach) { | 101 | each(pods, function (pod, callbackEach) { |
106 | pod.remove(callbackEach) | 102 | pod.destroy().asCallback(callbackEach) |
107 | }, callbackAsync) | 103 | }, callbackAsync) |
108 | } | 104 | } |
109 | ], function (err) { | 105 | ], function (err) { |
110 | // Don't forget to re activate the scheduler, even if there was an error | 106 | // Don't forget to re activate the scheduler, even if there was an error |
111 | Request.activate() | 107 | db.Request.activate() |
112 | 108 | ||
113 | if (err) return callback(err) | 109 | if (err) return callback(err) |
114 | 110 | ||
@@ -122,7 +118,7 @@ function removeVideoToFriends (videoParams) { | |||
122 | } | 118 | } |
123 | 119 | ||
124 | function sendOwnedVideosToPod (podId) { | 120 | function sendOwnedVideosToPod (podId) { |
125 | Video.listOwned(function (err, videosList) { | 121 | db.Video.listOwnedAndPopulateAuthor(function (err, videosList) { |
126 | if (err) { | 122 | if (err) { |
127 | logger.error('Cannot get the list of videos we own.') | 123 | logger.error('Cannot get the list of videos we own.') |
128 | return | 124 | return |
@@ -200,9 +196,9 @@ function getForeignPodsList (host, callback) { | |||
200 | 196 | ||
201 | function makeRequestsToWinningPods (cert, podsList, callback) { | 197 | function makeRequestsToWinningPods (cert, podsList, callback) { |
202 | // Stop pool requests | 198 | // Stop pool requests |
203 | Request.deactivate() | 199 | db.Request.deactivate() |
204 | // Flush pool requests | 200 | // Flush pool requests |
205 | Request.forceSend() | 201 | db.Request.forceSend() |
206 | 202 | ||
207 | eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { | 203 | eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { |
208 | const params = { | 204 | const params = { |
@@ -222,8 +218,8 @@ function makeRequestsToWinningPods (cert, podsList, callback) { | |||
222 | } | 218 | } |
223 | 219 | ||
224 | if (res.statusCode === 200) { | 220 | if (res.statusCode === 200) { |
225 | const podObj = new Pod({ host: pod.host, publicKey: body.cert }) | 221 | const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert }) |
226 | podObj.save(function (err, podCreated) { | 222 | podObj.save().asCallback(function (err, podCreated) { |
227 | if (err) { | 223 | if (err) { |
228 | logger.error('Cannot add friend %s pod.', pod.host, { error: err }) | 224 | logger.error('Cannot add friend %s pod.', pod.host, { error: err }) |
229 | return callbackEach() | 225 | return callbackEach() |
@@ -242,28 +238,57 @@ function makeRequestsToWinningPods (cert, podsList, callback) { | |||
242 | }, function endRequests () { | 238 | }, function endRequests () { |
243 | // Final callback, we've ended all the requests | 239 | // Final callback, we've ended all the requests |
244 | // Now we made new friends, we can re activate the pool of requests | 240 | // Now we made new friends, we can re activate the pool of requests |
245 | Request.activate() | 241 | db.Request.activate() |
246 | 242 | ||
247 | logger.debug('makeRequestsToWinningPods finished.') | 243 | logger.debug('makeRequestsToWinningPods finished.') |
248 | return callback() | 244 | return callback() |
249 | }) | 245 | }) |
250 | } | 246 | } |
251 | 247 | ||
248 | // Wrapper that populate "to" argument with all our friends if it is not specified | ||
252 | function createRequest (type, endpoint, data, to) { | 249 | function createRequest (type, endpoint, data, to) { |
253 | const req = new Request({ | 250 | if (to) return _createRequest(type, endpoint, data, to) |
251 | |||
252 | // If the "to" pods is not specified, we send the request to all our friends | ||
253 | db.Pod.listAllIds(function (err, podIds) { | ||
254 | if (err) { | ||
255 | logger.error('Cannot get pod ids', { error: err }) | ||
256 | return | ||
257 | } | ||
258 | |||
259 | return _createRequest(type, endpoint, data, podIds) | ||
260 | }) | ||
261 | } | ||
262 | |||
263 | function _createRequest (type, endpoint, data, to) { | ||
264 | const pods = [] | ||
265 | |||
266 | // If there are no destination pods abort | ||
267 | if (to.length === 0) return | ||
268 | |||
269 | to.forEach(function (toPod) { | ||
270 | pods.push(db.Pod.build({ id: toPod })) | ||
271 | }) | ||
272 | |||
273 | const createQuery = { | ||
254 | endpoint, | 274 | endpoint, |
255 | request: { | 275 | request: { |
256 | type: type, | 276 | type: type, |
257 | data: data | 277 | data: data |
258 | } | 278 | } |
259 | }) | ||
260 | |||
261 | if (to) { | ||
262 | req.to = to | ||
263 | } | 279 | } |
264 | 280 | ||
265 | req.save(function (err) { | 281 | // We run in transaction to keep coherency between Request and RequestToPod tables |
266 | if (err) logger.error('Cannot save the request.', { error: err }) | 282 | db.sequelize.transaction(function (t) { |
283 | const dbRequestOptions = { | ||
284 | transaction: t | ||
285 | } | ||
286 | |||
287 | return db.Request.create(createQuery, dbRequestOptions).then(function (request) { | ||
288 | return request.setPods(pods, dbRequestOptions) | ||
289 | }) | ||
290 | }).asCallback(function (err) { | ||
291 | if (err) logger.error('Error in createRequest transaction.', { error: err }) | ||
267 | }) | 292 | }) |
268 | } | 293 | } |
269 | 294 | ||
diff --git a/server/lib/oauth-model.js b/server/lib/oauth-model.js index d011c4b72..1c12f1b14 100644 --- a/server/lib/oauth-model.js +++ b/server/lib/oauth-model.js | |||
@@ -1,11 +1,6 @@ | |||
1 | const mongoose = require('mongoose') | 1 | const db = require('../initializers/database') |
2 | |||
3 | const logger = require('../helpers/logger') | 2 | const logger = require('../helpers/logger') |
4 | 3 | ||
5 | const OAuthClient = mongoose.model('OAuthClient') | ||
6 | const OAuthToken = mongoose.model('OAuthToken') | ||
7 | const User = mongoose.model('User') | ||
8 | |||
9 | // See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications | 4 | // See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications |
10 | const OAuthModel = { | 5 | const OAuthModel = { |
11 | getAccessToken, | 6 | getAccessToken, |
@@ -21,27 +16,25 @@ const OAuthModel = { | |||
21 | function getAccessToken (bearerToken) { | 16 | function getAccessToken (bearerToken) { |
22 | logger.debug('Getting access token (bearerToken: ' + bearerToken + ').') | 17 | logger.debug('Getting access token (bearerToken: ' + bearerToken + ').') |
23 | 18 | ||
24 | return OAuthToken.getByTokenAndPopulateUser(bearerToken) | 19 | return db.OAuthToken.getByTokenAndPopulateUser(bearerToken) |
25 | } | 20 | } |
26 | 21 | ||
27 | function getClient (clientId, clientSecret) { | 22 | function getClient (clientId, clientSecret) { |
28 | logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').') | 23 | logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').') |
29 | 24 | ||
30 | // TODO req validator | 25 | return db.OAuthClient.getByIdAndSecret(clientId, clientSecret) |
31 | const mongoId = new mongoose.mongo.ObjectID(clientId) | ||
32 | return OAuthClient.getByIdAndSecret(mongoId, clientSecret) | ||
33 | } | 26 | } |
34 | 27 | ||
35 | function getRefreshToken (refreshToken) { | 28 | function getRefreshToken (refreshToken) { |
36 | logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').') | 29 | logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').') |
37 | 30 | ||
38 | return OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken) | 31 | return db.OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken) |
39 | } | 32 | } |
40 | 33 | ||
41 | function getUser (username, password) { | 34 | function getUser (username, password) { |
42 | logger.debug('Getting User (username: ' + username + ', password: ' + password + ').') | 35 | logger.debug('Getting User (username: ' + username + ', password: ' + password + ').') |
43 | 36 | ||
44 | return User.getByUsername(username).then(function (user) { | 37 | return db.User.getByUsername(username).then(function (user) { |
45 | if (!user) return null | 38 | if (!user) return null |
46 | 39 | ||
47 | // We need to return a promise | 40 | // We need to return a promise |
@@ -60,8 +53,8 @@ function getUser (username, password) { | |||
60 | } | 53 | } |
61 | 54 | ||
62 | function revokeToken (token) { | 55 | function revokeToken (token) { |
63 | return OAuthToken.getByRefreshTokenAndPopulateUser(token.refreshToken).then(function (tokenDB) { | 56 | return db.OAuthToken.getByRefreshTokenAndPopulateUser(token.refreshToken).then(function (tokenDB) { |
64 | if (tokenDB) tokenDB.remove() | 57 | if (tokenDB) tokenDB.destroy() |
65 | 58 | ||
66 | /* | 59 | /* |
67 | * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js | 60 | * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js |
@@ -80,18 +73,19 @@ function revokeToken (token) { | |||
80 | function saveToken (token, client, user) { | 73 | function saveToken (token, client, user) { |
81 | logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.') | 74 | logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.') |
82 | 75 | ||
83 | const tokenObj = new OAuthToken({ | 76 | const tokenToCreate = { |
84 | accessToken: token.accessToken, | 77 | accessToken: token.accessToken, |
85 | accessTokenExpiresAt: token.accessTokenExpiresAt, | 78 | accessTokenExpiresAt: token.accessTokenExpiresAt, |
86 | client: client.id, | ||
87 | refreshToken: token.refreshToken, | 79 | refreshToken: token.refreshToken, |
88 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, | 80 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, |
89 | user: user.id | 81 | oAuthClientId: client.id, |
90 | }) | 82 | userId: user.id |
83 | } | ||
91 | 84 | ||
92 | return tokenObj.save().then(function (tokenCreated) { | 85 | return db.OAuthToken.create(tokenToCreate).then(function (tokenCreated) { |
93 | tokenCreated.client = client | 86 | tokenCreated.client = client |
94 | tokenCreated.user = user | 87 | tokenCreated.user = user |
88 | |||
95 | return tokenCreated | 89 | return tokenCreated |
96 | }).catch(function (err) { | 90 | }).catch(function (err) { |
97 | throw err | 91 | throw err |
diff --git a/server/middlewares/pods.js b/server/middlewares/pods.js index 487ea1259..e38fb341d 100644 --- a/server/middlewares/pods.js +++ b/server/middlewares/pods.js | |||
@@ -44,7 +44,6 @@ module.exports = podsMiddleware | |||
44 | function getHostWithPort (host) { | 44 | function getHostWithPort (host) { |
45 | const splitted = host.split(':') | 45 | const splitted = host.split(':') |
46 | 46 | ||
47 | console.log(splitted) | ||
48 | // The port was not specified | 47 | // The port was not specified |
49 | if (splitted.length === 1) { | 48 | if (splitted.length === 1) { |
50 | if (constants.REMOTE_SCHEME.HTTP === 'https') return host + ':443' | 49 | if (constants.REMOTE_SCHEME.HTTP === 'https') return host + ':443' |
diff --git a/server/middlewares/secure.js b/server/middlewares/secure.js index ee836beed..b7b4cdfb4 100644 --- a/server/middlewares/secure.js +++ b/server/middlewares/secure.js | |||
@@ -1,18 +1,16 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const db = require('../initializers/database') | ||
3 | const logger = require('../helpers/logger') | 4 | const logger = require('../helpers/logger') |
4 | const mongoose = require('mongoose') | ||
5 | const peertubeCrypto = require('../helpers/peertube-crypto') | 5 | const peertubeCrypto = require('../helpers/peertube-crypto') |
6 | 6 | ||
7 | const Pod = mongoose.model('Pod') | ||
8 | |||
9 | const secureMiddleware = { | 7 | const secureMiddleware = { |
10 | checkSignature | 8 | checkSignature |
11 | } | 9 | } |
12 | 10 | ||
13 | function checkSignature (req, res, next) { | 11 | function checkSignature (req, res, next) { |
14 | const host = req.body.signature.host | 12 | const host = req.body.signature.host |
15 | Pod.loadByHost(host, function (err, pod) { | 13 | db.Pod.loadByHost(host, function (err, pod) { |
16 | if (err) { | 14 | if (err) { |
17 | logger.error('Cannot get signed host in body.', { error: err }) | 15 | logger.error('Cannot get signed host in body.', { error: err }) |
18 | return res.sendStatus(500) | 16 | return res.sendStatus(500) |
diff --git a/server/middlewares/sort.js b/server/middlewares/sort.js index f0b7274eb..477e10571 100644 --- a/server/middlewares/sort.js +++ b/server/middlewares/sort.js | |||
@@ -6,13 +6,13 @@ const sortMiddleware = { | |||
6 | } | 6 | } |
7 | 7 | ||
8 | function setUsersSort (req, res, next) { | 8 | function setUsersSort (req, res, next) { |
9 | if (!req.query.sort) req.query.sort = '-createdDate' | 9 | if (!req.query.sort) req.query.sort = '-createdAt' |
10 | 10 | ||
11 | return next() | 11 | return next() |
12 | } | 12 | } |
13 | 13 | ||
14 | function setVideosSort (req, res, next) { | 14 | function setVideosSort (req, res, next) { |
15 | if (!req.query.sort) req.query.sort = '-createdDate' | 15 | if (!req.query.sort) req.query.sort = '-createdAt' |
16 | 16 | ||
17 | return next() | 17 | return next() |
18 | } | 18 | } |
diff --git a/server/middlewares/validators/users.js b/server/middlewares/validators/users.js index 02e4f34cb..0629550bc 100644 --- a/server/middlewares/validators/users.js +++ b/server/middlewares/validators/users.js | |||
@@ -1,12 +1,9 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const mongoose = require('mongoose') | ||
4 | |||
5 | const checkErrors = require('./utils').checkErrors | 3 | const checkErrors = require('./utils').checkErrors |
4 | const db = require('../../initializers/database') | ||
6 | const logger = require('../../helpers/logger') | 5 | const logger = require('../../helpers/logger') |
7 | 6 | ||
8 | const User = mongoose.model('User') | ||
9 | |||
10 | const validatorsUsers = { | 7 | const validatorsUsers = { |
11 | usersAdd, | 8 | usersAdd, |
12 | usersRemove, | 9 | usersRemove, |
@@ -20,7 +17,7 @@ function usersAdd (req, res, next) { | |||
20 | logger.debug('Checking usersAdd parameters', { parameters: req.body }) | 17 | logger.debug('Checking usersAdd parameters', { parameters: req.body }) |
21 | 18 | ||
22 | checkErrors(req, res, function () { | 19 | checkErrors(req, res, function () { |
23 | User.loadByUsername(req.body.username, function (err, user) { | 20 | db.User.loadByUsername(req.body.username, function (err, user) { |
24 | if (err) { | 21 | if (err) { |
25 | logger.error('Error in usersAdd request validator.', { error: err }) | 22 | logger.error('Error in usersAdd request validator.', { error: err }) |
26 | return res.sendStatus(500) | 23 | return res.sendStatus(500) |
@@ -34,12 +31,12 @@ function usersAdd (req, res, next) { | |||
34 | } | 31 | } |
35 | 32 | ||
36 | function usersRemove (req, res, next) { | 33 | function usersRemove (req, res, next) { |
37 | req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() | 34 | req.checkParams('id', 'Should have a valid id').notEmpty().isInt() |
38 | 35 | ||
39 | logger.debug('Checking usersRemove parameters', { parameters: req.params }) | 36 | logger.debug('Checking usersRemove parameters', { parameters: req.params }) |
40 | 37 | ||
41 | checkErrors(req, res, function () { | 38 | checkErrors(req, res, function () { |
42 | User.loadById(req.params.id, function (err, user) { | 39 | db.User.loadById(req.params.id, function (err, user) { |
43 | if (err) { | 40 | if (err) { |
44 | logger.error('Error in usersRemove request validator.', { error: err }) | 41 | logger.error('Error in usersRemove request validator.', { error: err }) |
45 | return res.sendStatus(500) | 42 | return res.sendStatus(500) |
@@ -55,7 +52,7 @@ function usersRemove (req, res, next) { | |||
55 | } | 52 | } |
56 | 53 | ||
57 | function usersUpdate (req, res, next) { | 54 | function usersUpdate (req, res, next) { |
58 | req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() | 55 | req.checkParams('id', 'Should have a valid id').notEmpty().isInt() |
59 | // Add old password verification | 56 | // Add old password verification |
60 | req.checkBody('password', 'Should have a valid password').isUserPasswordValid() | 57 | req.checkBody('password', 'Should have a valid password').isUserPasswordValid() |
61 | 58 | ||
diff --git a/server/middlewares/validators/videos.js b/server/middlewares/validators/videos.js index 76e943e77..7e90ca047 100644 --- a/server/middlewares/validators/videos.js +++ b/server/middlewares/validators/videos.js | |||
@@ -1,14 +1,11 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const mongoose = require('mongoose') | ||
4 | |||
5 | const checkErrors = require('./utils').checkErrors | 3 | const checkErrors = require('./utils').checkErrors |
6 | const constants = require('../../initializers/constants') | 4 | const constants = require('../../initializers/constants') |
7 | const customVideosValidators = require('../../helpers/custom-validators').videos | 5 | const customVideosValidators = require('../../helpers/custom-validators').videos |
6 | const db = require('../../initializers/database') | ||
8 | const logger = require('../../helpers/logger') | 7 | const logger = require('../../helpers/logger') |
9 | 8 | ||
10 | const Video = mongoose.model('Video') | ||
11 | |||
12 | const validatorsVideos = { | 9 | const validatorsVideos = { |
13 | videosAdd, | 10 | videosAdd, |
14 | videosGet, | 11 | videosGet, |
@@ -29,7 +26,7 @@ function videosAdd (req, res, next) { | |||
29 | checkErrors(req, res, function () { | 26 | checkErrors(req, res, function () { |
30 | const videoFile = req.files.videofile[0] | 27 | const videoFile = req.files.videofile[0] |
31 | 28 | ||
32 | Video.getDurationFromFile(videoFile.path, function (err, duration) { | 29 | db.Video.getDurationFromFile(videoFile.path, function (err, duration) { |
33 | if (err) { | 30 | if (err) { |
34 | return res.status(400).send('Cannot retrieve metadata of the file.') | 31 | return res.status(400).send('Cannot retrieve metadata of the file.') |
35 | } | 32 | } |
@@ -45,12 +42,12 @@ function videosAdd (req, res, next) { | |||
45 | } | 42 | } |
46 | 43 | ||
47 | function videosGet (req, res, next) { | 44 | function videosGet (req, res, next) { |
48 | req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() | 45 | req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) |
49 | 46 | ||
50 | logger.debug('Checking videosGet parameters', { parameters: req.params }) | 47 | logger.debug('Checking videosGet parameters', { parameters: req.params }) |
51 | 48 | ||
52 | checkErrors(req, res, function () { | 49 | checkErrors(req, res, function () { |
53 | Video.load(req.params.id, function (err, video) { | 50 | db.Video.load(req.params.id, function (err, video) { |
54 | if (err) { | 51 | if (err) { |
55 | logger.error('Error in videosGet request validator.', { error: err }) | 52 | logger.error('Error in videosGet request validator.', { error: err }) |
56 | return res.sendStatus(500) | 53 | return res.sendStatus(500) |
@@ -64,12 +61,12 @@ function videosGet (req, res, next) { | |||
64 | } | 61 | } |
65 | 62 | ||
66 | function videosRemove (req, res, next) { | 63 | function videosRemove (req, res, next) { |
67 | req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() | 64 | req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) |
68 | 65 | ||
69 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) | 66 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) |
70 | 67 | ||
71 | checkErrors(req, res, function () { | 68 | checkErrors(req, res, function () { |
72 | Video.load(req.params.id, function (err, video) { | 69 | db.Video.loadAndPopulateAuthor(req.params.id, function (err, video) { |
73 | if (err) { | 70 | if (err) { |
74 | logger.error('Error in videosRemove request validator.', { error: err }) | 71 | logger.error('Error in videosRemove request validator.', { error: err }) |
75 | return res.sendStatus(500) | 72 | return res.sendStatus(500) |
@@ -77,7 +74,7 @@ function videosRemove (req, res, next) { | |||
77 | 74 | ||
78 | if (!video) return res.status(404).send('Video not found') | 75 | if (!video) return res.status(404).send('Video not found') |
79 | else if (video.isOwned() === false) return res.status(403).send('Cannot remove video of another pod') | 76 | else if (video.isOwned() === false) return res.status(403).send('Cannot remove video of another pod') |
80 | else if (video.author !== res.locals.oauth.token.user.username) return res.status(403).send('Cannot remove video of another user') | 77 | else if (video.Author.name !== res.locals.oauth.token.user.username) return res.status(403).send('Cannot remove video of another user') |
81 | 78 | ||
82 | next() | 79 | next() |
83 | }) | 80 | }) |
diff --git a/server/models/application.js b/server/models/application.js index 452ac4283..ec1d7b122 100644 --- a/server/models/application.js +++ b/server/models/application.js | |||
@@ -1,31 +1,36 @@ | |||
1 | const mongoose = require('mongoose') | 1 | module.exports = function (sequelize, DataTypes) { |
2 | const Application = sequelize.define('Application', | ||
3 | { | ||
4 | sqlSchemaVersion: { | ||
5 | type: DataTypes.INTEGER, | ||
6 | defaultValue: 0 | ||
7 | } | ||
8 | }, | ||
9 | { | ||
10 | classMethods: { | ||
11 | loadSqlSchemaVersion, | ||
12 | updateSqlSchemaVersion | ||
13 | } | ||
14 | } | ||
15 | ) | ||
16 | |||
17 | return Application | ||
18 | } | ||
2 | 19 | ||
3 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
4 | 21 | ||
5 | const ApplicationSchema = mongoose.Schema({ | 22 | function loadSqlSchemaVersion (callback) { |
6 | mongoSchemaVersion: { | 23 | const query = { |
7 | type: Number, | 24 | attributes: [ 'sqlSchemaVersion' ] |
8 | default: 0 | ||
9 | } | 25 | } |
10 | }) | ||
11 | |||
12 | ApplicationSchema.statics = { | ||
13 | loadMongoSchemaVersion, | ||
14 | updateMongoSchemaVersion | ||
15 | } | ||
16 | |||
17 | mongoose.model('Application', ApplicationSchema) | ||
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | 26 | ||
21 | function loadMongoSchemaVersion (callback) { | 27 | return this.findOne(query).asCallback(function (err, data) { |
22 | return this.findOne({}, { mongoSchemaVersion: 1 }, function (err, data) { | 28 | const version = data ? data.sqlSchemaVersion : 0 |
23 | const version = data ? data.mongoSchemaVersion : 0 | ||
24 | 29 | ||
25 | return callback(err, version) | 30 | return callback(err, version) |
26 | }) | 31 | }) |
27 | } | 32 | } |
28 | 33 | ||
29 | function updateMongoSchemaVersion (newVersion, callback) { | 34 | function updateSqlSchemaVersion (newVersion, callback) { |
30 | return this.update({}, { mongoSchemaVersion: newVersion }, callback) | 35 | return this.update({ sqlSchemaVersion: newVersion }).asCallback(callback) |
31 | } | 36 | } |
diff --git a/server/models/author.js b/server/models/author.js new file mode 100644 index 000000000..493c2ca11 --- /dev/null +++ b/server/models/author.js | |||
@@ -0,0 +1,28 @@ | |||
1 | module.exports = function (sequelize, DataTypes) { | ||
2 | const Author = sequelize.define('Author', | ||
3 | { | ||
4 | name: { | ||
5 | type: DataTypes.STRING | ||
6 | } | ||
7 | }, | ||
8 | { | ||
9 | classMethods: { | ||
10 | associate | ||
11 | } | ||
12 | } | ||
13 | ) | ||
14 | |||
15 | return Author | ||
16 | } | ||
17 | |||
18 | // --------------------------------------------------------------------------- | ||
19 | |||
20 | function associate (models) { | ||
21 | this.belongsTo(models.Pod, { | ||
22 | foreignKey: { | ||
23 | name: 'podId', | ||
24 | allowNull: true | ||
25 | }, | ||
26 | onDelete: 'cascade' | ||
27 | }) | ||
28 | } | ||
diff --git a/server/models/oauth-client.js b/server/models/oauth-client.js index a1aefa985..15118591a 100644 --- a/server/models/oauth-client.js +++ b/server/models/oauth-client.js | |||
@@ -1,33 +1,63 @@ | |||
1 | const mongoose = require('mongoose') | 1 | module.exports = function (sequelize, DataTypes) { |
2 | 2 | const OAuthClient = sequelize.define('OAuthClient', | |
3 | // --------------------------------------------------------------------------- | 3 | { |
4 | 4 | clientId: { | |
5 | const OAuthClientSchema = mongoose.Schema({ | 5 | type: DataTypes.STRING |
6 | clientSecret: String, | 6 | }, |
7 | grants: Array, | 7 | clientSecret: { |
8 | redirectUris: Array | 8 | type: DataTypes.STRING |
9 | }) | 9 | }, |
10 | 10 | grants: { | |
11 | OAuthClientSchema.path('clientSecret').required(true) | 11 | type: DataTypes.ARRAY(DataTypes.STRING) |
12 | 12 | }, | |
13 | OAuthClientSchema.statics = { | 13 | redirectUris: { |
14 | getByIdAndSecret, | 14 | type: DataTypes.ARRAY(DataTypes.STRING) |
15 | list, | 15 | } |
16 | loadFirstClient | 16 | }, |
17 | { | ||
18 | classMethods: { | ||
19 | associate, | ||
20 | |||
21 | getByIdAndSecret, | ||
22 | list, | ||
23 | loadFirstClient | ||
24 | } | ||
25 | } | ||
26 | ) | ||
27 | |||
28 | return OAuthClient | ||
17 | } | 29 | } |
18 | 30 | ||
19 | mongoose.model('OAuthClient', OAuthClientSchema) | 31 | // TODO: validation |
32 | // OAuthClientSchema.path('clientSecret').required(true) | ||
20 | 33 | ||
21 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
22 | 35 | ||
36 | function associate (models) { | ||
37 | this.hasMany(models.OAuthToken, { | ||
38 | foreignKey: { | ||
39 | name: 'oAuthClientId', | ||
40 | allowNull: false | ||
41 | }, | ||
42 | onDelete: 'cascade' | ||
43 | }) | ||
44 | } | ||
45 | |||
23 | function list (callback) { | 46 | function list (callback) { |
24 | return this.find(callback) | 47 | return this.findAll().asCallback(callback) |
25 | } | 48 | } |
26 | 49 | ||
27 | function loadFirstClient (callback) { | 50 | function loadFirstClient (callback) { |
28 | return this.findOne({}, callback) | 51 | return this.findOne().asCallback(callback) |
29 | } | 52 | } |
30 | 53 | ||
31 | function getByIdAndSecret (id, clientSecret) { | 54 | function getByIdAndSecret (clientId, clientSecret) { |
32 | return this.findOne({ _id: id, clientSecret: clientSecret }).exec() | 55 | const query = { |
56 | where: { | ||
57 | clientId: clientId, | ||
58 | clientSecret: clientSecret | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return this.findOne(query) | ||
33 | } | 63 | } |
diff --git a/server/models/oauth-token.js b/server/models/oauth-token.js index aff73bfb1..c9108bf95 100644 --- a/server/models/oauth-token.js +++ b/server/models/oauth-token.js | |||
@@ -1,42 +1,71 @@ | |||
1 | const mongoose = require('mongoose') | ||
2 | |||
3 | const logger = require('../helpers/logger') | 1 | const logger = require('../helpers/logger') |
4 | 2 | ||
5 | // --------------------------------------------------------------------------- | 3 | // --------------------------------------------------------------------------- |
6 | 4 | ||
7 | const OAuthTokenSchema = mongoose.Schema({ | 5 | module.exports = function (sequelize, DataTypes) { |
8 | accessToken: String, | 6 | const OAuthToken = sequelize.define('OAuthToken', |
9 | accessTokenExpiresAt: Date, | 7 | { |
10 | client: { type: mongoose.Schema.Types.ObjectId, ref: 'OAuthClient' }, | 8 | accessToken: { |
11 | refreshToken: String, | 9 | type: DataTypes.STRING |
12 | refreshTokenExpiresAt: Date, | 10 | }, |
13 | user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } | 11 | accessTokenExpiresAt: { |
14 | }) | 12 | type: DataTypes.DATE |
15 | 13 | }, | |
16 | OAuthTokenSchema.path('accessToken').required(true) | 14 | refreshToken: { |
17 | OAuthTokenSchema.path('client').required(true) | 15 | type: DataTypes.STRING |
18 | OAuthTokenSchema.path('user').required(true) | 16 | }, |
19 | 17 | refreshTokenExpiresAt: { | |
20 | OAuthTokenSchema.statics = { | 18 | type: DataTypes.DATE |
21 | getByRefreshTokenAndPopulateClient, | 19 | } |
22 | getByTokenAndPopulateUser, | 20 | }, |
23 | getByRefreshTokenAndPopulateUser, | 21 | { |
24 | removeByUserId | 22 | classMethods: { |
23 | associate, | ||
24 | |||
25 | getByRefreshTokenAndPopulateClient, | ||
26 | getByTokenAndPopulateUser, | ||
27 | getByRefreshTokenAndPopulateUser, | ||
28 | removeByUserId | ||
29 | } | ||
30 | } | ||
31 | ) | ||
32 | |||
33 | return OAuthToken | ||
25 | } | 34 | } |
26 | 35 | ||
27 | mongoose.model('OAuthToken', OAuthTokenSchema) | 36 | // TODO: validation |
37 | // OAuthTokenSchema.path('accessToken').required(true) | ||
38 | // OAuthTokenSchema.path('client').required(true) | ||
39 | // OAuthTokenSchema.path('user').required(true) | ||
28 | 40 | ||
29 | // --------------------------------------------------------------------------- | 41 | // --------------------------------------------------------------------------- |
30 | 42 | ||
43 | function associate (models) { | ||
44 | this.belongsTo(models.User, { | ||
45 | foreignKey: { | ||
46 | name: 'userId', | ||
47 | allowNull: false | ||
48 | }, | ||
49 | onDelete: 'cascade' | ||
50 | }) | ||
51 | } | ||
52 | |||
31 | function getByRefreshTokenAndPopulateClient (refreshToken) { | 53 | function getByRefreshTokenAndPopulateClient (refreshToken) { |
32 | return this.findOne({ refreshToken: refreshToken }).populate('client').exec().then(function (token) { | 54 | const query = { |
55 | where: { | ||
56 | refreshToken: refreshToken | ||
57 | }, | ||
58 | include: [ this.associations.OAuthClient ] | ||
59 | } | ||
60 | |||
61 | return this.findOne(query).then(function (token) { | ||
33 | if (!token) return token | 62 | if (!token) return token |
34 | 63 | ||
35 | const tokenInfos = { | 64 | const tokenInfos = { |
36 | refreshToken: token.refreshToken, | 65 | refreshToken: token.refreshToken, |
37 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, | 66 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, |
38 | client: { | 67 | client: { |
39 | id: token.client._id.toString() | 68 | id: token.client.id |
40 | }, | 69 | }, |
41 | user: { | 70 | user: { |
42 | id: token.user | 71 | id: token.user |
@@ -50,13 +79,41 @@ function getByRefreshTokenAndPopulateClient (refreshToken) { | |||
50 | } | 79 | } |
51 | 80 | ||
52 | function getByTokenAndPopulateUser (bearerToken) { | 81 | function getByTokenAndPopulateUser (bearerToken) { |
53 | return this.findOne({ accessToken: bearerToken }).populate('user').exec() | 82 | const query = { |
83 | where: { | ||
84 | accessToken: bearerToken | ||
85 | }, | ||
86 | include: [ this.sequelize.models.User ] | ||
87 | } | ||
88 | |||
89 | return this.findOne(query).then(function (token) { | ||
90 | if (token) token.user = token.User | ||
91 | |||
92 | return token | ||
93 | }) | ||
54 | } | 94 | } |
55 | 95 | ||
56 | function getByRefreshTokenAndPopulateUser (refreshToken) { | 96 | function getByRefreshTokenAndPopulateUser (refreshToken) { |
57 | return this.findOne({ refreshToken: refreshToken }).populate('user').exec() | 97 | const query = { |
98 | where: { | ||
99 | refreshToken: refreshToken | ||
100 | }, | ||
101 | include: [ this.sequelize.models.User ] | ||
102 | } | ||
103 | |||
104 | return this.findOne(query).then(function (token) { | ||
105 | token.user = token.User | ||
106 | |||
107 | return token | ||
108 | }) | ||
58 | } | 109 | } |
59 | 110 | ||
60 | function removeByUserId (userId, callback) { | 111 | function removeByUserId (userId, callback) { |
61 | return this.remove({ user: userId }, callback) | 112 | const query = { |
113 | where: { | ||
114 | userId: userId | ||
115 | } | ||
116 | } | ||
117 | |||
118 | return this.destroy(query).asCallback(callback) | ||
62 | } | 119 | } |
diff --git a/server/models/pods.js b/server/models/pods.js index 49c73472a..2c1f56203 100644 --- a/server/models/pods.js +++ b/server/models/pods.js | |||
@@ -1,79 +1,62 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const each = require('async/each') | ||
4 | const mongoose = require('mongoose') | ||
5 | const map = require('lodash/map') | 3 | const map = require('lodash/map') |
6 | const validator = require('express-validator').validator | ||
7 | 4 | ||
8 | const constants = require('../initializers/constants') | 5 | const constants = require('../initializers/constants') |
9 | 6 | ||
10 | const Video = mongoose.model('Video') | ||
11 | |||
12 | // --------------------------------------------------------------------------- | 7 | // --------------------------------------------------------------------------- |
13 | 8 | ||
14 | const PodSchema = mongoose.Schema({ | 9 | module.exports = function (sequelize, DataTypes) { |
15 | host: String, | 10 | const Pod = sequelize.define('Pod', |
16 | publicKey: String, | 11 | { |
17 | score: { type: Number, max: constants.FRIEND_SCORE.MAX }, | 12 | host: { |
18 | createdDate: { | 13 | type: DataTypes.STRING |
19 | type: Date, | 14 | }, |
20 | default: Date.now | 15 | publicKey: { |
21 | } | 16 | type: DataTypes.STRING(5000) |
22 | }) | 17 | }, |
23 | 18 | score: { | |
24 | PodSchema.path('host').validate(validator.isURL) | 19 | type: DataTypes.INTEGER, |
25 | PodSchema.path('publicKey').required(true) | 20 | defaultValue: constants.FRIEND_SCORE.BASE |
26 | PodSchema.path('score').validate(function (value) { return !isNaN(value) }) | 21 | } |
27 | 22 | // Check createdAt | |
28 | PodSchema.methods = { | 23 | }, |
29 | toFormatedJSON | 24 | { |
25 | classMethods: { | ||
26 | associate, | ||
27 | |||
28 | countAll, | ||
29 | incrementScores, | ||
30 | list, | ||
31 | listAllIds, | ||
32 | listBadPods, | ||
33 | load, | ||
34 | loadByHost, | ||
35 | removeAll | ||
36 | }, | ||
37 | instanceMethods: { | ||
38 | toFormatedJSON | ||
39 | } | ||
40 | } | ||
41 | ) | ||
42 | |||
43 | return Pod | ||
30 | } | 44 | } |
31 | 45 | ||
32 | PodSchema.statics = { | 46 | // TODO: max score -> constants.FRIENDS_SCORE.MAX |
33 | countAll, | 47 | // TODO: validation |
34 | incrementScores, | 48 | // PodSchema.path('host').validate(validator.isURL) |
35 | list, | 49 | // PodSchema.path('publicKey').required(true) |
36 | listAllIds, | 50 | // PodSchema.path('score').validate(function (value) { return !isNaN(value) }) |
37 | listBadPods, | ||
38 | load, | ||
39 | loadByHost, | ||
40 | removeAll | ||
41 | } | ||
42 | |||
43 | PodSchema.pre('save', function (next) { | ||
44 | const self = this | ||
45 | |||
46 | Pod.loadByHost(this.host, function (err, pod) { | ||
47 | if (err) return next(err) | ||
48 | |||
49 | if (pod) return next(new Error('Pod already exists.')) | ||
50 | |||
51 | self.score = constants.FRIEND_SCORE.BASE | ||
52 | return next() | ||
53 | }) | ||
54 | }) | ||
55 | |||
56 | PodSchema.pre('remove', function (next) { | ||
57 | // Remove the videos owned by this pod too | ||
58 | Video.listByHost(this.host, function (err, videos) { | ||
59 | if (err) return next(err) | ||
60 | |||
61 | each(videos, function (video, callbackEach) { | ||
62 | video.remove(callbackEach) | ||
63 | }, next) | ||
64 | }) | ||
65 | }) | ||
66 | |||
67 | const Pod = mongoose.model('Pod', PodSchema) | ||
68 | 51 | ||
69 | // ------------------------------ METHODS ------------------------------ | 52 | // ------------------------------ METHODS ------------------------------ |
70 | 53 | ||
71 | function toFormatedJSON () { | 54 | function toFormatedJSON () { |
72 | const json = { | 55 | const json = { |
73 | id: this._id, | 56 | id: this.id, |
74 | host: this.host, | 57 | host: this.host, |
75 | score: this.score, | 58 | score: this.score, |
76 | createdDate: this.createdDate | 59 | createdAt: this.createdAt |
77 | } | 60 | } |
78 | 61 | ||
79 | return json | 62 | return json |
@@ -81,39 +64,76 @@ function toFormatedJSON () { | |||
81 | 64 | ||
82 | // ------------------------------ Statics ------------------------------ | 65 | // ------------------------------ Statics ------------------------------ |
83 | 66 | ||
67 | function associate (models) { | ||
68 | this.belongsToMany(models.Request, { | ||
69 | foreignKey: 'podId', | ||
70 | through: models.RequestToPod, | ||
71 | onDelete: 'CASCADE' | ||
72 | }) | ||
73 | } | ||
74 | |||
84 | function countAll (callback) { | 75 | function countAll (callback) { |
85 | return this.count(callback) | 76 | return this.count().asCallback(callback) |
86 | } | 77 | } |
87 | 78 | ||
88 | function incrementScores (ids, value, callback) { | 79 | function incrementScores (ids, value, callback) { |
89 | if (!callback) callback = function () {} | 80 | if (!callback) callback = function () {} |
90 | return this.update({ _id: { $in: ids } }, { $inc: { score: value } }, { multi: true }, callback) | 81 | |
82 | const update = { | ||
83 | score: this.sequelize.literal('score +' + value) | ||
84 | } | ||
85 | |||
86 | const query = { | ||
87 | where: { | ||
88 | id: { | ||
89 | $in: ids | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | return this.update(update, query).asCallback(callback) | ||
91 | } | 95 | } |
92 | 96 | ||
93 | function list (callback) { | 97 | function list (callback) { |
94 | return this.find(callback) | 98 | return this.findAll().asCallback(callback) |
95 | } | 99 | } |
96 | 100 | ||
97 | function listAllIds (callback) { | 101 | function listAllIds (callback) { |
98 | return this.find({}, { _id: 1 }, function (err, pods) { | 102 | const query = { |
103 | attributes: [ 'id' ] | ||
104 | } | ||
105 | |||
106 | return this.findAll(query).asCallback(function (err, pods) { | ||
99 | if (err) return callback(err) | 107 | if (err) return callback(err) |
100 | 108 | ||
101 | return callback(null, map(pods, '_id')) | 109 | return callback(null, map(pods, 'id')) |
102 | }) | 110 | }) |
103 | } | 111 | } |
104 | 112 | ||
105 | function listBadPods (callback) { | 113 | function listBadPods (callback) { |
106 | return this.find({ score: 0 }, callback) | 114 | const query = { |
115 | where: { | ||
116 | score: { $lte: 0 } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return this.findAll(query).asCallback(callback) | ||
107 | } | 121 | } |
108 | 122 | ||
109 | function load (id, callback) { | 123 | function load (id, callback) { |
110 | return this.findById(id, callback) | 124 | return this.findById(id).asCallback(callback) |
111 | } | 125 | } |
112 | 126 | ||
113 | function loadByHost (host, callback) { | 127 | function loadByHost (host, callback) { |
114 | return this.findOne({ host }, callback) | 128 | const query = { |
129 | where: { | ||
130 | host: host | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return this.findOne(query).asCallback(callback) | ||
115 | } | 135 | } |
116 | 136 | ||
117 | function removeAll (callback) { | 137 | function removeAll (callback) { |
118 | return this.remove({}, callback) | 138 | return this.destroy().asCallback(callback) |
119 | } | 139 | } |
diff --git a/server/models/request.js b/server/models/request.js index c2cfe83ce..882f747b7 100644 --- a/server/models/request.js +++ b/server/models/request.js | |||
@@ -2,66 +2,58 @@ | |||
2 | 2 | ||
3 | const each = require('async/each') | 3 | const each = require('async/each') |
4 | const eachLimit = require('async/eachLimit') | 4 | const eachLimit = require('async/eachLimit') |
5 | const values = require('lodash/values') | ||
6 | const mongoose = require('mongoose') | ||
7 | const waterfall = require('async/waterfall') | 5 | const waterfall = require('async/waterfall') |
8 | 6 | ||
9 | const constants = require('../initializers/constants') | 7 | const constants = require('../initializers/constants') |
10 | const logger = require('../helpers/logger') | 8 | const logger = require('../helpers/logger') |
11 | const requests = require('../helpers/requests') | 9 | const requests = require('../helpers/requests') |
12 | 10 | ||
13 | const Pod = mongoose.model('Pod') | ||
14 | |||
15 | let timer = null | 11 | let timer = null |
16 | let lastRequestTimestamp = 0 | 12 | let lastRequestTimestamp = 0 |
17 | 13 | ||
18 | // --------------------------------------------------------------------------- | 14 | // --------------------------------------------------------------------------- |
19 | 15 | ||
20 | const RequestSchema = mongoose.Schema({ | 16 | module.exports = function (sequelize, DataTypes) { |
21 | request: mongoose.Schema.Types.Mixed, | 17 | const Request = sequelize.define('Request', |
22 | endpoint: { | 18 | { |
23 | type: String, | 19 | request: { |
24 | enum: [ values(constants.REQUEST_ENDPOINTS) ] | 20 | type: DataTypes.JSON |
25 | }, | 21 | }, |
26 | to: [ | 22 | endpoint: { |
23 | // TODO: enum? | ||
24 | type: DataTypes.STRING | ||
25 | } | ||
26 | }, | ||
27 | { | 27 | { |
28 | type: mongoose.Schema.Types.ObjectId, | 28 | classMethods: { |
29 | ref: 'Pod' | 29 | associate, |
30 | |||
31 | activate, | ||
32 | countTotalRequests, | ||
33 | deactivate, | ||
34 | flush, | ||
35 | forceSend, | ||
36 | remainingMilliSeconds | ||
37 | } | ||
30 | } | 38 | } |
31 | ] | 39 | ) |
32 | }) | ||
33 | |||
34 | RequestSchema.statics = { | ||
35 | activate, | ||
36 | deactivate, | ||
37 | flush, | ||
38 | forceSend, | ||
39 | list, | ||
40 | remainingMilliSeconds | ||
41 | } | ||
42 | |||
43 | RequestSchema.pre('save', function (next) { | ||
44 | const self = this | ||
45 | |||
46 | if (self.to.length === 0) { | ||
47 | Pod.listAllIds(function (err, podIds) { | ||
48 | if (err) return next(err) | ||
49 | |||
50 | // No friends | ||
51 | if (podIds.length === 0) return | ||
52 | |||
53 | self.to = podIds | ||
54 | return next() | ||
55 | }) | ||
56 | } else { | ||
57 | return next() | ||
58 | } | ||
59 | }) | ||
60 | 40 | ||
61 | mongoose.model('Request', RequestSchema) | 41 | return Request |
42 | } | ||
62 | 43 | ||
63 | // ------------------------------ STATICS ------------------------------ | 44 | // ------------------------------ STATICS ------------------------------ |
64 | 45 | ||
46 | function associate (models) { | ||
47 | this.belongsToMany(models.Pod, { | ||
48 | foreignKey: { | ||
49 | name: 'requestId', | ||
50 | allowNull: false | ||
51 | }, | ||
52 | through: models.RequestToPod, | ||
53 | onDelete: 'CASCADE' | ||
54 | }) | ||
55 | } | ||
56 | |||
65 | function activate () { | 57 | function activate () { |
66 | logger.info('Requests scheduler activated.') | 58 | logger.info('Requests scheduler activated.') |
67 | lastRequestTimestamp = Date.now() | 59 | lastRequestTimestamp = Date.now() |
@@ -73,6 +65,14 @@ function activate () { | |||
73 | }, constants.REQUESTS_INTERVAL) | 65 | }, constants.REQUESTS_INTERVAL) |
74 | } | 66 | } |
75 | 67 | ||
68 | function countTotalRequests (callback) { | ||
69 | const query = { | ||
70 | include: [ this.sequelize.models.Pod ] | ||
71 | } | ||
72 | |||
73 | return this.count(query).asCallback(callback) | ||
74 | } | ||
75 | |||
76 | function deactivate () { | 76 | function deactivate () { |
77 | logger.info('Requests scheduler deactivated.') | 77 | logger.info('Requests scheduler deactivated.') |
78 | clearInterval(timer) | 78 | clearInterval(timer) |
@@ -90,10 +90,6 @@ function forceSend () { | |||
90 | makeRequests.call(this) | 90 | makeRequests.call(this) |
91 | } | 91 | } |
92 | 92 | ||
93 | function list (callback) { | ||
94 | this.find({ }, callback) | ||
95 | } | ||
96 | |||
97 | function remainingMilliSeconds () { | 93 | function remainingMilliSeconds () { |
98 | if (timer === null) return -1 | 94 | if (timer === null) return -1 |
99 | 95 | ||
@@ -136,6 +132,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) { | |||
136 | // Make all the requests of the scheduler | 132 | // Make all the requests of the scheduler |
137 | function makeRequests () { | 133 | function makeRequests () { |
138 | const self = this | 134 | const self = this |
135 | const RequestToPod = this.sequelize.models.RequestToPod | ||
139 | 136 | ||
140 | // We limit the size of the requests (REQUESTS_LIMIT) | 137 | // We limit the size of the requests (REQUESTS_LIMIT) |
141 | // We don't want to stuck with the same failing requests so we get a random list | 138 | // We don't want to stuck with the same failing requests so we get a random list |
@@ -156,20 +153,20 @@ function makeRequests () { | |||
156 | // We want to group requests by destinations pod and endpoint | 153 | // We want to group requests by destinations pod and endpoint |
157 | const requestsToMakeGrouped = {} | 154 | const requestsToMakeGrouped = {} |
158 | 155 | ||
159 | requests.forEach(function (poolRequest) { | 156 | requests.forEach(function (request) { |
160 | poolRequest.to.forEach(function (toPodId) { | 157 | request.Pods.forEach(function (toPod) { |
161 | const hashKey = toPodId + poolRequest.endpoint | 158 | const hashKey = toPod.id + request.endpoint |
162 | if (!requestsToMakeGrouped[hashKey]) { | 159 | if (!requestsToMakeGrouped[hashKey]) { |
163 | requestsToMakeGrouped[hashKey] = { | 160 | requestsToMakeGrouped[hashKey] = { |
164 | toPodId, | 161 | toPodId: toPod.id, |
165 | endpoint: poolRequest.endpoint, | 162 | endpoint: request.endpoint, |
166 | ids: [], // pool request ids, to delete them from the DB in the future | 163 | ids: [], // request ids, to delete them from the DB in the future |
167 | datas: [] // requests data, | 164 | datas: [] // requests data, |
168 | } | 165 | } |
169 | } | 166 | } |
170 | 167 | ||
171 | requestsToMakeGrouped[hashKey].ids.push(poolRequest._id) | 168 | requestsToMakeGrouped[hashKey].ids.push(request.id) |
172 | requestsToMakeGrouped[hashKey].datas.push(poolRequest.request) | 169 | requestsToMakeGrouped[hashKey].datas.push(request.request) |
173 | }) | 170 | }) |
174 | }) | 171 | }) |
175 | 172 | ||
@@ -179,8 +176,8 @@ function makeRequests () { | |||
179 | eachLimit(Object.keys(requestsToMakeGrouped), constants.REQUESTS_IN_PARALLEL, function (hashKey, callbackEach) { | 176 | eachLimit(Object.keys(requestsToMakeGrouped), constants.REQUESTS_IN_PARALLEL, function (hashKey, callbackEach) { |
180 | const requestToMake = requestsToMakeGrouped[hashKey] | 177 | const requestToMake = requestsToMakeGrouped[hashKey] |
181 | 178 | ||
182 | // FIXME: mongodb request inside a loop :/ | 179 | // FIXME: SQL request inside a loop :/ |
183 | Pod.load(requestToMake.toPodId, function (err, toPod) { | 180 | self.sequelize.models.Pod.load(requestToMake.toPodId, function (err, toPod) { |
184 | if (err) { | 181 | if (err) { |
185 | logger.error('Error finding pod by id.', { err: err }) | 182 | logger.error('Error finding pod by id.', { err: err }) |
186 | return callbackEach() | 183 | return callbackEach() |
@@ -191,7 +188,7 @@ function makeRequests () { | |||
191 | const requestIdsToDelete = requestToMake.ids | 188 | const requestIdsToDelete = requestToMake.ids |
192 | 189 | ||
193 | logger.info('Removing %d requests of unexisting pod %s.', requestIdsToDelete.length, requestToMake.toPodId) | 190 | logger.info('Removing %d requests of unexisting pod %s.', requestIdsToDelete.length, requestToMake.toPodId) |
194 | removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId) | 191 | RequestToPod.removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId) |
195 | return callbackEach() | 192 | return callbackEach() |
196 | } | 193 | } |
197 | 194 | ||
@@ -202,7 +199,7 @@ function makeRequests () { | |||
202 | goodPods.push(requestToMake.toPodId) | 199 | goodPods.push(requestToMake.toPodId) |
203 | 200 | ||
204 | // Remove the pod id of these request ids | 201 | // Remove the pod id of these request ids |
205 | removePodOf.call(self, requestToMake.ids, requestToMake.toPodId, callbackEach) | 202 | RequestToPod.removePodOf(requestToMake.ids, requestToMake.toPodId, callbackEach) |
206 | } else { | 203 | } else { |
207 | badPods.push(requestToMake.toPodId) | 204 | badPods.push(requestToMake.toPodId) |
208 | callbackEach() | 205 | callbackEach() |
@@ -211,18 +208,22 @@ function makeRequests () { | |||
211 | }) | 208 | }) |
212 | }, function () { | 209 | }, function () { |
213 | // All the requests were made, we update the pods score | 210 | // All the requests were made, we update the pods score |
214 | updatePodsScore(goodPods, badPods) | 211 | updatePodsScore.call(self, goodPods, badPods) |
215 | // Flush requests with no pod | 212 | // Flush requests with no pod |
216 | removeWithEmptyTo.call(self) | 213 | removeWithEmptyTo.call(self, function (err) { |
214 | if (err) logger.error('Error when removing requests with no pods.', { error: err }) | ||
215 | }) | ||
217 | }) | 216 | }) |
218 | }) | 217 | }) |
219 | } | 218 | } |
220 | 219 | ||
221 | // Remove pods with a score of 0 (too many requests where they were unreachable) | 220 | // Remove pods with a score of 0 (too many requests where they were unreachable) |
222 | function removeBadPods () { | 221 | function removeBadPods () { |
222 | const self = this | ||
223 | |||
223 | waterfall([ | 224 | waterfall([ |
224 | function findBadPods (callback) { | 225 | function findBadPods (callback) { |
225 | Pod.listBadPods(function (err, pods) { | 226 | self.sequelize.models.Pod.listBadPods(function (err, pods) { |
226 | if (err) { | 227 | if (err) { |
227 | logger.error('Cannot find bad pods.', { error: err }) | 228 | logger.error('Cannot find bad pods.', { error: err }) |
228 | return callback(err) | 229 | return callback(err) |
@@ -233,10 +234,8 @@ function removeBadPods () { | |||
233 | }, | 234 | }, |
234 | 235 | ||
235 | function removeTheseBadPods (pods, callback) { | 236 | function removeTheseBadPods (pods, callback) { |
236 | if (pods.length === 0) return callback(null, 0) | ||
237 | |||
238 | each(pods, function (pod, callbackEach) { | 237 | each(pods, function (pod, callbackEach) { |
239 | pod.remove(callbackEach) | 238 | pod.destroy().asCallback(callbackEach) |
240 | }, function (err) { | 239 | }, function (err) { |
241 | return callback(err, pods.length) | 240 | return callback(err, pods.length) |
242 | }) | 241 | }) |
@@ -253,43 +252,67 @@ function removeBadPods () { | |||
253 | } | 252 | } |
254 | 253 | ||
255 | function updatePodsScore (goodPods, badPods) { | 254 | function updatePodsScore (goodPods, badPods) { |
255 | const self = this | ||
256 | const Pod = this.sequelize.models.Pod | ||
257 | |||
256 | logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length) | 258 | logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length) |
257 | 259 | ||
258 | Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) { | 260 | if (goodPods.length !== 0) { |
259 | if (err) logger.error('Cannot increment scores of good pods.') | 261 | Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) { |
260 | }) | 262 | if (err) logger.error('Cannot increment scores of good pods.') |
263 | }) | ||
264 | } | ||
261 | 265 | ||
262 | Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) { | 266 | if (badPods.length !== 0) { |
263 | if (err) logger.error('Cannot decrement scores of bad pods.') | 267 | Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) { |
264 | removeBadPods() | 268 | if (err) logger.error('Cannot decrement scores of bad pods.') |
265 | }) | 269 | removeBadPods.call(self) |
270 | }) | ||
271 | } | ||
266 | } | 272 | } |
267 | 273 | ||
268 | function listWithLimitAndRandom (limit, callback) { | 274 | function listWithLimitAndRandom (limit, callback) { |
269 | const self = this | 275 | const self = this |
270 | 276 | ||
271 | self.count(function (err, count) { | 277 | self.count().asCallback(function (err, count) { |
272 | if (err) return callback(err) | 278 | if (err) return callback(err) |
273 | 279 | ||
280 | // Optimization... | ||
281 | if (count === 0) return callback(null, []) | ||
282 | |||
274 | let start = Math.floor(Math.random() * count) - limit | 283 | let start = Math.floor(Math.random() * count) - limit |
275 | if (start < 0) start = 0 | 284 | if (start < 0) start = 0 |
276 | 285 | ||
277 | self.find().sort({ _id: 1 }).skip(start).limit(limit).exec(callback) | 286 | const query = { |
287 | order: [ | ||
288 | [ 'id', 'ASC' ] | ||
289 | ], | ||
290 | offset: start, | ||
291 | limit: limit, | ||
292 | include: [ this.sequelize.models.Pod ] | ||
293 | } | ||
294 | |||
295 | self.findAll(query).asCallback(callback) | ||
278 | }) | 296 | }) |
279 | } | 297 | } |
280 | 298 | ||
281 | function removeAll (callback) { | 299 | function removeAll (callback) { |
282 | this.remove({ }, callback) | 300 | // Delete all requests |
283 | } | 301 | this.destroy({ truncate: true }).asCallback(callback) |
284 | |||
285 | function removePodOf (requestsIds, podId, callback) { | ||
286 | if (!callback) callback = function () {} | ||
287 | |||
288 | this.update({ _id: { $in: requestsIds } }, { $pull: { to: podId } }, { multi: true }, callback) | ||
289 | } | 302 | } |
290 | 303 | ||
291 | function removeWithEmptyTo (callback) { | 304 | function removeWithEmptyTo (callback) { |
292 | if (!callback) callback = function () {} | 305 | if (!callback) callback = function () {} |
293 | 306 | ||
294 | this.remove({ to: { $size: 0 } }, callback) | 307 | const query = { |
308 | where: { | ||
309 | id: { | ||
310 | $notIn: [ | ||
311 | this.sequelize.literal('SELECT "requestId" FROM "RequestToPods"') | ||
312 | ] | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | this.destroy(query).asCallback(callback) | ||
295 | } | 318 | } |
diff --git a/server/models/requestToPod.js b/server/models/requestToPod.js new file mode 100644 index 000000000..378c2bdcf --- /dev/null +++ b/server/models/requestToPod.js | |||
@@ -0,0 +1,30 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | // --------------------------------------------------------------------------- | ||
4 | |||
5 | module.exports = function (sequelize, DataTypes) { | ||
6 | const RequestToPod = sequelize.define('RequestToPod', {}, { | ||
7 | classMethods: { | ||
8 | removePodOf | ||
9 | } | ||
10 | }) | ||
11 | |||
12 | return RequestToPod | ||
13 | } | ||
14 | |||
15 | // --------------------------------------------------------------------------- | ||
16 | |||
17 | function removePodOf (requestsIds, podId, callback) { | ||
18 | if (!callback) callback = function () {} | ||
19 | |||
20 | const query = { | ||
21 | where: { | ||
22 | requestId: { | ||
23 | $in: requestsIds | ||
24 | }, | ||
25 | podId: podId | ||
26 | } | ||
27 | } | ||
28 | |||
29 | this.destroy(query).asCallback(callback) | ||
30 | } | ||
diff --git a/server/models/user.js b/server/models/user.js index a19de7072..e50eb96ea 100644 --- a/server/models/user.js +++ b/server/models/user.js | |||
@@ -1,60 +1,60 @@ | |||
1 | const mongoose = require('mongoose') | ||
2 | |||
3 | const customUsersValidators = require('../helpers/custom-validators').users | ||
4 | const modelUtils = require('./utils') | 1 | const modelUtils = require('./utils') |
5 | const peertubeCrypto = require('../helpers/peertube-crypto') | 2 | const peertubeCrypto = require('../helpers/peertube-crypto') |
6 | 3 | ||
7 | const OAuthToken = mongoose.model('OAuthToken') | ||
8 | |||
9 | // --------------------------------------------------------------------------- | 4 | // --------------------------------------------------------------------------- |
10 | 5 | ||
11 | const UserSchema = mongoose.Schema({ | 6 | module.exports = function (sequelize, DataTypes) { |
12 | createdDate: { | 7 | const User = sequelize.define('User', |
13 | type: Date, | 8 | { |
14 | default: Date.now | 9 | password: { |
15 | }, | 10 | type: DataTypes.STRING |
16 | password: String, | 11 | }, |
17 | username: String, | 12 | username: { |
18 | role: String | 13 | type: DataTypes.STRING |
19 | }) | 14 | }, |
20 | 15 | role: { | |
21 | UserSchema.path('password').required(customUsersValidators.isUserPasswordValid) | 16 | type: DataTypes.STRING |
22 | UserSchema.path('username').required(customUsersValidators.isUserUsernameValid) | 17 | } |
23 | UserSchema.path('role').validate(customUsersValidators.isUserRoleValid) | 18 | }, |
24 | 19 | { | |
25 | UserSchema.methods = { | 20 | classMethods: { |
26 | isPasswordMatch, | 21 | associate, |
27 | toFormatedJSON | 22 | |
23 | countTotal, | ||
24 | getByUsername, | ||
25 | list, | ||
26 | listForApi, | ||
27 | loadById, | ||
28 | loadByUsername | ||
29 | }, | ||
30 | instanceMethods: { | ||
31 | isPasswordMatch, | ||
32 | toFormatedJSON | ||
33 | }, | ||
34 | hooks: { | ||
35 | beforeCreate: beforeCreateOrUpdate, | ||
36 | beforeUpdate: beforeCreateOrUpdate | ||
37 | } | ||
38 | } | ||
39 | ) | ||
40 | |||
41 | return User | ||
28 | } | 42 | } |
29 | 43 | ||
30 | UserSchema.statics = { | 44 | // TODO: Validation |
31 | countTotal, | 45 | // UserSchema.path('password').required(customUsersValidators.isUserPasswordValid) |
32 | getByUsername, | 46 | // UserSchema.path('username').required(customUsersValidators.isUserUsernameValid) |
33 | list, | 47 | // UserSchema.path('role').validate(customUsersValidators.isUserRoleValid) |
34 | listForApi, | ||
35 | loadById, | ||
36 | loadByUsername | ||
37 | } | ||
38 | 48 | ||
39 | UserSchema.pre('save', function (next) { | 49 | function beforeCreateOrUpdate (user, options, next) { |
40 | const user = this | 50 | peertubeCrypto.cryptPassword(user.password, function (err, hash) { |
41 | |||
42 | peertubeCrypto.cryptPassword(this.password, function (err, hash) { | ||
43 | if (err) return next(err) | 51 | if (err) return next(err) |
44 | 52 | ||
45 | user.password = hash | 53 | user.password = hash |
46 | 54 | ||
47 | return next() | 55 | return next() |
48 | }) | 56 | }) |
49 | }) | 57 | } |
50 | |||
51 | UserSchema.pre('remove', function (next) { | ||
52 | const user = this | ||
53 | |||
54 | OAuthToken.removeByUserId(user._id, next) | ||
55 | }) | ||
56 | |||
57 | mongoose.model('User', UserSchema) | ||
58 | 58 | ||
59 | // ------------------------------ METHODS ------------------------------ | 59 | // ------------------------------ METHODS ------------------------------ |
60 | 60 | ||
@@ -64,35 +64,63 @@ function isPasswordMatch (password, callback) { | |||
64 | 64 | ||
65 | function toFormatedJSON () { | 65 | function toFormatedJSON () { |
66 | return { | 66 | return { |
67 | id: this._id, | 67 | id: this.id, |
68 | username: this.username, | 68 | username: this.username, |
69 | role: this.role, | 69 | role: this.role, |
70 | createdDate: this.createdDate | 70 | createdAt: this.createdAt |
71 | } | 71 | } |
72 | } | 72 | } |
73 | // ------------------------------ STATICS ------------------------------ | 73 | // ------------------------------ STATICS ------------------------------ |
74 | 74 | ||
75 | function associate (models) { | ||
76 | this.hasMany(models.OAuthToken, { | ||
77 | foreignKey: 'userId', | ||
78 | onDelete: 'cascade' | ||
79 | }) | ||
80 | } | ||
81 | |||
75 | function countTotal (callback) { | 82 | function countTotal (callback) { |
76 | return this.count(callback) | 83 | return this.count().asCallback(callback) |
77 | } | 84 | } |
78 | 85 | ||
79 | function getByUsername (username) { | 86 | function getByUsername (username) { |
80 | return this.findOne({ username: username }) | 87 | const query = { |
88 | where: { | ||
89 | username: username | ||
90 | } | ||
91 | } | ||
92 | |||
93 | return this.findOne(query) | ||
81 | } | 94 | } |
82 | 95 | ||
83 | function list (callback) { | 96 | function list (callback) { |
84 | return this.find(callback) | 97 | return this.find().asCallback(callback) |
85 | } | 98 | } |
86 | 99 | ||
87 | function listForApi (start, count, sort, callback) { | 100 | function listForApi (start, count, sort, callback) { |
88 | const query = {} | 101 | const query = { |
89 | return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) | 102 | offset: start, |
103 | limit: count, | ||
104 | order: [ modelUtils.getSort(sort) ] | ||
105 | } | ||
106 | |||
107 | return this.findAndCountAll(query).asCallback(function (err, result) { | ||
108 | if (err) return callback(err) | ||
109 | |||
110 | return callback(null, result.rows, result.count) | ||
111 | }) | ||
90 | } | 112 | } |
91 | 113 | ||
92 | function loadById (id, callback) { | 114 | function loadById (id, callback) { |
93 | return this.findById(id, callback) | 115 | return this.findById(id).asCallback(callback) |
94 | } | 116 | } |
95 | 117 | ||
96 | function loadByUsername (username, callback) { | 118 | function loadByUsername (username, callback) { |
97 | return this.findOne({ username: username }, callback) | 119 | const query = { |
120 | where: { | ||
121 | username: username | ||
122 | } | ||
123 | } | ||
124 | |||
125 | return this.findOne(query).asCallback(callback) | ||
98 | } | 126 | } |
diff --git a/server/models/utils.js b/server/models/utils.js index e798aabe6..49636b3d8 100644 --- a/server/models/utils.js +++ b/server/models/utils.js | |||
@@ -1,28 +1,23 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const parallel = require('async/parallel') | ||
4 | |||
5 | const utils = { | 3 | const utils = { |
6 | listForApiWithCount | 4 | getSort |
7 | } | 5 | } |
8 | 6 | ||
9 | function listForApiWithCount (query, start, count, sort, callback) { | 7 | // Translate for example "-name" to [ 'name', 'DESC' ] |
10 | const self = this | 8 | function getSort (value) { |
9 | let field | ||
10 | let direction | ||
11 | 11 | ||
12 | parallel([ | 12 | if (value.substring(0, 1) === '-') { |
13 | function (asyncCallback) { | 13 | direction = 'DESC' |
14 | self.find(query).skip(start).limit(count).sort(sort).exec(asyncCallback) | 14 | field = value.substring(1) |
15 | }, | 15 | } else { |
16 | function (asyncCallback) { | 16 | direction = 'ASC' |
17 | self.count(query, asyncCallback) | 17 | field = value |
18 | } | 18 | } |
19 | ], function (err, results) { | ||
20 | if (err) return callback(err) | ||
21 | 19 | ||
22 | const data = results[0] | 20 | return [ field, direction ] |
23 | const total = results[1] | ||
24 | return callback(null, data, total) | ||
25 | }) | ||
26 | } | 21 | } |
27 | 22 | ||
28 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
diff --git a/server/models/video.js b/server/models/video.js index 330067cdf..8ef07c9e6 100644 --- a/server/models/video.js +++ b/server/models/video.js | |||
@@ -7,102 +7,93 @@ const magnetUtil = require('magnet-uri') | |||
7 | const parallel = require('async/parallel') | 7 | const parallel = require('async/parallel') |
8 | const parseTorrent = require('parse-torrent') | 8 | const parseTorrent = require('parse-torrent') |
9 | const pathUtils = require('path') | 9 | const pathUtils = require('path') |
10 | const mongoose = require('mongoose') | ||
11 | 10 | ||
12 | const constants = require('../initializers/constants') | 11 | const constants = require('../initializers/constants') |
13 | const customVideosValidators = require('../helpers/custom-validators').videos | ||
14 | const logger = require('../helpers/logger') | 12 | const logger = require('../helpers/logger') |
15 | const modelUtils = require('./utils') | 13 | const modelUtils = require('./utils') |
16 | 14 | ||
17 | // --------------------------------------------------------------------------- | 15 | // --------------------------------------------------------------------------- |
18 | 16 | ||
17 | module.exports = function (sequelize, DataTypes) { | ||
19 | // TODO: add indexes on searchable columns | 18 | // TODO: add indexes on searchable columns |
20 | const VideoSchema = mongoose.Schema({ | 19 | const Video = sequelize.define('Video', |
21 | name: String, | 20 | { |
22 | extname: { | 21 | id: { |
23 | type: String, | 22 | type: DataTypes.UUID, |
24 | enum: [ '.mp4', '.webm', '.ogv' ] | 23 | defaultValue: DataTypes.UUIDV4, |
25 | }, | 24 | primaryKey: true |
26 | remoteId: mongoose.Schema.Types.ObjectId, | ||
27 | description: String, | ||
28 | magnet: { | ||
29 | infoHash: String | ||
30 | }, | ||
31 | podHost: String, | ||
32 | author: String, | ||
33 | duration: Number, | ||
34 | tags: [ String ], | ||
35 | createdDate: { | ||
36 | type: Date, | ||
37 | default: Date.now | ||
38 | } | ||
39 | }) | ||
40 | |||
41 | VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid) | ||
42 | VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid) | ||
43 | VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid) | ||
44 | VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid) | ||
45 | VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid) | ||
46 | VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid) | ||
47 | |||
48 | VideoSchema.methods = { | ||
49 | generateMagnetUri, | ||
50 | getVideoFilename, | ||
51 | getThumbnailName, | ||
52 | getPreviewName, | ||
53 | getTorrentName, | ||
54 | isOwned, | ||
55 | toFormatedJSON, | ||
56 | toRemoteJSON | ||
57 | } | ||
58 | |||
59 | VideoSchema.statics = { | ||
60 | generateThumbnailFromBase64, | ||
61 | getDurationFromFile, | ||
62 | listForApi, | ||
63 | listByHostAndRemoteId, | ||
64 | listByHost, | ||
65 | listOwned, | ||
66 | listOwnedByAuthor, | ||
67 | listRemotes, | ||
68 | load, | ||
69 | search | ||
70 | } | ||
71 | |||
72 | VideoSchema.pre('remove', function (next) { | ||
73 | const video = this | ||
74 | const tasks = [] | ||
75 | |||
76 | tasks.push( | ||
77 | function (callback) { | ||
78 | removeThumbnail(video, callback) | ||
79 | } | ||
80 | ) | ||
81 | |||
82 | if (video.isOwned()) { | ||
83 | tasks.push( | ||
84 | function (callback) { | ||
85 | removeFile(video, callback) | ||
86 | }, | 25 | }, |
87 | function (callback) { | 26 | name: { |
88 | removeTorrent(video, callback) | 27 | type: DataTypes.STRING |
89 | }, | 28 | }, |
90 | function (callback) { | 29 | extname: { |
91 | removePreview(video, callback) | 30 | // TODO: enum? |
31 | type: DataTypes.STRING | ||
32 | }, | ||
33 | remoteId: { | ||
34 | type: DataTypes.UUID | ||
35 | }, | ||
36 | description: { | ||
37 | type: DataTypes.STRING | ||
38 | }, | ||
39 | infoHash: { | ||
40 | type: DataTypes.STRING | ||
41 | }, | ||
42 | duration: { | ||
43 | type: DataTypes.INTEGER | ||
44 | }, | ||
45 | tags: { | ||
46 | type: DataTypes.ARRAY(DataTypes.STRING) | ||
92 | } | 47 | } |
93 | ) | 48 | }, |
94 | } | 49 | { |
50 | classMethods: { | ||
51 | associate, | ||
52 | |||
53 | generateThumbnailFromBase64, | ||
54 | getDurationFromFile, | ||
55 | listForApi, | ||
56 | listByHostAndRemoteId, | ||
57 | listOwnedAndPopulateAuthor, | ||
58 | listOwnedByAuthor, | ||
59 | load, | ||
60 | loadAndPopulateAuthor, | ||
61 | loadAndPopulateAuthorAndPod, | ||
62 | searchAndPopulateAuthorAndPod | ||
63 | }, | ||
64 | instanceMethods: { | ||
65 | generateMagnetUri, | ||
66 | getVideoFilename, | ||
67 | getThumbnailName, | ||
68 | getPreviewName, | ||
69 | getTorrentName, | ||
70 | isOwned, | ||
71 | toFormatedJSON, | ||
72 | toRemoteJSON | ||
73 | }, | ||
74 | hooks: { | ||
75 | beforeCreate, | ||
76 | afterDestroy | ||
77 | } | ||
78 | } | ||
79 | ) | ||
95 | 80 | ||
96 | parallel(tasks, next) | 81 | return Video |
97 | }) | 82 | } |
98 | 83 | ||
99 | VideoSchema.pre('save', function (next) { | 84 | // TODO: Validation |
100 | const video = this | 85 | // VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid) |
86 | // VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid) | ||
87 | // VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid) | ||
88 | // VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid) | ||
89 | // VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid) | ||
90 | // VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid) | ||
91 | |||
92 | function beforeCreate (video, options, next) { | ||
101 | const tasks = [] | 93 | const tasks = [] |
102 | 94 | ||
103 | if (video.isOwned()) { | 95 | if (video.isOwned()) { |
104 | const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) | 96 | const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) |
105 | this.podHost = constants.CONFIG.WEBSERVER.HOST | ||
106 | 97 | ||
107 | tasks.push( | 98 | tasks.push( |
108 | // TODO: refractoring | 99 | // TODO: refractoring |
@@ -123,7 +114,7 @@ VideoSchema.pre('save', function (next) { | |||
123 | if (err) return callback(err) | 114 | if (err) return callback(err) |
124 | 115 | ||
125 | const parsedTorrent = parseTorrent(torrent) | 116 | const parsedTorrent = parseTorrent(torrent) |
126 | video.magnet.infoHash = parsedTorrent.infoHash | 117 | video.infoHash = parsedTorrent.infoHash |
127 | 118 | ||
128 | callback(null) | 119 | callback(null) |
129 | }) | 120 | }) |
@@ -141,12 +132,46 @@ VideoSchema.pre('save', function (next) { | |||
141 | } | 132 | } |
142 | 133 | ||
143 | return next() | 134 | return next() |
144 | }) | 135 | } |
136 | |||
137 | function afterDestroy (video, options, next) { | ||
138 | const tasks = [] | ||
145 | 139 | ||
146 | mongoose.model('Video', VideoSchema) | 140 | tasks.push( |
141 | function (callback) { | ||
142 | removeThumbnail(video, callback) | ||
143 | } | ||
144 | ) | ||
145 | |||
146 | if (video.isOwned()) { | ||
147 | tasks.push( | ||
148 | function (callback) { | ||
149 | removeFile(video, callback) | ||
150 | }, | ||
151 | function (callback) { | ||
152 | removeTorrent(video, callback) | ||
153 | }, | ||
154 | function (callback) { | ||
155 | removePreview(video, callback) | ||
156 | } | ||
157 | ) | ||
158 | } | ||
159 | |||
160 | parallel(tasks, next) | ||
161 | } | ||
147 | 162 | ||
148 | // ------------------------------ METHODS ------------------------------ | 163 | // ------------------------------ METHODS ------------------------------ |
149 | 164 | ||
165 | function associate (models) { | ||
166 | this.belongsTo(models.Author, { | ||
167 | foreignKey: { | ||
168 | name: 'authorId', | ||
169 | allowNull: false | ||
170 | }, | ||
171 | onDelete: 'cascade' | ||
172 | }) | ||
173 | } | ||
174 | |||
150 | function generateMagnetUri () { | 175 | function generateMagnetUri () { |
151 | let baseUrlHttp, baseUrlWs | 176 | let baseUrlHttp, baseUrlWs |
152 | 177 | ||
@@ -154,8 +179,8 @@ function generateMagnetUri () { | |||
154 | baseUrlHttp = constants.CONFIG.WEBSERVER.URL | 179 | baseUrlHttp = constants.CONFIG.WEBSERVER.URL |
155 | baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT | 180 | baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT |
156 | } else { | 181 | } else { |
157 | baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost | 182 | baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.Author.Pod.host |
158 | baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.podHost | 183 | baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.Author.Pod.host |
159 | } | 184 | } |
160 | 185 | ||
161 | const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName() | 186 | const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName() |
@@ -166,7 +191,7 @@ function generateMagnetUri () { | |||
166 | xs, | 191 | xs, |
167 | announce, | 192 | announce, |
168 | urlList, | 193 | urlList, |
169 | infoHash: this.magnet.infoHash, | 194 | infoHash: this.infoHash, |
170 | name: this.name | 195 | name: this.name |
171 | } | 196 | } |
172 | 197 | ||
@@ -174,20 +199,20 @@ function generateMagnetUri () { | |||
174 | } | 199 | } |
175 | 200 | ||
176 | function getVideoFilename () { | 201 | function getVideoFilename () { |
177 | if (this.isOwned()) return this._id + this.extname | 202 | if (this.isOwned()) return this.id + this.extname |
178 | 203 | ||
179 | return this.remoteId + this.extname | 204 | return this.remoteId + this.extname |
180 | } | 205 | } |
181 | 206 | ||
182 | function getThumbnailName () { | 207 | function getThumbnailName () { |
183 | // We always have a copy of the thumbnail | 208 | // We always have a copy of the thumbnail |
184 | return this._id + '.jpg' | 209 | return this.id + '.jpg' |
185 | } | 210 | } |
186 | 211 | ||
187 | function getPreviewName () { | 212 | function getPreviewName () { |
188 | const extension = '.jpg' | 213 | const extension = '.jpg' |
189 | 214 | ||
190 | if (this.isOwned()) return this._id + extension | 215 | if (this.isOwned()) return this.id + extension |
191 | 216 | ||
192 | return this.remoteId + extension | 217 | return this.remoteId + extension |
193 | } | 218 | } |
@@ -195,7 +220,7 @@ function getPreviewName () { | |||
195 | function getTorrentName () { | 220 | function getTorrentName () { |
196 | const extension = '.torrent' | 221 | const extension = '.torrent' |
197 | 222 | ||
198 | if (this.isOwned()) return this._id + extension | 223 | if (this.isOwned()) return this.id + extension |
199 | 224 | ||
200 | return this.remoteId + extension | 225 | return this.remoteId + extension |
201 | } | 226 | } |
@@ -205,18 +230,27 @@ function isOwned () { | |||
205 | } | 230 | } |
206 | 231 | ||
207 | function toFormatedJSON () { | 232 | function toFormatedJSON () { |
233 | let podHost | ||
234 | |||
235 | if (this.Author.Pod) { | ||
236 | podHost = this.Author.Pod.host | ||
237 | } else { | ||
238 | // It means it's our video | ||
239 | podHost = constants.CONFIG.WEBSERVER.HOST | ||
240 | } | ||
241 | |||
208 | const json = { | 242 | const json = { |
209 | id: this._id, | 243 | id: this.id, |
210 | name: this.name, | 244 | name: this.name, |
211 | description: this.description, | 245 | description: this.description, |
212 | podHost: this.podHost, | 246 | podHost, |
213 | isLocal: this.isOwned(), | 247 | isLocal: this.isOwned(), |
214 | magnetUri: this.generateMagnetUri(), | 248 | magnetUri: this.generateMagnetUri(), |
215 | author: this.author, | 249 | author: this.Author.name, |
216 | duration: this.duration, | 250 | duration: this.duration, |
217 | tags: this.tags, | 251 | tags: this.tags, |
218 | thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(), | 252 | thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(), |
219 | createdDate: this.createdDate | 253 | createdAt: this.createdAt |
220 | } | 254 | } |
221 | 255 | ||
222 | return json | 256 | return json |
@@ -236,13 +270,13 @@ function toRemoteJSON (callback) { | |||
236 | const remoteVideo = { | 270 | const remoteVideo = { |
237 | name: self.name, | 271 | name: self.name, |
238 | description: self.description, | 272 | description: self.description, |
239 | magnet: self.magnet, | 273 | infoHash: self.infoHash, |
240 | remoteId: self._id, | 274 | remoteId: self.id, |
241 | author: self.author, | 275 | author: self.Author.name, |
242 | duration: self.duration, | 276 | duration: self.duration, |
243 | thumbnailBase64: new Buffer(thumbnailData).toString('base64'), | 277 | thumbnailBase64: new Buffer(thumbnailData).toString('base64'), |
244 | tags: self.tags, | 278 | tags: self.tags, |
245 | createdDate: self.createdDate, | 279 | createdAt: self.createdAt, |
246 | extname: self.extname | 280 | extname: self.extname |
247 | } | 281 | } |
248 | 282 | ||
@@ -273,50 +307,168 @@ function getDurationFromFile (videoPath, callback) { | |||
273 | } | 307 | } |
274 | 308 | ||
275 | function listForApi (start, count, sort, callback) { | 309 | function listForApi (start, count, sort, callback) { |
276 | const query = {} | 310 | const query = { |
277 | return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) | 311 | offset: start, |
312 | limit: count, | ||
313 | order: [ modelUtils.getSort(sort) ], | ||
314 | include: [ | ||
315 | { | ||
316 | model: this.sequelize.models.Author, | ||
317 | include: [ this.sequelize.models.Pod ] | ||
318 | } | ||
319 | ] | ||
320 | } | ||
321 | |||
322 | return this.findAndCountAll(query).asCallback(function (err, result) { | ||
323 | if (err) return callback(err) | ||
324 | |||
325 | return callback(null, result.rows, result.count) | ||
326 | }) | ||
278 | } | 327 | } |
279 | 328 | ||
280 | function listByHostAndRemoteId (fromHost, remoteId, callback) { | 329 | function listByHostAndRemoteId (fromHost, remoteId, callback) { |
281 | this.find({ podHost: fromHost, remoteId: remoteId }, callback) | 330 | const query = { |
282 | } | 331 | where: { |
332 | remoteId: remoteId | ||
333 | }, | ||
334 | include: [ | ||
335 | { | ||
336 | model: this.sequelize.models.Author, | ||
337 | include: [ | ||
338 | { | ||
339 | model: this.sequelize.models.Pod, | ||
340 | where: { | ||
341 | host: fromHost | ||
342 | } | ||
343 | } | ||
344 | ] | ||
345 | } | ||
346 | ] | ||
347 | } | ||
283 | 348 | ||
284 | function listByHost (fromHost, callback) { | 349 | return this.findAll(query).asCallback(callback) |
285 | this.find({ podHost: fromHost }, callback) | ||
286 | } | 350 | } |
287 | 351 | ||
288 | function listOwned (callback) { | 352 | function listOwnedAndPopulateAuthor (callback) { |
289 | // If remoteId is null this is *our* video | 353 | // If remoteId is null this is *our* video |
290 | this.find({ remoteId: null }, callback) | 354 | const query = { |
355 | where: { | ||
356 | remoteId: null | ||
357 | }, | ||
358 | include: [ this.sequelize.models.Author ] | ||
359 | } | ||
360 | |||
361 | return this.findAll(query).asCallback(callback) | ||
291 | } | 362 | } |
292 | 363 | ||
293 | function listOwnedByAuthor (author, callback) { | 364 | function listOwnedByAuthor (author, callback) { |
294 | this.find({ remoteId: null, author: author }, callback) | 365 | const query = { |
295 | } | 366 | where: { |
367 | remoteId: null | ||
368 | }, | ||
369 | include: [ | ||
370 | { | ||
371 | model: this.sequelize.models.Author, | ||
372 | where: { | ||
373 | name: author | ||
374 | } | ||
375 | } | ||
376 | ] | ||
377 | } | ||
296 | 378 | ||
297 | function listRemotes (callback) { | 379 | return this.findAll(query).asCallback(callback) |
298 | this.find({ remoteId: { $ne: null } }, callback) | ||
299 | } | 380 | } |
300 | 381 | ||
301 | function load (id, callback) { | 382 | function load (id, callback) { |
302 | this.findById(id, callback) | 383 | return this.findById(id).asCallback(callback) |
303 | } | 384 | } |
304 | 385 | ||
305 | function search (value, field, start, count, sort, callback) { | 386 | function loadAndPopulateAuthor (id, callback) { |
306 | const query = {} | 387 | const options = { |
388 | include: [ this.sequelize.models.Author ] | ||
389 | } | ||
390 | |||
391 | return this.findById(id, options).asCallback(callback) | ||
392 | } | ||
393 | |||
394 | function loadAndPopulateAuthorAndPod (id, callback) { | ||
395 | const options = { | ||
396 | include: [ | ||
397 | { | ||
398 | model: this.sequelize.models.Author, | ||
399 | include: [ this.sequelize.models.Pod ] | ||
400 | } | ||
401 | ] | ||
402 | } | ||
403 | |||
404 | return this.findById(id, options).asCallback(callback) | ||
405 | } | ||
406 | |||
407 | function searchAndPopulateAuthorAndPod (value, field, start, count, sort, callback) { | ||
408 | const podInclude = { | ||
409 | model: this.sequelize.models.Pod | ||
410 | } | ||
411 | const authorInclude = { | ||
412 | model: this.sequelize.models.Author, | ||
413 | include: [ | ||
414 | podInclude | ||
415 | ] | ||
416 | } | ||
417 | |||
418 | const query = { | ||
419 | where: {}, | ||
420 | include: [ | ||
421 | authorInclude | ||
422 | ], | ||
423 | offset: start, | ||
424 | limit: count, | ||
425 | order: [ modelUtils.getSort(sort) ] | ||
426 | } | ||
427 | |||
428 | // TODO: include our pod for podHost searches (we are not stored in the database) | ||
307 | // Make an exact search with the magnet | 429 | // Make an exact search with the magnet |
308 | if (field === 'magnetUri') { | 430 | if (field === 'magnetUri') { |
309 | const infoHash = magnetUtil.decode(value).infoHash | 431 | const infoHash = magnetUtil.decode(value).infoHash |
310 | query.magnet = { | 432 | query.where.infoHash = infoHash |
311 | infoHash | ||
312 | } | ||
313 | } else if (field === 'tags') { | 433 | } else if (field === 'tags') { |
314 | query[field] = value | 434 | query.where[field] = value |
435 | } else if (field === 'host') { | ||
436 | const whereQuery = { | ||
437 | '$Author.Pod.host$': { | ||
438 | $like: '%' + value + '%' | ||
439 | } | ||
440 | } | ||
441 | |||
442 | // Include our pod? (not stored in the database) | ||
443 | if (constants.CONFIG.WEBSERVER.HOST.indexOf(value) !== -1) { | ||
444 | query.where = { | ||
445 | $or: [ | ||
446 | whereQuery, | ||
447 | { | ||
448 | remoteId: null | ||
449 | } | ||
450 | ] | ||
451 | } | ||
452 | } else { | ||
453 | query.where = whereQuery | ||
454 | } | ||
455 | } else if (field === 'author') { | ||
456 | query.where = { | ||
457 | '$Author.name$': { | ||
458 | $like: '%' + value + '%' | ||
459 | } | ||
460 | } | ||
315 | } else { | 461 | } else { |
316 | query[field] = new RegExp(value, 'i') | 462 | query.where[field] = { |
463 | $like: '%' + value + '%' | ||
464 | } | ||
317 | } | 465 | } |
318 | 466 | ||
319 | modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) | 467 | return this.findAndCountAll(query).asCallback(function (err, result) { |
468 | if (err) return callback(err) | ||
469 | |||
470 | return callback(null, result.rows, result.count) | ||
471 | }) | ||
320 | } | 472 | } |
321 | 473 | ||
322 | // --------------------------------------------------------------------------- | 474 | // --------------------------------------------------------------------------- |
diff --git a/server/tests/api/check-params.js b/server/tests/api/check-params.js index 444c2fc55..d9e51770c 100644 --- a/server/tests/api/check-params.js +++ b/server/tests/api/check-params.js | |||
@@ -465,7 +465,7 @@ describe('Test parameters validator', function () { | |||
465 | 465 | ||
466 | it('Should return 404 with an incorrect video', function (done) { | 466 | it('Should return 404 with an incorrect video', function (done) { |
467 | request(server.url) | 467 | request(server.url) |
468 | .get(path + '123456789012345678901234') | 468 | .get(path + '4da6fde3-88f7-4d16-b119-108df5630b06') |
469 | .set('Accept', 'application/json') | 469 | .set('Accept', 'application/json') |
470 | .expect(404, done) | 470 | .expect(404, done) |
471 | }) | 471 | }) |
@@ -490,7 +490,7 @@ describe('Test parameters validator', function () { | |||
490 | 490 | ||
491 | it('Should fail with a video which does not exist', function (done) { | 491 | it('Should fail with a video which does not exist', function (done) { |
492 | request(server.url) | 492 | request(server.url) |
493 | .delete(path + '123456789012345678901234') | 493 | .delete(path + '4da6fde3-88f7-4d16-b119-108df5630b06') |
494 | .set('Authorization', 'Bearer ' + server.accessToken) | 494 | .set('Authorization', 'Bearer ' + server.accessToken) |
495 | .expect(404, done) | 495 | .expect(404, done) |
496 | }) | 496 | }) |
@@ -711,7 +711,7 @@ describe('Test parameters validator', function () { | |||
711 | 711 | ||
712 | it('Should return 404 with a non existing id', function (done) { | 712 | it('Should return 404 with a non existing id', function (done) { |
713 | request(server.url) | 713 | request(server.url) |
714 | .delete(path + '579f982228c99c221d8092b8') | 714 | .delete(path + '45') |
715 | .set('Authorization', 'Bearer ' + server.accessToken) | 715 | .set('Authorization', 'Bearer ' + server.accessToken) |
716 | .expect(404, done) | 716 | .expect(404, done) |
717 | }) | 717 | }) |
diff --git a/server/tests/api/friends-basic.js b/server/tests/api/friends-basic.js index a871f9838..3a904dbd7 100644 --- a/server/tests/api/friends-basic.js +++ b/server/tests/api/friends-basic.js | |||
@@ -97,7 +97,7 @@ describe('Test basic friends', function () { | |||
97 | const pod = result[0] | 97 | const pod = result[0] |
98 | expect(pod.host).to.equal(servers[2].host) | 98 | expect(pod.host).to.equal(servers[2].host) |
99 | expect(pod.score).to.equal(20) | 99 | expect(pod.score).to.equal(20) |
100 | expect(miscsUtils.dateIsValid(pod.createdDate)).to.be.true | 100 | expect(miscsUtils.dateIsValid(pod.createdAt)).to.be.true |
101 | 101 | ||
102 | next() | 102 | next() |
103 | }) | 103 | }) |
@@ -114,7 +114,7 @@ describe('Test basic friends', function () { | |||
114 | const pod = result[0] | 114 | const pod = result[0] |
115 | expect(pod.host).to.equal(servers[1].host) | 115 | expect(pod.host).to.equal(servers[1].host) |
116 | expect(pod.score).to.equal(20) | 116 | expect(pod.score).to.equal(20) |
117 | expect(miscsUtils.dateIsValid(pod.createdDate)).to.be.true | 117 | expect(miscsUtils.dateIsValid(pod.createdAt)).to.be.true |
118 | 118 | ||
119 | next() | 119 | next() |
120 | }) | 120 | }) |
diff --git a/server/tests/api/multiple-pods.js b/server/tests/api/multiple-pods.js index be278d7c5..f0fe59c5f 100644 --- a/server/tests/api/multiple-pods.js +++ b/server/tests/api/multiple-pods.js | |||
@@ -104,7 +104,7 @@ describe('Test multiple pods', function () { | |||
104 | expect(video.magnetUri).to.exist | 104 | expect(video.magnetUri).to.exist |
105 | expect(video.duration).to.equal(10) | 105 | expect(video.duration).to.equal(10) |
106 | expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) | 106 | expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) |
107 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 107 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
108 | expect(video.author).to.equal('root') | 108 | expect(video.author).to.equal('root') |
109 | 109 | ||
110 | if (server.url !== 'http://localhost:9001') { | 110 | if (server.url !== 'http://localhost:9001') { |
@@ -166,7 +166,7 @@ describe('Test multiple pods', function () { | |||
166 | expect(video.magnetUri).to.exist | 166 | expect(video.magnetUri).to.exist |
167 | expect(video.duration).to.equal(5) | 167 | expect(video.duration).to.equal(5) |
168 | expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) | 168 | expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) |
169 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 169 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
170 | expect(video.author).to.equal('root') | 170 | expect(video.author).to.equal('root') |
171 | 171 | ||
172 | if (server.url !== 'http://localhost:9002') { | 172 | if (server.url !== 'http://localhost:9002') { |
@@ -246,7 +246,7 @@ describe('Test multiple pods', function () { | |||
246 | expect(video1.duration).to.equal(5) | 246 | expect(video1.duration).to.equal(5) |
247 | expect(video1.tags).to.deep.equal([ 'tag1p3' ]) | 247 | expect(video1.tags).to.deep.equal([ 'tag1p3' ]) |
248 | expect(video1.author).to.equal('root') | 248 | expect(video1.author).to.equal('root') |
249 | expect(miscsUtils.dateIsValid(video1.createdDate)).to.be.true | 249 | expect(miscsUtils.dateIsValid(video1.createdAt)).to.be.true |
250 | 250 | ||
251 | expect(video2.name).to.equal('my super name for pod 3-2') | 251 | expect(video2.name).to.equal('my super name for pod 3-2') |
252 | expect(video2.description).to.equal('my super description for pod 3-2') | 252 | expect(video2.description).to.equal('my super description for pod 3-2') |
@@ -255,7 +255,7 @@ describe('Test multiple pods', function () { | |||
255 | expect(video2.duration).to.equal(5) | 255 | expect(video2.duration).to.equal(5) |
256 | expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) | 256 | expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) |
257 | expect(video2.author).to.equal('root') | 257 | expect(video2.author).to.equal('root') |
258 | expect(miscsUtils.dateIsValid(video2.createdDate)).to.be.true | 258 | expect(miscsUtils.dateIsValid(video2.createdAt)).to.be.true |
259 | 259 | ||
260 | if (server.url !== 'http://localhost:9003') { | 260 | if (server.url !== 'http://localhost:9003') { |
261 | expect(video1.isLocal).to.be.false | 261 | expect(video1.isLocal).to.be.false |
diff --git a/server/tests/api/requests.js b/server/tests/api/requests.js index af36f6e34..7e790b54b 100644 --- a/server/tests/api/requests.js +++ b/server/tests/api/requests.js | |||
@@ -69,7 +69,7 @@ describe('Test requests stats', function () { | |||
69 | }) | 69 | }) |
70 | }) | 70 | }) |
71 | 71 | ||
72 | it('Should have the correct request', function (done) { | 72 | it('Should have the correct total request', function (done) { |
73 | this.timeout(15000) | 73 | this.timeout(15000) |
74 | 74 | ||
75 | const server = servers[0] | 75 | const server = servers[0] |
@@ -83,11 +83,7 @@ describe('Test requests stats', function () { | |||
83 | if (err) throw err | 83 | if (err) throw err |
84 | 84 | ||
85 | const body = res.body | 85 | const body = res.body |
86 | expect(body.requests).to.have.lengthOf(1) | 86 | expect(body.totalRequests).to.equal(1) |
87 | |||
88 | const request = body.requests[0] | ||
89 | expect(request.to).to.have.lengthOf(1) | ||
90 | expect(request.request.type).to.equal('add') | ||
91 | 87 | ||
92 | // Wait one cycle | 88 | // Wait one cycle |
93 | setTimeout(done, 10000) | 89 | setTimeout(done, 10000) |
@@ -95,27 +91,6 @@ describe('Test requests stats', function () { | |||
95 | }) | 91 | }) |
96 | }) | 92 | }) |
97 | 93 | ||
98 | it('Should have the correct requests', function (done) { | ||
99 | const server = servers[0] | ||
100 | |||
101 | uploadVideo(server, function (err) { | ||
102 | if (err) throw err | ||
103 | |||
104 | getRequestsStats(server, function (err, res) { | ||
105 | if (err) throw err | ||
106 | |||
107 | const body = res.body | ||
108 | expect(body.requests).to.have.lengthOf(2) | ||
109 | |||
110 | const request = body.requests[1] | ||
111 | expect(request.to).to.have.lengthOf(1) | ||
112 | expect(request.request.type).to.equal('add') | ||
113 | |||
114 | done() | ||
115 | }) | ||
116 | }) | ||
117 | }) | ||
118 | |||
119 | after(function (done) { | 94 | after(function (done) { |
120 | process.kill(-servers[0].app.pid) | 95 | process.kill(-servers[0].app.pid) |
121 | 96 | ||
diff --git a/server/tests/api/single-pod.js b/server/tests/api/single-pod.js index 65d1a7a65..aedecacf3 100644 --- a/server/tests/api/single-pod.js +++ b/server/tests/api/single-pod.js | |||
@@ -82,7 +82,7 @@ describe('Test a single pod', function () { | |||
82 | expect(video.author).to.equal('root') | 82 | expect(video.author).to.equal('root') |
83 | expect(video.isLocal).to.be.true | 83 | expect(video.isLocal).to.be.true |
84 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) | 84 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) |
85 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 85 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
86 | 86 | ||
87 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { | 87 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { |
88 | if (err) throw err | 88 | if (err) throw err |
@@ -116,7 +116,7 @@ describe('Test a single pod', function () { | |||
116 | expect(video.author).to.equal('root') | 116 | expect(video.author).to.equal('root') |
117 | expect(video.isLocal).to.be.true | 117 | expect(video.isLocal).to.be.true |
118 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) | 118 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) |
119 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 119 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
120 | 120 | ||
121 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { | 121 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { |
122 | if (err) throw err | 122 | if (err) throw err |
@@ -142,7 +142,7 @@ describe('Test a single pod', function () { | |||
142 | expect(video.author).to.equal('root') | 142 | expect(video.author).to.equal('root') |
143 | expect(video.isLocal).to.be.true | 143 | expect(video.isLocal).to.be.true |
144 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) | 144 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) |
145 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 145 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
146 | 146 | ||
147 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { | 147 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { |
148 | if (err) throw err | 148 | if (err) throw err |
@@ -154,7 +154,7 @@ describe('Test a single pod', function () { | |||
154 | }) | 154 | }) |
155 | 155 | ||
156 | it('Should search the video by podHost', function (done) { | 156 | it('Should search the video by podHost', function (done) { |
157 | videosUtils.searchVideo(server.url, '9001', 'podHost', function (err, res) { | 157 | videosUtils.searchVideo(server.url, '9001', 'host', function (err, res) { |
158 | if (err) throw err | 158 | if (err) throw err |
159 | 159 | ||
160 | expect(res.body.total).to.equal(1) | 160 | expect(res.body.total).to.equal(1) |
@@ -168,7 +168,7 @@ describe('Test a single pod', function () { | |||
168 | expect(video.author).to.equal('root') | 168 | expect(video.author).to.equal('root') |
169 | expect(video.isLocal).to.be.true | 169 | expect(video.isLocal).to.be.true |
170 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) | 170 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) |
171 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 171 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
172 | 172 | ||
173 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { | 173 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { |
174 | if (err) throw err | 174 | if (err) throw err |
@@ -194,7 +194,7 @@ describe('Test a single pod', function () { | |||
194 | expect(video.author).to.equal('root') | 194 | expect(video.author).to.equal('root') |
195 | expect(video.isLocal).to.be.true | 195 | expect(video.isLocal).to.be.true |
196 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) | 196 | expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) |
197 | expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true | 197 | expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true |
198 | 198 | ||
199 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { | 199 | videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { |
200 | if (err) throw err | 200 | if (err) throw err |
@@ -425,7 +425,7 @@ describe('Test a single pod', function () { | |||
425 | }) | 425 | }) |
426 | 426 | ||
427 | it('Should search all the 9001 port videos', function (done) { | 427 | it('Should search all the 9001 port videos', function (done) { |
428 | videosUtils.searchVideoWithPagination(server.url, '9001', 'podHost', 0, 15, function (err, res) { | 428 | videosUtils.searchVideoWithPagination(server.url, '9001', 'host', 0, 15, function (err, res) { |
429 | if (err) throw err | 429 | if (err) throw err |
430 | 430 | ||
431 | const videos = res.body.data | 431 | const videos = res.body.data |
@@ -437,7 +437,7 @@ describe('Test a single pod', function () { | |||
437 | }) | 437 | }) |
438 | 438 | ||
439 | it('Should search all the localhost videos', function (done) { | 439 | it('Should search all the localhost videos', function (done) { |
440 | videosUtils.searchVideoWithPagination(server.url, 'localhost', 'podHost', 0, 15, function (err, res) { | 440 | videosUtils.searchVideoWithPagination(server.url, 'localhost', 'host', 0, 15, function (err, res) { |
441 | if (err) throw err | 441 | if (err) throw err |
442 | 442 | ||
443 | const videos = res.body.data | 443 | const videos = res.body.data |
diff --git a/server/tests/api/users.js b/server/tests/api/users.js index 94267f104..e6d937eb0 100644 --- a/server/tests/api/users.js +++ b/server/tests/api/users.js | |||
@@ -261,8 +261,8 @@ describe('Test users', function () { | |||
261 | }) | 261 | }) |
262 | }) | 262 | }) |
263 | 263 | ||
264 | it('Should list only the second user by createdDate desc', function (done) { | 264 | it('Should list only the second user by createdAt desc', function (done) { |
265 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-createdDate', function (err, res) { | 265 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-createdAt', function (err, res) { |
266 | if (err) throw err | 266 | if (err) throw err |
267 | 267 | ||
268 | const result = res.body | 268 | const result = res.body |
@@ -279,8 +279,8 @@ describe('Test users', function () { | |||
279 | }) | 279 | }) |
280 | }) | 280 | }) |
281 | 281 | ||
282 | it('Should list all the users by createdDate asc', function (done) { | 282 | it('Should list all the users by createdAt asc', function (done) { |
283 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 2, 'createdDate', function (err, res) { | 283 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 2, 'createdAt', function (err, res) { |
284 | if (err) throw err | 284 | if (err) throw err |
285 | 285 | ||
286 | const result = res.body | 286 | const result = res.body |
diff --git a/server/tests/utils/servers.js b/server/tests/utils/servers.js index 01c9a2f39..4e55f8f5c 100644 --- a/server/tests/utils/servers.js +++ b/server/tests/utils/servers.js | |||
@@ -60,12 +60,12 @@ function runServer (number, callback) { | |||
60 | 60 | ||
61 | // These actions are async so we need to be sure that they have both been done | 61 | // These actions are async so we need to be sure that they have both been done |
62 | const serverRunString = { | 62 | const serverRunString = { |
63 | 'Connected to mongodb': false, | 63 | 'Database is ready': false, |
64 | 'Server listening on port': false | 64 | 'Server listening on port': false |
65 | } | 65 | } |
66 | 66 | ||
67 | const regexps = { | 67 | const regexps = { |
68 | client_id: 'Client id: ([a-f0-9]+)', | 68 | client_id: 'Client id: (.+)', |
69 | client_secret: 'Client secret: (.+)', | 69 | client_secret: 'Client secret: (.+)', |
70 | user_username: 'Username: (.+)', | 70 | user_username: 'Username: (.+)', |
71 | user_password: 'User password: (.+)' | 71 | user_password: 'User password: (.+)' |
diff --git a/server/tests/utils/videos.js b/server/tests/utils/videos.js index 536093db1..5c120597f 100644 --- a/server/tests/utils/videos.js +++ b/server/tests/utils/videos.js | |||
@@ -25,7 +25,7 @@ function getAllVideosListBy (url, end) { | |||
25 | 25 | ||
26 | request(url) | 26 | request(url) |
27 | .get(path) | 27 | .get(path) |
28 | .query({ sort: 'createdDate' }) | 28 | .query({ sort: 'createdAt' }) |
29 | .query({ start: 0 }) | 29 | .query({ start: 0 }) |
30 | .query({ count: 10000 }) | 30 | .query({ count: 10000 }) |
31 | .set('Accept', 'application/json') | 31 | .set('Accept', 'application/json') |