1
0
Fork 0

First version with PostgreSQL

This commit is contained in:
Chocobozzz 2016-12-11 21:50:51 +01:00
parent 108626609e
commit feb4bdfd9b
68 changed files with 1171 additions and 730 deletions

View File

@ -15,7 +15,7 @@
<td>{{ friend.id }}</td> <td>{{ friend.id }}</td>
<td>{{ friend.host }}</td> <td>{{ friend.host }}</td>
<td>{{ friend.score }}</td> <td>{{ friend.score }}</td>
<td>{{ friend.createdDate | date: 'medium' }}</td> <td>{{ friend.createdAt | date: 'medium' }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -2,5 +2,5 @@ export interface Friend {
id: string; id: string;
host: string; host: string;
score: number; score: number;
createdDate: Date; createdAt: Date;
} }

View File

@ -18,6 +18,6 @@
<div> <div>
<span class="label-description">Remaining requests:</span> <span class="label-description">Remaining requests:</span>
{{ stats.requests.length }} {{ stats.totalRequests }}
</div> </div>
</div> </div>

View File

@ -19,7 +19,7 @@ export class RequestStatsComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
if (this.stats.secondsInterval !== null) { if (this.stats !== null && this.stats.secondsInterval !== null) {
clearInterval(this.interval); clearInterval(this.interval);
} }
} }

View File

@ -7,18 +7,18 @@ export class RequestStats {
maxRequestsInParallel: number; maxRequestsInParallel: number;
milliSecondsInterval: number; milliSecondsInterval: number;
remainingMilliSeconds: number; remainingMilliSeconds: number;
requests: Request[]; totalRequests: number;
constructor(hash: { constructor(hash: {
maxRequestsInParallel: number, maxRequestsInParallel: number,
milliSecondsInterval: number, milliSecondsInterval: number,
remainingMilliSeconds: number, remainingMilliSeconds: number,
requests: Request[]; totalRequests: number;
}) { }) {
this.maxRequestsInParallel = hash.maxRequestsInParallel; this.maxRequestsInParallel = hash.maxRequestsInParallel;
this.milliSecondsInterval = hash.milliSecondsInterval; this.milliSecondsInterval = hash.milliSecondsInterval;
this.remainingMilliSeconds = hash.remainingMilliSeconds; this.remainingMilliSeconds = hash.remainingMilliSeconds;
this.requests = hash.requests; this.totalRequests = hash.totalRequests;
} }
get remainingSeconds() { get remainingSeconds() {

View File

@ -14,7 +14,7 @@
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td>{{ user.id }}</td> <td>{{ user.id }}</td>
<td>{{ user.username }}</td> <td>{{ user.username }}</td>
<td>{{ user.createdDate | date: 'medium' }}</td> <td>{{ user.createdAt | date: 'medium' }}</td>
<td class="text-right"> <td class="text-right">
<span class="glyphicon glyphicon-remove" *ngIf="!user.isAdmin()" (click)="removeUser(user)"></span> <span class="glyphicon glyphicon-remove" *ngIf="!user.isAdmin()" (click)="removeUser(user)"></span>
</td> </td>

View File

@ -7,9 +7,6 @@ export class AuthUser extends User {
USERNAME: 'username' USERNAME: 'username'
}; };
id: string;
role: string;
username: string;
tokens: Tokens; tokens: Tokens;
static load() { static load() {
@ -17,7 +14,7 @@ export class AuthUser extends User {
if (usernameLocalStorage) { if (usernameLocalStorage) {
return new AuthUser( return new AuthUser(
{ {
id: localStorage.getItem(this.KEYS.ID), id: parseInt(localStorage.getItem(this.KEYS.ID)),
username: localStorage.getItem(this.KEYS.USERNAME), username: localStorage.getItem(this.KEYS.USERNAME),
role: localStorage.getItem(this.KEYS.ROLE) role: localStorage.getItem(this.KEYS.ROLE)
}, },
@ -35,7 +32,7 @@ export class AuthUser extends User {
Tokens.flush(); Tokens.flush();
} }
constructor(userHash: { id: string, username: string, role: string }, hashTokens: any) { constructor(userHash: { id: number, username: string, role: string }, hashTokens: any) {
super(userHash); super(userHash);
this.tokens = new Tokens(hashTokens); this.tokens = new Tokens(hashTokens);
} }
@ -58,7 +55,7 @@ export class AuthUser extends User {
} }
save() { save() {
localStorage.setItem(AuthUser.KEYS.ID, this.id); localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
localStorage.setItem(AuthUser.KEYS.USERNAME, this.username); localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
localStorage.setItem(AuthUser.KEYS.ROLE, this.role); localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
this.tokens.save(); this.tokens.save();

View File

@ -1 +1 @@
export type SearchField = "name" | "author" | "podUrl" | "magnetUri" | "tags"; export type SearchField = "name" | "author" | "host" | "magnetUri" | "tags";

View File

@ -14,8 +14,8 @@ export class SearchComponent implements OnInit {
fieldChoices = { fieldChoices = {
name: 'Name', name: 'Name',
author: 'Author', author: 'Author',
podUrl: 'Pod Url', host: 'Pod Host',
magnetUri: 'Magnet Uri', magnetUri: 'Magnet URI',
tags: 'Tags' tags: 'Tags'
}; };
searchCriterias: Search = { searchCriterias: Search = {

View File

@ -1,16 +1,16 @@
export class User { export class User {
id: string; id: number;
username: string; username: string;
role: string; role: string;
createdDate: Date; createdAt: Date;
constructor(hash: { id: string, username: string, role: string, createdDate?: Date }) { constructor(hash: { id: number, username: string, role: string, createdAt?: Date }) {
this.id = hash.id; this.id = hash.id;
this.username = hash.username; this.username = hash.username;
this.role = hash.role; this.role = hash.role;
if (hash.createdDate) { if (hash.createdAt) {
this.createdDate = hash.createdDate; this.createdAt = hash.createdAt;
} }
} }

View File

@ -1,3 +1,3 @@
export type SortField = "name" | "-name" export type SortField = "name" | "-name"
| "duration" | "-duration" | "duration" | "-duration"
| "createdDate" | "-createdDate"; | "createdAt" | "-createdAt";

View File

@ -1,7 +1,7 @@
export class Video { export class Video {
author: string; author: string;
by: string; by: string;
createdDate: Date; createdAt: Date;
description: string; description: string;
duration: string; duration: string;
id: string; id: string;
@ -27,7 +27,7 @@ export class Video {
constructor(hash: { constructor(hash: {
author: string, author: string,
createdDate: string, createdAt: string,
description: string, description: string,
duration: number; duration: number;
id: string, id: string,
@ -39,7 +39,7 @@ export class Video {
thumbnailPath: string thumbnailPath: string
}) { }) {
this.author = hash.author; this.author = hash.author;
this.createdDate = new Date(hash.createdDate); this.createdAt = new Date(hash.createdAt);
this.description = hash.description; this.description = hash.description;
this.duration = Video.createDurationString(hash.duration); this.duration = Video.createDurationString(hash.duration);
this.id = hash.id; this.id = hash.id;

View File

@ -145,7 +145,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
}; };
} }
this.sort = <SortField>routeParams['sort'] || '-createdDate'; this.sort = <SortField>routeParams['sort'] || '-createdAt';
if (routeParams['page'] !== undefined) { if (routeParams['page'] !== undefined) {
this.pagination.currentPage = parseInt(routeParams['page']); this.pagination.currentPage = parseInt(routeParams['page']);

View File

@ -23,6 +23,6 @@
</span> </span>
<a [routerLink]="['/videos/list', { field: 'author', search: video.author, sort: currentSort }]" class="video-miniature-author">{{ video.by }}</a> <a [routerLink]="['/videos/list', { field: 'author', search: video.author, sort: currentSort }]" class="video-miniature-author">{{ video.by }}</a>
<span class="video-miniature-created-date">{{ video.createdDate | date:'short' }}</span> <span class="video-miniature-created-at">{{ video.createdAt | date:'short' }}</span>
</div> </div>
</div> </div>

View File

@ -79,7 +79,7 @@
} }
} }
.video-miniature-author, .video-miniature-created-date { .video-miniature-author, .video-miniature-created-at {
display: block; display: block;
margin-left: 1px; margin-left: 1px;
font-size: 12px; font-size: 12px;

View File

@ -17,8 +17,8 @@ export class VideoSortComponent {
'-name': 'Name - Desc', '-name': 'Name - Desc',
'duration': 'Duration - Asc', 'duration': 'Duration - Asc',
'-duration': 'Duration - Desc', '-duration': 'Duration - Desc',
'createdDate': 'Created Date - Asc', 'createdAt': 'Created Date - Asc',
'-createdDate': 'Created Date - Desc' '-createdAt': 'Created Date - Desc'
}; };
get choiceKeys() { get choiceKeys() {

View File

@ -47,7 +47,7 @@
{{ video.by }} {{ video.by }}
</a> </a>
</span> </span>
<span id="video-date">on {{ video.createdDate | date:'short' }}</span> <span id="video-date">on {{ video.createdAt | date:'short' }}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,8 +8,8 @@ webserver:
database: database:
hostname: 'localhost' hostname: 'localhost'
port: 27017 port: 5432
suffix: '-dev' suffix: '_dev'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -5,4 +5,4 @@ webserver:
port: 80 port: 80
database: database:
suffix: '-prod' suffix: '_prod'

View File

@ -6,7 +6,7 @@ webserver:
port: 9001 port: 9001
database: database:
suffix: '-test1' suffix: '_test1'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,7 +6,7 @@ webserver:
port: 9002 port: 9002
database: database:
suffix: '-test2' suffix: '_test2'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,7 +6,7 @@ webserver:
port: 9003 port: 9003
database: database:
suffix: '-test3' suffix: '_test3'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,7 +6,7 @@ webserver:
port: 9004 port: 9004
database: database:
suffix: '-test4' suffix: '_test4'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,7 +6,7 @@ webserver:
port: 9005 port: 9005
database: database:
suffix: '-test5' suffix: '_test5'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,7 +6,7 @@ webserver:
port: 9006 port: 9006
database: database:
suffix: '-test6' suffix: '_test6'
# From the project root directory # From the project root directory
storage: storage:

View File

@ -6,4 +6,4 @@ webserver:
database: database:
hostname: 'localhost' hostname: 'localhost'
port: 27017 port: 5432

View File

@ -56,16 +56,18 @@
"lodash": "^4.11.1", "lodash": "^4.11.1",
"magnet-uri": "^5.1.4", "magnet-uri": "^5.1.4",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mongoose": "^4.0.5",
"morgan": "^1.5.3", "morgan": "^1.5.3",
"multer": "^1.1.0", "multer": "^1.1.0",
"openssl-wrapper": "^0.3.4", "openssl-wrapper": "^0.3.4",
"parse-torrent": "^5.8.0", "parse-torrent": "^5.8.0",
"password-generator": "^2.0.2", "password-generator": "^2.0.2",
"pg": "^6.1.0",
"pg-hstore": "^2.3.2",
"request": "^2.57.0", "request": "^2.57.0",
"request-replay": "^1.0.2", "request-replay": "^1.0.2",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"scripty": "^1.5.0", "scripty": "^1.5.0",
"sequelize": "^3.27.0",
"ursa": "^0.9.1", "ursa": "^0.9.1",
"winston": "^2.1.1", "winston": "^2.1.1",
"ws": "^1.1.1" "ws": "^1.1.1"

View File

@ -1,6 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env sh
for i in $(seq 1 6); do for i in $(seq 1 6); do
printf "use peertube-test%s;\ndb.dropDatabase();" "$i" | mongo dropdb "peertube_test$i"
rm -rf "./test$i" rm -rf "./test$i"
createdb "peertube_test$i"
done done

View File

@ -17,10 +17,9 @@ const app = express()
// ----------- Database ----------- // ----------- Database -----------
const constants = require('./server/initializers/constants') const constants = require('./server/initializers/constants')
const database = require('./server/initializers/database')
const logger = require('./server/helpers/logger') const logger = require('./server/helpers/logger')
// Initialize database and models
database.connect() const db = require('./server/initializers/database')
// ----------- Checker ----------- // ----------- Checker -----------
const checker = require('./server/initializers/checker') const checker = require('./server/initializers/checker')
@ -39,9 +38,7 @@ if (errorMessage !== null) {
const customValidators = require('./server/helpers/custom-validators') const customValidators = require('./server/helpers/custom-validators')
const installer = require('./server/initializers/installer') const installer = require('./server/initializers/installer')
const migrator = require('./server/initializers/migrator') const migrator = require('./server/initializers/migrator')
const mongoose = require('mongoose')
const routes = require('./server/controllers') const routes = require('./server/controllers')
const Request = mongoose.model('Request')
// ----------- Command line ----------- // ----------- Command line -----------
@ -130,7 +127,7 @@ installer.installApplication(function (err) {
// ----------- Make the server listening ----------- // ----------- Make the server listening -----------
server.listen(port, function () { server.listen(port, function () {
// Activate the pool requests // Activate the pool requests
Request.activate() db.Request.activate()
logger.info('Server listening on port %d', port) logger.info('Server listening on port %d', port)
logger.info('Webserver: %s', constants.CONFIG.WEBSERVER.URL) logger.info('Webserver: %s', constants.CONFIG.WEBSERVER.URL)

View File

@ -1,13 +1,11 @@
'use strict' 'use strict'
const express = require('express') const express = require('express')
const mongoose = require('mongoose')
const constants = require('../../initializers/constants') const constants = require('../../initializers/constants')
const db = require('../../initializers/database')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const Client = mongoose.model('OAuthClient')
const router = express.Router() const router = express.Router()
router.get('/local', getLocalClient) router.get('/local', getLocalClient)
@ -27,12 +25,12 @@ function getLocalClient (req, res, next) {
return res.type('json').status(403).end() return res.type('json').status(403).end()
} }
Client.loadFirstClient(function (err, client) { db.OAuthClient.loadFirstClient(function (err, client) {
if (err) return next(err) if (err) return next(err)
if (!client) return next(new Error('No client available.')) if (!client) return next(new Error('No client available.'))
res.json({ res.json({
client_id: client._id, client_id: client.clientId,
client_secret: client.clientSecret client_secret: client.clientSecret
}) })
}) })

View File

@ -1,9 +1,9 @@
'use strict' 'use strict'
const express = require('express') const express = require('express')
const mongoose = require('mongoose')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const db = require('../../initializers/database')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const friends = require('../../lib/friends') const friends = require('../../lib/friends')
const middlewares = require('../../middlewares') const middlewares = require('../../middlewares')
@ -15,7 +15,6 @@ const validators = middlewares.validators.pods
const signatureValidator = middlewares.validators.remote.signature const signatureValidator = middlewares.validators.remote.signature
const router = express.Router() const router = express.Router()
const Pod = mongoose.model('Pod')
router.get('/', listPods) router.get('/', listPods)
router.post('/', router.post('/',
@ -53,15 +52,15 @@ function addPods (req, res, next) {
waterfall([ waterfall([
function addPod (callback) { function addPod (callback) {
const pod = new Pod(informations) const pod = db.Pod.build(informations)
pod.save(function (err, podCreated) { pod.save().asCallback(function (err, podCreated) {
// Be sure about the number of parameters for the callback // Be sure about the number of parameters for the callback
return callback(err, podCreated) return callback(err, podCreated)
}) })
}, },
function sendMyVideos (podCreated, callback) { function sendMyVideos (podCreated, callback) {
friends.sendOwnedVideosToPod(podCreated._id) friends.sendOwnedVideosToPod(podCreated.id)
callback(null) callback(null)
}, },
@ -84,7 +83,7 @@ function addPods (req, res, next) {
} }
function listPods (req, res, next) { function listPods (req, res, next) {
Pod.list(function (err, podsList) { db.Pod.list(function (err, podsList) {
if (err) return next(err) if (err) return next(err)
res.json(getFormatedPods(podsList)) res.json(getFormatedPods(podsList))
@ -111,11 +110,11 @@ function removePods (req, res, next) {
waterfall([ waterfall([
function loadPod (callback) { function loadPod (callback) {
Pod.loadByHost(host, callback) db.Pod.loadByHost(host, callback)
}, },
function removePod (pod, callback) { function removePod (pod, callback) {
pod.remove(callback) pod.destroy().asCallback(callback)
} }
], function (err) { ], function (err) {
if (err) return next(err) if (err) return next(err)

View File

@ -3,15 +3,15 @@
const each = require('async/each') const each = require('async/each')
const eachSeries = require('async/eachSeries') const eachSeries = require('async/eachSeries')
const express = require('express') const express = require('express')
const mongoose = require('mongoose') const waterfall = require('async/waterfall')
const db = require('../../initializers/database')
const middlewares = require('../../middlewares') const middlewares = require('../../middlewares')
const secureMiddleware = middlewares.secure const secureMiddleware = middlewares.secure
const validators = middlewares.validators.remote const validators = middlewares.validators.remote
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const router = express.Router() const router = express.Router()
const Video = mongoose.model('Video')
router.post('/videos', router.post('/videos',
validators.signature, validators.signature,
@ -53,34 +53,99 @@ function remoteVideos (req, res, next) {
function addRemoteVideo (videoToCreateData, fromHost, callback) { function addRemoteVideo (videoToCreateData, fromHost, callback) {
logger.debug('Adding remote video "%s".', videoToCreateData.name) logger.debug('Adding remote video "%s".', videoToCreateData.name)
const video = new Video(videoToCreateData) waterfall([
video.podHost = fromHost
Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) { function findOrCreatePod (callback) {
if (err) { fromHost
logger.error('Cannot generate thumbnail from base 64 data.', { error: err })
return callback(err) const query = {
where: {
host: fromHost
},
defaults: {
host: fromHost
}
}
db.Pod.findOrCreate(query).asCallback(function (err, result) {
// [ instance, wasCreated ]
return callback(err, result[0])
})
},
function findOrCreateAuthor (pod, callback) {
const username = videoToCreateData.author
const query = {
where: {
name: username,
podId: pod.id
},
defaults: {
name: username,
podId: pod.id
}
}
db.Author.findOrCreate(query).asCallback(function (err, result) {
// [ instance, wasCreated ]
return callback(err, result[0])
})
},
function createVideoObject (author, callback) {
const videoData = {
name: videoToCreateData.name,
remoteId: videoToCreateData.remoteId,
extname: videoToCreateData.extname,
infoHash: videoToCreateData.infoHash,
description: videoToCreateData.description,
authorId: author.id,
duration: videoToCreateData.duration,
tags: videoToCreateData.tags
}
const video = db.Video.build(videoData)
return callback(null, video)
},
function generateThumbnail (video, callback) {
db.Video.generateThumbnailFromBase64(video, videoToCreateData.thumbnailBase64, function (err) {
if (err) {
logger.error('Cannot generate thumbnail from base 64 data.', { error: err })
return callback(err)
}
video.save().asCallback(callback)
})
},
function insertIntoDB (video, callback) {
video.save().asCallback(callback)
} }
video.save(callback) ], callback)
})
} }
function removeRemoteVideo (videoToRemoveData, fromHost, callback) { function removeRemoteVideo (videoToRemoveData, fromHost, callback) {
// TODO: use bulkDestroy?
// We need the list because we have to remove some other stuffs (thumbnail etc) // We need the list because we have to remove some other stuffs (thumbnail etc)
Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) { db.Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) {
if (err) { if (err) {
logger.error('Cannot list videos from host and magnets.', { error: err }) logger.error('Cannot list videos from host and remote id.', { error: err.message })
return callback(err) return callback(err)
} }
if (videosList.length === 0) { if (videosList.length === 0) {
logger.error('No remote video was found for this pod.', { magnetUri: videoToRemoveData.magnetUri, podHost: fromHost }) logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromHost })
} }
each(videosList, function (video, callbackEach) { each(videosList, function (video, callbackEach) {
logger.debug('Removing remote video %s.', video.magnetUri) logger.debug('Removing remote video %s.', video.remoteId)
video.remove(callbackEach) video.destroy().asCallback(callbackEach)
}, callback) }, callback)
}) })
} }

View File

@ -1,15 +1,13 @@
'use strict' 'use strict'
const express = require('express') const express = require('express')
const mongoose = require('mongoose')
const constants = require('../../initializers/constants') const constants = require('../../initializers/constants')
const db = require('../../initializers/database')
const middlewares = require('../../middlewares') const middlewares = require('../../middlewares')
const admin = middlewares.admin const admin = middlewares.admin
const oAuth = middlewares.oauth const oAuth = middlewares.oauth
const Request = mongoose.model('Request')
const router = express.Router() const router = express.Router()
router.get('/stats', router.get('/stats',
@ -25,13 +23,13 @@ module.exports = router
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function getStatsRequests (req, res, next) { function getStatsRequests (req, res, next) {
Request.list(function (err, requests) { db.Request.countTotalRequests(function (err, totalRequests) {
if (err) return next(err) if (err) return next(err)
return res.json({ return res.json({
requests: requests, totalRequests: totalRequests,
maxRequestsInParallel: constants.REQUESTS_IN_PARALLEL, maxRequestsInParallel: constants.REQUESTS_IN_PARALLEL,
remainingMilliSeconds: Request.remainingMilliSeconds(), remainingMilliSeconds: db.Request.remainingMilliSeconds(),
milliSecondsInterval: constants.REQUESTS_INTERVAL milliSecondsInterval: constants.REQUESTS_INTERVAL
}) })
}) })

View File

@ -2,10 +2,10 @@
const each = require('async/each') const each = require('async/each')
const express = require('express') const express = require('express')
const mongoose = require('mongoose')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const constants = require('../../initializers/constants') const constants = require('../../initializers/constants')
const db = require('../../initializers/database')
const friends = require('../../lib/friends') const friends = require('../../lib/friends')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const middlewares = require('../../middlewares') const middlewares = require('../../middlewares')
@ -17,9 +17,6 @@ const validatorsPagination = middlewares.validators.pagination
const validatorsSort = middlewares.validators.sort const validatorsSort = middlewares.validators.sort
const validatorsUsers = middlewares.validators.users const validatorsUsers = middlewares.validators.users
const User = mongoose.model('User')
const Video = mongoose.model('Video')
const router = express.Router() const router = express.Router()
router.get('/me', oAuth.authenticate, getUserInformation) router.get('/me', oAuth.authenticate, getUserInformation)
@ -62,13 +59,13 @@ module.exports = router
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function createUser (req, res, next) { function createUser (req, res, next) {
const user = new User({ const user = db.User.build({
username: req.body.username, username: req.body.username,
password: req.body.password, password: req.body.password,
role: constants.USER_ROLES.USER role: constants.USER_ROLES.USER
}) })
user.save(function (err, createdUser) { user.save().asCallback(function (err, createdUser) {
if (err) return next(err) if (err) return next(err)
return res.type('json').status(204).end() return res.type('json').status(204).end()
@ -76,7 +73,7 @@ function createUser (req, res, next) {
} }
function getUserInformation (req, res, next) { function getUserInformation (req, res, next) {
User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
if (err) return next(err) if (err) return next(err)
return res.json(user.toFormatedJSON()) return res.json(user.toFormatedJSON())
@ -84,7 +81,7 @@ function getUserInformation (req, res, next) {
} }
function listUsers (req, res, next) { function listUsers (req, res, next) {
User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) { db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
if (err) return next(err) if (err) return next(err)
res.json(getFormatedUsers(usersList, usersTotal)) res.json(getFormatedUsers(usersList, usersTotal))
@ -94,18 +91,19 @@ function listUsers (req, res, next) {
function removeUser (req, res, next) { function removeUser (req, res, next) {
waterfall([ waterfall([
function getUser (callback) { function getUser (callback) {
User.loadById(req.params.id, callback) db.User.loadById(req.params.id, callback)
}, },
// TODO: use foreignkey?
function getVideos (user, callback) { function getVideos (user, callback) {
Video.listOwnedByAuthor(user.username, function (err, videos) { db.Video.listOwnedByAuthor(user.username, function (err, videos) {
return callback(err, user, videos) return callback(err, user, videos)
}) })
}, },
function removeVideosFromDB (user, videos, callback) { function removeVideosFromDB (user, videos, callback) {
each(videos, function (video, callbackEach) { each(videos, function (video, callbackEach) {
video.remove(callbackEach) video.destroy().asCallback(callbackEach)
}, function (err) { }, function (err) {
return callback(err, user, videos) return callback(err, user, videos)
}) })
@ -115,7 +113,7 @@ function removeUser (req, res, next) {
videos.forEach(function (video) { videos.forEach(function (video) {
const params = { const params = {
name: video.name, name: video.name,
magnetUri: video.magnetUri remoteId: video.id
} }
friends.removeVideoToFriends(params) friends.removeVideoToFriends(params)
@ -125,7 +123,7 @@ function removeUser (req, res, next) {
}, },
function removeUserFromDB (user, callback) { function removeUserFromDB (user, callback) {
user.remove(callback) user.destroy().asCallback(callback)
} }
], function andFinally (err) { ], function andFinally (err) {
if (err) { if (err) {
@ -138,11 +136,11 @@ function removeUser (req, res, next) {
} }
function updateUser (req, res, next) { function updateUser (req, res, next) {
User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) { db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
if (err) return next(err) if (err) return next(err)
user.password = req.body.password user.password = req.body.password
user.save(function (err) { user.save().asCallback(function (err) {
if (err) return next(err) if (err) return next(err)
return res.sendStatus(204) return res.sendStatus(204)

View File

@ -2,12 +2,12 @@
const express = require('express') const express = require('express')
const fs = require('fs') const fs = require('fs')
const mongoose = require('mongoose')
const multer = require('multer') const multer = require('multer')
const path = require('path') const path = require('path')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const constants = require('../../initializers/constants') const constants = require('../../initializers/constants')
const db = require('../../initializers/database')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const friends = require('../../lib/friends') const friends = require('../../lib/friends')
const middlewares = require('../../middlewares') const middlewares = require('../../middlewares')
@ -22,7 +22,6 @@ const sort = middlewares.sort
const utils = require('../../helpers/utils') const utils = require('../../helpers/utils')
const router = express.Router() const router = express.Router()
const Video = mongoose.model('Video')
// multer configuration // multer configuration
const storage = multer.diskStorage({ const storage = multer.diskStorage({
@ -87,40 +86,60 @@ function addVideo (req, res, next) {
const videoInfos = req.body const videoInfos = req.body
waterfall([ waterfall([
function createVideoObject (callback) {
const id = mongoose.Types.ObjectId()
function findOrCreateAuthor (callback) {
const username = res.locals.oauth.token.user.username
const query = {
where: {
name: username,
podId: null
},
defaults: {
name: username,
podId: null // null because it is OUR pod
}
}
db.Author.findOrCreate(query).asCallback(function (err, result) {
// [ instance, wasCreated ]
return callback(err, result[0])
})
},
function createVideoObject (author, callback) {
const videoData = { const videoData = {
_id: id,
name: videoInfos.name, name: videoInfos.name,
remoteId: null, remoteId: null,
extname: path.extname(videoFile.filename), extname: path.extname(videoFile.filename),
description: videoInfos.description, description: videoInfos.description,
author: res.locals.oauth.token.user.username,
duration: videoFile.duration, duration: videoFile.duration,
tags: videoInfos.tags tags: videoInfos.tags,
authorId: author.id
} }
const video = new Video(videoData) const video = db.Video.build(videoData)
return callback(null, video) return callback(null, author, video)
}, },
// Set the videoname the same as the MongoDB id // Set the videoname the same as the id
function renameVideoFile (video, callback) { function renameVideoFile (author, video, callback) {
const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
const source = path.join(videoDir, videoFile.filename) const source = path.join(videoDir, videoFile.filename)
const destination = path.join(videoDir, video.getVideoFilename()) const destination = path.join(videoDir, video.getVideoFilename())
fs.rename(source, destination, function (err) { fs.rename(source, destination, function (err) {
return callback(err, video) return callback(err, author, video)
}) })
}, },
function insertIntoDB (video, callback) { function insertIntoDB (author, video, callback) {
video.save(function (err, video) { video.save().asCallback(function (err, videoCreated) {
// Assert there are only one argument sent to the next function (video) // Do not forget to add Author informations to the created video
return callback(err, video) videoCreated.Author = author
return callback(err, videoCreated)
}) })
}, },
@ -147,7 +166,7 @@ function addVideo (req, res, next) {
} }
function getVideo (req, res, next) { function getVideo (req, res, next) {
Video.load(req.params.id, function (err, video) { db.Video.loadAndPopulateAuthorAndPod(req.params.id, function (err, video) {
if (err) return next(err) if (err) return next(err)
if (!video) { if (!video) {
@ -159,7 +178,7 @@ function getVideo (req, res, next) {
} }
function listVideos (req, res, next) { function listVideos (req, res, next) {
Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
if (err) return next(err) if (err) return next(err)
res.json(getFormatedVideos(videosList, videosTotal)) res.json(getFormatedVideos(videosList, videosTotal))
@ -171,11 +190,11 @@ function removeVideo (req, res, next) {
waterfall([ waterfall([
function getVideo (callback) { function getVideo (callback) {
Video.load(videoId, callback) db.Video.load(videoId, callback)
}, },
function removeFromDB (video, callback) { function removeFromDB (video, callback) {
video.remove(function (err) { video.destroy().asCallback(function (err) {
if (err) return callback(err) if (err) return callback(err)
return callback(null, video) return callback(null, video)
@ -185,7 +204,7 @@ function removeVideo (req, res, next) {
function sendInformationToFriends (video, callback) { function sendInformationToFriends (video, callback) {
const params = { const params = {
name: video.name, name: video.name,
remoteId: video._id remoteId: video.id
} }
friends.removeVideoToFriends(params) friends.removeVideoToFriends(params)
@ -203,7 +222,7 @@ function removeVideo (req, res, next) {
} }
function searchVideos (req, res, next) { function searchVideos (req, res, next) {
Video.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, db.Video.searchAndPopulateAuthorAndPod(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
function (err, videosList, videosTotal) { function (err, videosList, videosTotal) {
if (err) return next(err) if (err) return next(err)

View File

@ -3,13 +3,12 @@
const parallel = require('async/parallel') const parallel = require('async/parallel')
const express = require('express') const express = require('express')
const fs = require('fs') const fs = require('fs')
const mongoose = require('mongoose')
const path = require('path') const path = require('path')
const validator = require('express-validator').validator const validator = require('express-validator').validator
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const db = require('../initializers/database')
const Video = mongoose.model('Video')
const router = express.Router() const router = express.Router()
const opengraphComment = '<!-- opengraph tags -->' const opengraphComment = '<!-- opengraph tags -->'
@ -45,14 +44,14 @@ function addOpenGraphTags (htmlStringPage, video) {
if (video.isOwned()) { if (video.isOwned()) {
basePreviewUrlHttp = constants.CONFIG.WEBSERVER.URL basePreviewUrlHttp = constants.CONFIG.WEBSERVER.URL
} else { } else {
basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.podHost basePreviewUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + video.Author.Pod.host
} }
// We fetch the remote preview (bigger than the thumbnail) // We fetch the remote preview (bigger than the thumbnail)
// This should not overhead the remote server since social websites put in a cache the OpenGraph tags // This should not overhead the remote server since social websites put in a cache the OpenGraph tags
// We can't use the thumbnail because these social websites want bigger images (> 200x200 for Facebook for example) // We can't use the thumbnail because these social websites want bigger images (> 200x200 for Facebook for example)
const previewUrl = basePreviewUrlHttp + constants.STATIC_PATHS.PREVIEWS + video.getPreviewName() const previewUrl = basePreviewUrlHttp + constants.STATIC_PATHS.PREVIEWS + video.getPreviewName()
const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video._id const videoUrl = constants.CONFIG.WEBSERVER.URL + '/videos/watch/' + video.id
const metaTags = { const metaTags = {
'og:type': 'video', 'og:type': 'video',
@ -86,7 +85,7 @@ function generateWatchHtmlPage (req, res, next) {
const videoId = req.params.id const videoId = req.params.id
// Let Angular application handle errors // Let Angular application handle errors
if (!validator.isMongoId(videoId)) return res.sendFile(indexPath) if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
parallel({ parallel({
file: function (callback) { file: function (callback) {
@ -94,7 +93,7 @@ function generateWatchHtmlPage (req, res, next) {
}, },
video: function (callback) { video: function (callback) {
Video.load(videoId, callback) db.Video.loadAndPopulateAuthorAndPod(videoId, callback)
} }
}, function (err, results) { }, function (err, results) {
if (err) return next(err) if (err) return next(err)

View File

@ -13,7 +13,7 @@ const videosValidators = {
isVideoDateValid, isVideoDateValid,
isVideoDescriptionValid, isVideoDescriptionValid,
isVideoDurationValid, isVideoDurationValid,
isVideoMagnetValid, isVideoInfoHashValid,
isVideoNameValid, isVideoNameValid,
isVideoPodHostValid, isVideoPodHostValid,
isVideoTagsValid, isVideoTagsValid,
@ -28,14 +28,15 @@ function isEachRemoteVideosValid (requests) {
return ( return (
isRequestTypeAddValid(request.type) && isRequestTypeAddValid(request.type) &&
isVideoAuthorValid(video.author) && isVideoAuthorValid(video.author) &&
isVideoDateValid(video.createdDate) && isVideoDateValid(video.createdAt) &&
isVideoDescriptionValid(video.description) && isVideoDescriptionValid(video.description) &&
isVideoDurationValid(video.duration) && isVideoDurationValid(video.duration) &&
isVideoMagnetValid(video.magnet) && isVideoInfoHashValid(video.infoHash) &&
isVideoNameValid(video.name) && isVideoNameValid(video.name) &&
isVideoTagsValid(video.tags) && isVideoTagsValid(video.tags) &&
isVideoThumbnail64Valid(video.thumbnailBase64) && isVideoThumbnail64Valid(video.thumbnailBase64) &&
isVideoRemoteIdValid(video.remoteId) isVideoRemoteIdValid(video.remoteId) &&
isVideoExtnameValid(video.extname)
) || ) ||
( (
isRequestTypeRemoveValid(request.type) && isRequestTypeRemoveValid(request.type) &&
@ -61,8 +62,12 @@ function isVideoDurationValid (value) {
return validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) return validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
} }
function isVideoMagnetValid (value) { function isVideoExtnameValid (value) {
return validator.isLength(value.infoHash, VIDEOS_CONSTRAINTS_FIELDS.MAGNET.INFO_HASH) return VIDEOS_CONSTRAINTS_FIELDS.EXTNAME.indexOf(value) !== -1
}
function isVideoInfoHashValid (value) {
return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
} }
function isVideoNameValid (value) { function isVideoNameValid (value) {
@ -93,7 +98,7 @@ function isVideoThumbnail64Valid (value) {
} }
function isVideoRemoteIdValid (value) { function isVideoRemoteIdValid (value) {
return validator.isMongoId(value) return validator.isUUID(value, 4)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -22,7 +22,8 @@ const logger = new winston.Logger({
json: true, json: true,
maxsize: 5242880, maxsize: 5242880,
maxFiles: 5, maxFiles: 5,
colorize: false colorize: false,
prettyPrint: true
}), }),
new winston.transports.Console({ new winston.transports.Console({
level: 'debug', level: 'debug',
@ -30,7 +31,8 @@ const logger = new winston.Logger({
handleExceptions: true, handleExceptions: true,
humanReadableUnhandledException: true, humanReadableUnhandledException: true,
json: false, json: false,
colorize: true colorize: true,
prettyPrint: true
}) })
], ],
exitOnError: true exitOnError: true

View File

@ -1,10 +1,8 @@
'use strict' 'use strict'
const config = require('config') const config = require('config')
const mongoose = require('mongoose')
const Client = mongoose.model('OAuthClient') const db = require('./database')
const User = mongoose.model('User')
const checker = { const checker = {
checkConfig, checkConfig,
@ -44,7 +42,7 @@ function checkMissedConfig () {
} }
function clientsExist (callback) { function clientsExist (callback) {
Client.list(function (err, clients) { db.OAuthClient.list(function (err, clients) {
if (err) return callback(err) if (err) return callback(err)
return callback(null, clients.length !== 0) return callback(null, clients.length !== 0)
@ -52,7 +50,7 @@ function clientsExist (callback) {
} }
function usersExist (callback) { function usersExist (callback) {
User.countTotal(function (err, totalUsers) { db.User.countTotal(function (err, totalUsers) {
if (err) return callback(err) if (err) return callback(err)
return callback(null, totalUsers !== 0) return callback(null, totalUsers !== 0)

View File

@ -14,13 +14,13 @@ const PAGINATION_COUNT_DEFAULT = 15
// Sortable columns per schema // Sortable columns per schema
const SEARCHABLE_COLUMNS = { const SEARCHABLE_COLUMNS = {
VIDEOS: [ 'name', 'magnetUri', 'podHost', 'author', 'tags' ] VIDEOS: [ 'name', 'magnetUri', 'host', 'author', 'tags' ]
} }
// Sortable columns per schema // Sortable columns per schema
const SORTABLE_COLUMNS = { const SORTABLE_COLUMNS = {
USERS: [ 'username', '-username', 'createdDate', '-createdDate' ], USERS: [ 'username', '-username', 'createdAt', '-createdAt' ],
VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ] VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdAt', '-createdAt' ]
} }
const OAUTH_LIFETIME = { const OAUTH_LIFETIME = {
@ -67,9 +67,8 @@ const CONSTRAINTS_FIELDS = {
VIDEOS: { VIDEOS: {
NAME: { min: 3, max: 50 }, // Length NAME: { min: 3, max: 50 }, // Length
DESCRIPTION: { min: 3, max: 250 }, // Length DESCRIPTION: { min: 3, max: 250 }, // Length
MAGNET: { EXTNAME: [ '.mp4', '.ogv', '.webm' ],
INFO_HASH: { min: 10, max: 50 } // Length INFO_HASH: { min: 10, max: 50 }, // Length
},
DURATION: { min: 1, max: 7200 }, // Number DURATION: { min: 1, max: 7200 }, // Number
TAGS: { min: 1, max: 3 }, // Number of total tags TAGS: { min: 1, max: 3 }, // Number of total tags
TAG: { min: 2, max: 10 }, // Length TAG: { min: 2, max: 10 }, // Length
@ -88,7 +87,7 @@ const FRIEND_SCORE = {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const MONGO_MIGRATION_SCRIPTS = [ const MIGRATION_SCRIPTS = [
{ {
script: '0005-create-application', script: '0005-create-application',
version: 5 version: 5
@ -122,7 +121,7 @@ const MONGO_MIGRATION_SCRIPTS = [
version: 40 version: 40
} }
] ]
const LAST_MONGO_SCHEMA_VERSION = (maxBy(MONGO_MIGRATION_SCRIPTS, 'version'))['version'] const LAST_SQL_SCHEMA_VERSION = (maxBy(MIGRATION_SCRIPTS, 'version'))['version']
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -198,8 +197,8 @@ module.exports = {
CONFIG, CONFIG,
CONSTRAINTS_FIELDS, CONSTRAINTS_FIELDS,
FRIEND_SCORE, FRIEND_SCORE,
LAST_MONGO_SCHEMA_VERSION, LAST_SQL_SCHEMA_VERSION,
MONGO_MIGRATION_SCRIPTS, MIGRATION_SCRIPTS,
OAUTH_LIFETIME, OAUTH_LIFETIME,
PAGINATION_COUNT_DEFAULT, PAGINATION_COUNT_DEFAULT,
PODS_SCORE, PODS_SCORE,

View File

@ -1,36 +1,46 @@
'use strict' 'use strict'
const mongoose = require('mongoose') const fs = require('fs')
const path = require('path')
const Sequelize = require('sequelize')
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
// Bootstrap models const database = {}
require('../models/application')
require('../models/oauth-token')
require('../models/user')
require('../models/oauth-client')
require('../models/video')
// Request model needs Video model
require('../models/pods')
// Request model needs Pod model
require('../models/request')
const database = { const sequelize = new Sequelize(constants.CONFIG.DATABASE.DBNAME, 'peertube', 'peertube', {
connect: connect dialect: 'postgres',
} host: constants.CONFIG.DATABASE.HOSTNAME,
port: constants.CONFIG.DATABASE.PORT
})
function connect () { const modelDirectory = path.join(__dirname, '..', 'models')
mongoose.Promise = global.Promise fs.readdir(modelDirectory, function (err, files) {
mongoose.connect('mongodb://' + constants.CONFIG.DATABASE.HOSTNAME + ':' + constants.CONFIG.DATABASE.PORT + '/' + constants.CONFIG.DATABASE.DBNAME) if (err) throw err
mongoose.connection.on('error', function () {
throw new Error('Mongodb connection error.') files.filter(function (file) {
if (file === 'utils.js') return false
return true
})
.forEach(function (file) {
const model = sequelize.import(path.join(modelDirectory, file))
database[model.name] = model
}) })
mongoose.connection.on('open', function () { Object.keys(database).forEach(function (modelName) {
logger.info('Connected to mongodb.') if ('associate' in database[modelName]) {
database[modelName].associate(database)
}
}) })
}
logger.info('Database is ready.')
})
database.sequelize = sequelize
database.Sequelize = Sequelize
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -3,26 +3,27 @@
const config = require('config') const config = require('config')
const each = require('async/each') const each = require('async/each')
const mkdirp = require('mkdirp') const mkdirp = require('mkdirp')
const mongoose = require('mongoose')
const passwordGenerator = require('password-generator') const passwordGenerator = require('password-generator')
const path = require('path') const path = require('path')
const series = require('async/series') const series = require('async/series')
const checker = require('./checker') const checker = require('./checker')
const constants = require('./constants') const constants = require('./constants')
const db = require('./database')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const peertubeCrypto = require('../helpers/peertube-crypto') const peertubeCrypto = require('../helpers/peertube-crypto')
const Application = mongoose.model('Application')
const Client = mongoose.model('OAuthClient')
const User = mongoose.model('User')
const installer = { const installer = {
installApplication installApplication
} }
function installApplication (callback) { function installApplication (callback) {
series([ series([
function createDatabase (callbackAsync) {
db.sequelize.sync().asCallback(callbackAsync)
// db.sequelize.sync({ force: true }).asCallback(callbackAsync)
},
function createDirectories (callbackAsync) { function createDirectories (callbackAsync) {
createDirectoriesIfNotExist(callbackAsync) createDirectoriesIfNotExist(callbackAsync)
}, },
@ -65,16 +66,18 @@ function createOAuthClientIfNotExist (callback) {
logger.info('Creating a default OAuth Client.') logger.info('Creating a default OAuth Client.')
const secret = passwordGenerator(32, false) const id = passwordGenerator(32, false, /[a-z0-9]/)
const client = new Client({ const secret = passwordGenerator(32, false, /[a-zA-Z0-9]/)
const client = db.OAuthClient.build({
clientId: id,
clientSecret: secret, clientSecret: secret,
grants: [ 'password', 'refresh_token' ] grants: [ 'password', 'refresh_token' ]
}) })
client.save(function (err, createdClient) { client.save().asCallback(function (err, createdClient) {
if (err) return callback(err) if (err) return callback(err)
logger.info('Client id: ' + createdClient._id) logger.info('Client id: ' + createdClient.clientId)
logger.info('Client secret: ' + createdClient.clientSecret) logger.info('Client secret: ' + createdClient.clientSecret)
return callback(null) return callback(null)
@ -106,21 +109,21 @@ function createOAuthAdminIfNotExist (callback) {
password = passwordGenerator(8, true) password = passwordGenerator(8, true)
} }
const user = new User({ const user = db.User.build({
username, username,
password, password,
role role
}) })
user.save(function (err, createdUser) { user.save().asCallback(function (err, createdUser) {
if (err) return callback(err) if (err) return callback(err)
logger.info('Username: ' + username) logger.info('Username: ' + username)
logger.info('User password: ' + password) logger.info('User password: ' + password)
logger.info('Creating Application collection.') logger.info('Creating Application collection.')
const application = new Application({ mongoSchemaVersion: constants.LAST_MONGO_SCHEMA_VERSION }) const application = db.Application.build({ sqlSchemaVersion: constants.LAST_SQL_SCHEMA_VERSION })
application.save(callback) application.save().asCallback(callback)
}) })
}) })
} }

View File

@ -1,24 +1,22 @@
'use strict' 'use strict'
const eachSeries = require('async/eachSeries') const eachSeries = require('async/eachSeries')
const mongoose = require('mongoose')
const path = require('path') const path = require('path')
const constants = require('./constants') const constants = require('./constants')
const db = require('./database')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const Application = mongoose.model('Application')
const migrator = { const migrator = {
migrate: migrate migrate: migrate
} }
function migrate (callback) { function migrate (callback) {
Application.loadMongoSchemaVersion(function (err, actualVersion) { db.Application.loadSqlSchemaVersion(function (err, actualVersion) {
if (err) return callback(err) if (err) return callback(err)
// If there are a new mongo schemas // If there are a new mongo schemas
if (!actualVersion || actualVersion < constants.LAST_MONGO_SCHEMA_VERSION) { if (!actualVersion || actualVersion < constants.LAST_SQL_SCHEMA_VERSION) {
logger.info('Begin migrations.') logger.info('Begin migrations.')
eachSeries(constants.MONGO_MIGRATION_SCRIPTS, function (entity, callbackEach) { eachSeries(constants.MONGO_MIGRATION_SCRIPTS, function (entity, callbackEach) {
@ -36,12 +34,12 @@ function migrate (callback) {
if (err) return callbackEach(err) if (err) return callbackEach(err)
// Update the new mongo version schema // Update the new mongo version schema
Application.updateMongoSchemaVersion(versionScript, callbackEach) db.Application.updateSqlSchemaVersion(versionScript, callbackEach)
}) })
}, function (err) { }, function (err) {
if (err) return callback(err) if (err) return callback(err)
logger.info('Migrations finished. New mongo version schema: %s', constants.LAST_MONGO_SCHEMA_VERSION) logger.info('Migrations finished. New SQL version schema: %s', constants.LAST_SQL_SCHEMA_VERSION)
return callback(null) return callback(null)
}) })
} else { } else {

View File

@ -4,18 +4,14 @@ const each = require('async/each')
const eachLimit = require('async/eachLimit') const eachLimit = require('async/eachLimit')
const eachSeries = require('async/eachSeries') const eachSeries = require('async/eachSeries')
const fs = require('fs') const fs = require('fs')
const mongoose = require('mongoose')
const request = require('request') const request = require('request')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const db = require('../initializers/database')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const requests = require('../helpers/requests') const requests = require('../helpers/requests')
const Pod = mongoose.model('Pod')
const Request = mongoose.model('Request')
const Video = mongoose.model('Video')
const friends = { const friends = {
addVideoToFriends, addVideoToFriends,
hasFriends, hasFriends,
@ -31,7 +27,7 @@ function addVideoToFriends (video) {
} }
function hasFriends (callback) { function hasFriends (callback) {
Pod.countAll(function (err, count) { db.Pod.countAll(function (err, count) {
if (err) return callback(err) if (err) return callback(err)
const hasFriends = (count !== 0) const hasFriends = (count !== 0)
@ -69,13 +65,13 @@ function makeFriends (hosts, callback) {
function quitFriends (callback) { function quitFriends (callback) {
// Stop pool requests // Stop pool requests
Request.deactivate() db.Request.deactivate()
// Flush pool requests // Flush pool requests
Request.flush() db.Request.flush()
waterfall([ waterfall([
function getPodsList (callbackAsync) { function getPodsList (callbackAsync) {
return Pod.list(callbackAsync) return db.Pod.list(callbackAsync)
}, },
function announceIQuitMyFriends (pods, callbackAsync) { function announceIQuitMyFriends (pods, callbackAsync) {
@ -103,12 +99,12 @@ function quitFriends (callback) {
function removePodsFromDB (pods, callbackAsync) { function removePodsFromDB (pods, callbackAsync) {
each(pods, function (pod, callbackEach) { each(pods, function (pod, callbackEach) {
pod.remove(callbackEach) pod.destroy().asCallback(callbackEach)
}, callbackAsync) }, callbackAsync)
} }
], function (err) { ], function (err) {
// Don't forget to re activate the scheduler, even if there was an error // Don't forget to re activate the scheduler, even if there was an error
Request.activate() db.Request.activate()
if (err) return callback(err) if (err) return callback(err)
@ -122,7 +118,7 @@ function removeVideoToFriends (videoParams) {
} }
function sendOwnedVideosToPod (podId) { function sendOwnedVideosToPod (podId) {
Video.listOwned(function (err, videosList) { db.Video.listOwnedAndPopulateAuthor(function (err, videosList) {
if (err) { if (err) {
logger.error('Cannot get the list of videos we own.') logger.error('Cannot get the list of videos we own.')
return return
@ -200,9 +196,9 @@ function getForeignPodsList (host, callback) {
function makeRequestsToWinningPods (cert, podsList, callback) { function makeRequestsToWinningPods (cert, podsList, callback) {
// Stop pool requests // Stop pool requests
Request.deactivate() db.Request.deactivate()
// Flush pool requests // Flush pool requests
Request.forceSend() db.Request.forceSend()
eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
const params = { const params = {
@ -222,8 +218,8 @@ function makeRequestsToWinningPods (cert, podsList, callback) {
} }
if (res.statusCode === 200) { if (res.statusCode === 200) {
const podObj = new Pod({ host: pod.host, publicKey: body.cert }) const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert })
podObj.save(function (err, podCreated) { podObj.save().asCallback(function (err, podCreated) {
if (err) { if (err) {
logger.error('Cannot add friend %s pod.', pod.host, { error: err }) logger.error('Cannot add friend %s pod.', pod.host, { error: err })
return callbackEach() return callbackEach()
@ -242,28 +238,57 @@ function makeRequestsToWinningPods (cert, podsList, callback) {
}, function endRequests () { }, function endRequests () {
// Final callback, we've ended all the requests // Final callback, we've ended all the requests
// Now we made new friends, we can re activate the pool of requests // Now we made new friends, we can re activate the pool of requests
Request.activate() db.Request.activate()
logger.debug('makeRequestsToWinningPods finished.') logger.debug('makeRequestsToWinningPods finished.')
return callback() return callback()
}) })
} }
// Wrapper that populate "to" argument with all our friends if it is not specified
function createRequest (type, endpoint, data, to) { function createRequest (type, endpoint, data, to) {
const req = new Request({ if (to) return _createRequest(type, endpoint, data, to)
// If the "to" pods is not specified, we send the request to all our friends
db.Pod.listAllIds(function (err, podIds) {
if (err) {
logger.error('Cannot get pod ids', { error: err })
return
}
return _createRequest(type, endpoint, data, podIds)
})
}
function _createRequest (type, endpoint, data, to) {
const pods = []
// If there are no destination pods abort
if (to.length === 0) return
to.forEach(function (toPod) {
pods.push(db.Pod.build({ id: toPod }))
})
const createQuery = {
endpoint, endpoint,
request: { request: {
type: type, type: type,
data: data data: data
} }
})
if (to) {
req.to = to
} }
req.save(function (err) { // We run in transaction to keep coherency between Request and RequestToPod tables
if (err) logger.error('Cannot save the request.', { error: err }) db.sequelize.transaction(function (t) {
const dbRequestOptions = {
transaction: t
}
return db.Request.create(createQuery, dbRequestOptions).then(function (request) {
return request.setPods(pods, dbRequestOptions)
})
}).asCallback(function (err) {
if (err) logger.error('Error in createRequest transaction.', { error: err })
}) })
} }

View File

@ -1,11 +1,6 @@
const mongoose = require('mongoose') const db = require('../initializers/database')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const OAuthClient = mongoose.model('OAuthClient')
const OAuthToken = mongoose.model('OAuthToken')
const User = mongoose.model('User')
// See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications // See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications
const OAuthModel = { const OAuthModel = {
getAccessToken, getAccessToken,
@ -21,27 +16,25 @@ const OAuthModel = {
function getAccessToken (bearerToken) { function getAccessToken (bearerToken) {
logger.debug('Getting access token (bearerToken: ' + bearerToken + ').') logger.debug('Getting access token (bearerToken: ' + bearerToken + ').')
return OAuthToken.getByTokenAndPopulateUser(bearerToken) return db.OAuthToken.getByTokenAndPopulateUser(bearerToken)
} }
function getClient (clientId, clientSecret) { function getClient (clientId, clientSecret) {
logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').') logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').')
// TODO req validator return db.OAuthClient.getByIdAndSecret(clientId, clientSecret)
const mongoId = new mongoose.mongo.ObjectID(clientId)
return OAuthClient.getByIdAndSecret(mongoId, clientSecret)
} }
function getRefreshToken (refreshToken) { function getRefreshToken (refreshToken) {
logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').') logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').')
return OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken) return db.OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken)
} }
function getUser (username, password) { function getUser (username, password) {
logger.debug('Getting User (username: ' + username + ', password: ' + password + ').') logger.debug('Getting User (username: ' + username + ', password: ' + password + ').')
return User.getByUsername(username).then(function (user) { return db.User.getByUsername(username).then(function (user) {
if (!user) return null if (!user) return null
// We need to return a promise // We need to return a promise
@ -60,8 +53,8 @@ function getUser (username, password) {
} }
function revokeToken (token) { function revokeToken (token) {
return OAuthToken.getByRefreshTokenAndPopulateUser(token.refreshToken).then(function (tokenDB) { return db.OAuthToken.getByRefreshTokenAndPopulateUser(token.refreshToken).then(function (tokenDB) {
if (tokenDB) tokenDB.remove() if (tokenDB) tokenDB.destroy()
/* /*
* Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js
@ -80,18 +73,19 @@ function revokeToken (token) {
function saveToken (token, client, user) { function saveToken (token, client, user) {
logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.') logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.')
const tokenObj = new OAuthToken({ const tokenToCreate = {
accessToken: token.accessToken, accessToken: token.accessToken,
accessTokenExpiresAt: token.accessTokenExpiresAt, accessTokenExpiresAt: token.accessTokenExpiresAt,
client: client.id,
refreshToken: token.refreshToken, refreshToken: token.refreshToken,
refreshTokenExpiresAt: token.refreshTokenExpiresAt, refreshTokenExpiresAt: token.refreshTokenExpiresAt,
user: user.id oAuthClientId: client.id,
}) userId: user.id
}
return tokenObj.save().then(function (tokenCreated) { return db.OAuthToken.create(tokenToCreate).then(function (tokenCreated) {
tokenCreated.client = client tokenCreated.client = client
tokenCreated.user = user tokenCreated.user = user
return tokenCreated return tokenCreated
}).catch(function (err) { }).catch(function (err) {
throw err throw err

View File

@ -44,7 +44,6 @@ module.exports = podsMiddleware
function getHostWithPort (host) { function getHostWithPort (host) {
const splitted = host.split(':') const splitted = host.split(':')
console.log(splitted)
// The port was not specified // The port was not specified
if (splitted.length === 1) { if (splitted.length === 1) {
if (constants.REMOTE_SCHEME.HTTP === 'https') return host + ':443' if (constants.REMOTE_SCHEME.HTTP === 'https') return host + ':443'

View File

@ -1,18 +1,16 @@
'use strict' 'use strict'
const db = require('../initializers/database')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const mongoose = require('mongoose')
const peertubeCrypto = require('../helpers/peertube-crypto') const peertubeCrypto = require('../helpers/peertube-crypto')
const Pod = mongoose.model('Pod')
const secureMiddleware = { const secureMiddleware = {
checkSignature checkSignature
} }
function checkSignature (req, res, next) { function checkSignature (req, res, next) {
const host = req.body.signature.host const host = req.body.signature.host
Pod.loadByHost(host, function (err, pod) { db.Pod.loadByHost(host, function (err, pod) {
if (err) { if (err) {
logger.error('Cannot get signed host in body.', { error: err }) logger.error('Cannot get signed host in body.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)

View File

@ -6,13 +6,13 @@ const sortMiddleware = {
} }
function setUsersSort (req, res, next) { function setUsersSort (req, res, next) {
if (!req.query.sort) req.query.sort = '-createdDate' if (!req.query.sort) req.query.sort = '-createdAt'
return next() return next()
} }
function setVideosSort (req, res, next) { function setVideosSort (req, res, next) {
if (!req.query.sort) req.query.sort = '-createdDate' if (!req.query.sort) req.query.sort = '-createdAt'
return next() return next()
} }

View File

@ -1,12 +1,9 @@
'use strict' 'use strict'
const mongoose = require('mongoose')
const checkErrors = require('./utils').checkErrors const checkErrors = require('./utils').checkErrors
const db = require('../../initializers/database')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const User = mongoose.model('User')
const validatorsUsers = { const validatorsUsers = {
usersAdd, usersAdd,
usersRemove, usersRemove,
@ -20,7 +17,7 @@ function usersAdd (req, res, next) {
logger.debug('Checking usersAdd parameters', { parameters: req.body }) logger.debug('Checking usersAdd parameters', { parameters: req.body })
checkErrors(req, res, function () { checkErrors(req, res, function () {
User.loadByUsername(req.body.username, function (err, user) { db.User.loadByUsername(req.body.username, function (err, user) {
if (err) { if (err) {
logger.error('Error in usersAdd request validator.', { error: err }) logger.error('Error in usersAdd request validator.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)
@ -34,12 +31,12 @@ function usersAdd (req, res, next) {
} }
function usersRemove (req, res, next) { function usersRemove (req, res, next) {
req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() req.checkParams('id', 'Should have a valid id').notEmpty().isInt()
logger.debug('Checking usersRemove parameters', { parameters: req.params }) logger.debug('Checking usersRemove parameters', { parameters: req.params })
checkErrors(req, res, function () { checkErrors(req, res, function () {
User.loadById(req.params.id, function (err, user) { db.User.loadById(req.params.id, function (err, user) {
if (err) { if (err) {
logger.error('Error in usersRemove request validator.', { error: err }) logger.error('Error in usersRemove request validator.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)
@ -55,7 +52,7 @@ function usersRemove (req, res, next) {
} }
function usersUpdate (req, res, next) { function usersUpdate (req, res, next) {
req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() req.checkParams('id', 'Should have a valid id').notEmpty().isInt()
// Add old password verification // Add old password verification
req.checkBody('password', 'Should have a valid password').isUserPasswordValid() req.checkBody('password', 'Should have a valid password').isUserPasswordValid()

View File

@ -1,14 +1,11 @@
'use strict' 'use strict'
const mongoose = require('mongoose')
const checkErrors = require('./utils').checkErrors const checkErrors = require('./utils').checkErrors
const constants = require('../../initializers/constants') const constants = require('../../initializers/constants')
const customVideosValidators = require('../../helpers/custom-validators').videos const customVideosValidators = require('../../helpers/custom-validators').videos
const db = require('../../initializers/database')
const logger = require('../../helpers/logger') const logger = require('../../helpers/logger')
const Video = mongoose.model('Video')
const validatorsVideos = { const validatorsVideos = {
videosAdd, videosAdd,
videosGet, videosGet,
@ -29,7 +26,7 @@ function videosAdd (req, res, next) {
checkErrors(req, res, function () { checkErrors(req, res, function () {
const videoFile = req.files.videofile[0] const videoFile = req.files.videofile[0]
Video.getDurationFromFile(videoFile.path, function (err, duration) { db.Video.getDurationFromFile(videoFile.path, function (err, duration) {
if (err) { if (err) {
return res.status(400).send('Cannot retrieve metadata of the file.') return res.status(400).send('Cannot retrieve metadata of the file.')
} }
@ -45,12 +42,12 @@ function videosAdd (req, res, next) {
} }
function videosGet (req, res, next) { function videosGet (req, res, next) {
req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
logger.debug('Checking videosGet parameters', { parameters: req.params }) logger.debug('Checking videosGet parameters', { parameters: req.params })
checkErrors(req, res, function () { checkErrors(req, res, function () {
Video.load(req.params.id, function (err, video) { db.Video.load(req.params.id, function (err, video) {
if (err) { if (err) {
logger.error('Error in videosGet request validator.', { error: err }) logger.error('Error in videosGet request validator.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)
@ -64,12 +61,12 @@ function videosGet (req, res, next) {
} }
function videosRemove (req, res, next) { function videosRemove (req, res, next) {
req.checkParams('id', 'Should have a valid id').notEmpty().isMongoId() req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
logger.debug('Checking videosRemove parameters', { parameters: req.params }) logger.debug('Checking videosRemove parameters', { parameters: req.params })
checkErrors(req, res, function () { checkErrors(req, res, function () {
Video.load(req.params.id, function (err, video) { db.Video.loadAndPopulateAuthor(req.params.id, function (err, video) {
if (err) { if (err) {
logger.error('Error in videosRemove request validator.', { error: err }) logger.error('Error in videosRemove request validator.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)
@ -77,7 +74,7 @@ function videosRemove (req, res, next) {
if (!video) return res.status(404).send('Video not found') if (!video) return res.status(404).send('Video not found')
else if (video.isOwned() === false) return res.status(403).send('Cannot remove video of another pod') else if (video.isOwned() === false) return res.status(403).send('Cannot remove video of another pod')
else if (video.author !== res.locals.oauth.token.user.username) return res.status(403).send('Cannot remove video of another user') else if (video.Author.name !== res.locals.oauth.token.user.username) return res.status(403).send('Cannot remove video of another user')
next() next()
}) })

View File

@ -1,31 +1,36 @@
const mongoose = require('mongoose') module.exports = function (sequelize, DataTypes) {
const Application = sequelize.define('Application',
{
sqlSchemaVersion: {
type: DataTypes.INTEGER,
defaultValue: 0
}
},
{
classMethods: {
loadSqlSchemaVersion,
updateSqlSchemaVersion
}
}
)
// --------------------------------------------------------------------------- return Application
const ApplicationSchema = mongoose.Schema({
mongoSchemaVersion: {
type: Number,
default: 0
}
})
ApplicationSchema.statics = {
loadMongoSchemaVersion,
updateMongoSchemaVersion
} }
mongoose.model('Application', ApplicationSchema)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function loadMongoSchemaVersion (callback) { function loadSqlSchemaVersion (callback) {
return this.findOne({}, { mongoSchemaVersion: 1 }, function (err, data) { const query = {
const version = data ? data.mongoSchemaVersion : 0 attributes: [ 'sqlSchemaVersion' ]
}
return this.findOne(query).asCallback(function (err, data) {
const version = data ? data.sqlSchemaVersion : 0
return callback(err, version) return callback(err, version)
}) })
} }
function updateMongoSchemaVersion (newVersion, callback) { function updateSqlSchemaVersion (newVersion, callback) {
return this.update({}, { mongoSchemaVersion: newVersion }, callback) return this.update({ sqlSchemaVersion: newVersion }).asCallback(callback)
} }

28
server/models/author.js Normal file
View File

@ -0,0 +1,28 @@
module.exports = function (sequelize, DataTypes) {
const Author = sequelize.define('Author',
{
name: {
type: DataTypes.STRING
}
},
{
classMethods: {
associate
}
}
)
return Author
}
// ---------------------------------------------------------------------------
function associate (models) {
this.belongsTo(models.Pod, {
foreignKey: {
name: 'podId',
allowNull: true
},
onDelete: 'cascade'
})
}

View File

@ -1,33 +1,63 @@
const mongoose = require('mongoose') module.exports = function (sequelize, DataTypes) {
const OAuthClient = sequelize.define('OAuthClient',
{
clientId: {
type: DataTypes.STRING
},
clientSecret: {
type: DataTypes.STRING
},
grants: {
type: DataTypes.ARRAY(DataTypes.STRING)
},
redirectUris: {
type: DataTypes.ARRAY(DataTypes.STRING)
}
},
{
classMethods: {
associate,
// --------------------------------------------------------------------------- getByIdAndSecret,
list,
loadFirstClient
}
}
)
const OAuthClientSchema = mongoose.Schema({ return OAuthClient
clientSecret: String,
grants: Array,
redirectUris: Array
})
OAuthClientSchema.path('clientSecret').required(true)
OAuthClientSchema.statics = {
getByIdAndSecret,
list,
loadFirstClient
} }
mongoose.model('OAuthClient', OAuthClientSchema) // TODO: validation
// OAuthClientSchema.path('clientSecret').required(true)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function associate (models) {
this.hasMany(models.OAuthToken, {
foreignKey: {
name: 'oAuthClientId',
allowNull: false
},
onDelete: 'cascade'
})
}
function list (callback) { function list (callback) {
return this.find(callback) return this.findAll().asCallback(callback)
} }
function loadFirstClient (callback) { function loadFirstClient (callback) {
return this.findOne({}, callback) return this.findOne().asCallback(callback)
} }
function getByIdAndSecret (id, clientSecret) { function getByIdAndSecret (clientId, clientSecret) {
return this.findOne({ _id: id, clientSecret: clientSecret }).exec() const query = {
where: {
clientId: clientId,
clientSecret: clientSecret
}
}
return this.findOne(query)
} }

View File

@ -1,42 +1,71 @@
const mongoose = require('mongoose')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const OAuthTokenSchema = mongoose.Schema({ module.exports = function (sequelize, DataTypes) {
accessToken: String, const OAuthToken = sequelize.define('OAuthToken',
accessTokenExpiresAt: Date, {
client: { type: mongoose.Schema.Types.ObjectId, ref: 'OAuthClient' }, accessToken: {
refreshToken: String, type: DataTypes.STRING
refreshTokenExpiresAt: Date, },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } accessTokenExpiresAt: {
}) type: DataTypes.DATE
},
refreshToken: {
type: DataTypes.STRING
},
refreshTokenExpiresAt: {
type: DataTypes.DATE
}
},
{
classMethods: {
associate,
OAuthTokenSchema.path('accessToken').required(true) getByRefreshTokenAndPopulateClient,
OAuthTokenSchema.path('client').required(true) getByTokenAndPopulateUser,
OAuthTokenSchema.path('user').required(true) getByRefreshTokenAndPopulateUser,
removeByUserId
}
}
)
OAuthTokenSchema.statics = { return OAuthToken
getByRefreshTokenAndPopulateClient,
getByTokenAndPopulateUser,
getByRefreshTokenAndPopulateUser,
removeByUserId
} }
mongoose.model('OAuthToken', OAuthTokenSchema) // TODO: validation
// OAuthTokenSchema.path('accessToken').required(true)
// OAuthTokenSchema.path('client').required(true)
// OAuthTokenSchema.path('user').required(true)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function associate (models) {
this.belongsTo(models.User, {
foreignKey: {
name: 'userId',
allowNull: false
},
onDelete: 'cascade'
})
}
function getByRefreshTokenAndPopulateClient (refreshToken) { function getByRefreshTokenAndPopulateClient (refreshToken) {
return this.findOne({ refreshToken: refreshToken }).populate('client').exec().then(function (token) { const query = {
where: {
refreshToken: refreshToken
},
include: [ this.associations.OAuthClient ]
}
return this.findOne(query).then(function (token) {
if (!token) return token if (!token) return token
const tokenInfos = { const tokenInfos = {
refreshToken: token.refreshToken, refreshToken: token.refreshToken,
refreshTokenExpiresAt: token.refreshTokenExpiresAt, refreshTokenExpiresAt: token.refreshTokenExpiresAt,
client: { client: {
id: token.client._id.toString() id: token.client.id
}, },
user: { user: {
id: token.user id: token.user
@ -50,13 +79,41 @@ function getByRefreshTokenAndPopulateClient (refreshToken) {
} }
function getByTokenAndPopulateUser (bearerToken) { function getByTokenAndPopulateUser (bearerToken) {
return this.findOne({ accessToken: bearerToken }).populate('user').exec() const query = {
where: {
accessToken: bearerToken
},
include: [ this.sequelize.models.User ]
}
return this.findOne(query).then(function (token) {
if (token) token.user = token.User
return token
})
} }
function getByRefreshTokenAndPopulateUser (refreshToken) { function getByRefreshTokenAndPopulateUser (refreshToken) {
return this.findOne({ refreshToken: refreshToken }).populate('user').exec() const query = {
where: {
refreshToken: refreshToken
},
include: [ this.sequelize.models.User ]
}
return this.findOne(query).then(function (token) {
token.user = token.User
return token
})
} }
function removeByUserId (userId, callback) { function removeByUserId (userId, callback) {
return this.remove({ user: userId }, callback) const query = {
where: {
userId: userId
}
}
return this.destroy(query).asCallback(callback)
} }

View File

@ -1,79 +1,62 @@
'use strict' 'use strict'
const each = require('async/each')
const mongoose = require('mongoose')
const map = require('lodash/map') const map = require('lodash/map')
const validator = require('express-validator').validator
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const Video = mongoose.model('Video')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const PodSchema = mongoose.Schema({ module.exports = function (sequelize, DataTypes) {
host: String, const Pod = sequelize.define('Pod',
publicKey: String, {
score: { type: Number, max: constants.FRIEND_SCORE.MAX }, host: {
createdDate: { type: DataTypes.STRING
type: Date, },
default: Date.now publicKey: {
} type: DataTypes.STRING(5000)
}) },
score: {
type: DataTypes.INTEGER,
defaultValue: constants.FRIEND_SCORE.BASE
}
// Check createdAt
},
{
classMethods: {
associate,
PodSchema.path('host').validate(validator.isURL) countAll,
PodSchema.path('publicKey').required(true) incrementScores,
PodSchema.path('score').validate(function (value) { return !isNaN(value) }) list,
listAllIds,
listBadPods,
load,
loadByHost,
removeAll
},
instanceMethods: {
toFormatedJSON
}
}
)
PodSchema.methods = { return Pod
toFormatedJSON
} }
PodSchema.statics = { // TODO: max score -> constants.FRIENDS_SCORE.MAX
countAll, // TODO: validation
incrementScores, // PodSchema.path('host').validate(validator.isURL)
list, // PodSchema.path('publicKey').required(true)
listAllIds, // PodSchema.path('score').validate(function (value) { return !isNaN(value) })
listBadPods,
load,
loadByHost,
removeAll
}
PodSchema.pre('save', function (next) {
const self = this
Pod.loadByHost(this.host, function (err, pod) {
if (err) return next(err)
if (pod) return next(new Error('Pod already exists.'))
self.score = constants.FRIEND_SCORE.BASE
return next()
})
})
PodSchema.pre('remove', function (next) {
// Remove the videos owned by this pod too
Video.listByHost(this.host, function (err, videos) {
if (err) return next(err)
each(videos, function (video, callbackEach) {
video.remove(callbackEach)
}, next)
})
})
const Pod = mongoose.model('Pod', PodSchema)
// ------------------------------ METHODS ------------------------------ // ------------------------------ METHODS ------------------------------
function toFormatedJSON () { function toFormatedJSON () {
const json = { const json = {
id: this._id, id: this.id,
host: this.host, host: this.host,
score: this.score, score: this.score,
createdDate: this.createdDate createdAt: this.createdAt
} }
return json return json
@ -81,39 +64,76 @@ function toFormatedJSON () {
// ------------------------------ Statics ------------------------------ // ------------------------------ Statics ------------------------------
function associate (models) {
this.belongsToMany(models.Request, {
foreignKey: 'podId',
through: models.RequestToPod,
onDelete: 'CASCADE'
})
}
function countAll (callback) { function countAll (callback) {
return this.count(callback) return this.count().asCallback(callback)
} }
function incrementScores (ids, value, callback) { function incrementScores (ids, value, callback) {
if (!callback) callback = function () {} if (!callback) callback = function () {}
return this.update({ _id: { $in: ids } }, { $inc: { score: value } }, { multi: true }, callback)
const update = {
score: this.sequelize.literal('score +' + value)
}
const query = {
where: {
id: {
$in: ids
}
}
}
return this.update(update, query).asCallback(callback)
} }
function list (callback) { function list (callback) {
return this.find(callback) return this.findAll().asCallback(callback)
} }
function listAllIds (callback) { function listAllIds (callback) {
return this.find({}, { _id: 1 }, function (err, pods) { const query = {
attributes: [ 'id' ]
}
return this.findAll(query).asCallback(function (err, pods) {
if (err) return callback(err) if (err) return callback(err)
return callback(null, map(pods, '_id')) return callback(null, map(pods, 'id'))
}) })
} }
function listBadPods (callback) { function listBadPods (callback) {
return this.find({ score: 0 }, callback) const query = {
where: {
score: { $lte: 0 }
}
}
return this.findAll(query).asCallback(callback)
} }
function load (id, callback) { function load (id, callback) {
return this.findById(id, callback) return this.findById(id).asCallback(callback)
} }
function loadByHost (host, callback) { function loadByHost (host, callback) {
return this.findOne({ host }, callback) const query = {
where: {
host: host
}
}
return this.findOne(query).asCallback(callback)
} }
function removeAll (callback) { function removeAll (callback) {
return this.remove({}, callback) return this.destroy().asCallback(callback)
} }

View File

@ -2,66 +2,58 @@
const each = require('async/each') const each = require('async/each')
const eachLimit = require('async/eachLimit') const eachLimit = require('async/eachLimit')
const values = require('lodash/values')
const mongoose = require('mongoose')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const requests = require('../helpers/requests') const requests = require('../helpers/requests')
const Pod = mongoose.model('Pod')
let timer = null let timer = null
let lastRequestTimestamp = 0 let lastRequestTimestamp = 0
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const RequestSchema = mongoose.Schema({ module.exports = function (sequelize, DataTypes) {
request: mongoose.Schema.Types.Mixed, const Request = sequelize.define('Request',
endpoint: {
type: String,
enum: [ values(constants.REQUEST_ENDPOINTS) ]
},
to: [
{ {
type: mongoose.Schema.Types.ObjectId, request: {
ref: 'Pod' type: DataTypes.JSON
} },
] endpoint: {
}) // TODO: enum?
type: DataTypes.STRING
}
},
{
classMethods: {
associate,
RequestSchema.statics = { activate,
activate, countTotalRequests,
deactivate, deactivate,
flush, flush,
forceSend, forceSend,
list, remainingMilliSeconds
remainingMilliSeconds }
}
)
return Request
} }
RequestSchema.pre('save', function (next) {
const self = this
if (self.to.length === 0) {
Pod.listAllIds(function (err, podIds) {
if (err) return next(err)
// No friends
if (podIds.length === 0) return
self.to = podIds
return next()
})
} else {
return next()
}
})
mongoose.model('Request', RequestSchema)
// ------------------------------ STATICS ------------------------------ // ------------------------------ STATICS ------------------------------
function associate (models) {
this.belongsToMany(models.Pod, {
foreignKey: {
name: 'requestId',
allowNull: false
},
through: models.RequestToPod,
onDelete: 'CASCADE'
})
}
function activate () { function activate () {
logger.info('Requests scheduler activated.') logger.info('Requests scheduler activated.')
lastRequestTimestamp = Date.now() lastRequestTimestamp = Date.now()
@ -73,6 +65,14 @@ function activate () {
}, constants.REQUESTS_INTERVAL) }, constants.REQUESTS_INTERVAL)
} }
function countTotalRequests (callback) {
const query = {
include: [ this.sequelize.models.Pod ]
}
return this.count(query).asCallback(callback)
}
function deactivate () { function deactivate () {
logger.info('Requests scheduler deactivated.') logger.info('Requests scheduler deactivated.')
clearInterval(timer) clearInterval(timer)
@ -90,10 +90,6 @@ function forceSend () {
makeRequests.call(this) makeRequests.call(this)
} }
function list (callback) {
this.find({ }, callback)
}
function remainingMilliSeconds () { function remainingMilliSeconds () {
if (timer === null) return -1 if (timer === null) return -1
@ -136,6 +132,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) {
// Make all the requests of the scheduler // Make all the requests of the scheduler
function makeRequests () { function makeRequests () {
const self = this const self = this
const RequestToPod = this.sequelize.models.RequestToPod
// We limit the size of the requests (REQUESTS_LIMIT) // We limit the size of the requests (REQUESTS_LIMIT)
// We don't want to stuck with the same failing requests so we get a random list // We don't want to stuck with the same failing requests so we get a random list
@ -156,20 +153,20 @@ function makeRequests () {
// We want to group requests by destinations pod and endpoint // We want to group requests by destinations pod and endpoint
const requestsToMakeGrouped = {} const requestsToMakeGrouped = {}
requests.forEach(function (poolRequest) { requests.forEach(function (request) {
poolRequest.to.forEach(function (toPodId) { request.Pods.forEach(function (toPod) {
const hashKey = toPodId + poolRequest.endpoint const hashKey = toPod.id + request.endpoint
if (!requestsToMakeGrouped[hashKey]) { if (!requestsToMakeGrouped[hashKey]) {
requestsToMakeGrouped[hashKey] = { requestsToMakeGrouped[hashKey] = {
toPodId, toPodId: toPod.id,
endpoint: poolRequest.endpoint, endpoint: request.endpoint,
ids: [], // pool request ids, to delete them from the DB in the future ids: [], // request ids, to delete them from the DB in the future
datas: [] // requests data, datas: [] // requests data,
} }
} }
requestsToMakeGrouped[hashKey].ids.push(poolRequest._id) requestsToMakeGrouped[hashKey].ids.push(request.id)
requestsToMakeGrouped[hashKey].datas.push(poolRequest.request) requestsToMakeGrouped[hashKey].datas.push(request.request)
}) })
}) })
@ -179,8 +176,8 @@ function makeRequests () {
eachLimit(Object.keys(requestsToMakeGrouped), constants.REQUESTS_IN_PARALLEL, function (hashKey, callbackEach) { eachLimit(Object.keys(requestsToMakeGrouped), constants.REQUESTS_IN_PARALLEL, function (hashKey, callbackEach) {
const requestToMake = requestsToMakeGrouped[hashKey] const requestToMake = requestsToMakeGrouped[hashKey]
// FIXME: mongodb request inside a loop :/ // FIXME: SQL request inside a loop :/
Pod.load(requestToMake.toPodId, function (err, toPod) { self.sequelize.models.Pod.load(requestToMake.toPodId, function (err, toPod) {
if (err) { if (err) {
logger.error('Error finding pod by id.', { err: err }) logger.error('Error finding pod by id.', { err: err })
return callbackEach() return callbackEach()
@ -191,7 +188,7 @@ function makeRequests () {
const requestIdsToDelete = requestToMake.ids const requestIdsToDelete = requestToMake.ids
logger.info('Removing %d requests of unexisting pod %s.', requestIdsToDelete.length, requestToMake.toPodId) logger.info('Removing %d requests of unexisting pod %s.', requestIdsToDelete.length, requestToMake.toPodId)
removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId) RequestToPod.removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId)
return callbackEach() return callbackEach()
} }
@ -202,7 +199,7 @@ function makeRequests () {
goodPods.push(requestToMake.toPodId) goodPods.push(requestToMake.toPodId)
// Remove the pod id of these request ids // Remove the pod id of these request ids
removePodOf.call(self, requestToMake.ids, requestToMake.toPodId, callbackEach) RequestToPod.removePodOf(requestToMake.ids, requestToMake.toPodId, callbackEach)
} else { } else {
badPods.push(requestToMake.toPodId) badPods.push(requestToMake.toPodId)
callbackEach() callbackEach()
@ -211,18 +208,22 @@ function makeRequests () {
}) })
}, function () { }, function () {
// All the requests were made, we update the pods score // All the requests were made, we update the pods score
updatePodsScore(goodPods, badPods) updatePodsScore.call(self, goodPods, badPods)
// Flush requests with no pod // Flush requests with no pod
removeWithEmptyTo.call(self) removeWithEmptyTo.call(self, function (err) {
if (err) logger.error('Error when removing requests with no pods.', { error: err })
})
}) })
}) })
} }
// Remove pods with a score of 0 (too many requests where they were unreachable) // Remove pods with a score of 0 (too many requests where they were unreachable)
function removeBadPods () { function removeBadPods () {
const self = this
waterfall([ waterfall([
function findBadPods (callback) { function findBadPods (callback) {
Pod.listBadPods(function (err, pods) { self.sequelize.models.Pod.listBadPods(function (err, pods) {
if (err) { if (err) {
logger.error('Cannot find bad pods.', { error: err }) logger.error('Cannot find bad pods.', { error: err })
return callback(err) return callback(err)
@ -233,10 +234,8 @@ function removeBadPods () {
}, },
function removeTheseBadPods (pods, callback) { function removeTheseBadPods (pods, callback) {
if (pods.length === 0) return callback(null, 0)
each(pods, function (pod, callbackEach) { each(pods, function (pod, callbackEach) {
pod.remove(callbackEach) pod.destroy().asCallback(callbackEach)
}, function (err) { }, function (err) {
return callback(err, pods.length) return callback(err, pods.length)
}) })
@ -253,43 +252,67 @@ function removeBadPods () {
} }
function updatePodsScore (goodPods, badPods) { function updatePodsScore (goodPods, badPods) {
const self = this
const Pod = this.sequelize.models.Pod
logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length) logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) { if (goodPods.length !== 0) {
if (err) logger.error('Cannot increment scores of good pods.') Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) {
}) if (err) logger.error('Cannot increment scores of good pods.')
})
}
Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) { if (badPods.length !== 0) {
if (err) logger.error('Cannot decrement scores of bad pods.') Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) {
removeBadPods() if (err) logger.error('Cannot decrement scores of bad pods.')
}) removeBadPods.call(self)
})
}
} }
function listWithLimitAndRandom (limit, callback) { function listWithLimitAndRandom (limit, callback) {
const self = this const self = this
self.count(function (err, count) { self.count().asCallback(function (err, count) {
if (err) return callback(err) if (err) return callback(err)
// Optimization...
if (count === 0) return callback(null, [])
let start = Math.floor(Math.random() * count) - limit let start = Math.floor(Math.random() * count) - limit
if (start < 0) start = 0 if (start < 0) start = 0
self.find().sort({ _id: 1 }).skip(start).limit(limit).exec(callback) const query = {
order: [
[ 'id', 'ASC' ]
],
offset: start,
limit: limit,
include: [ this.sequelize.models.Pod ]
}
self.findAll(query).asCallback(callback)
}) })
} }
function removeAll (callback) { function removeAll (callback) {
this.remove({ }, callback) // Delete all requests
} this.destroy({ truncate: true }).asCallback(callback)
function removePodOf (requestsIds, podId, callback) {
if (!callback) callback = function () {}
this.update({ _id: { $in: requestsIds } }, { $pull: { to: podId } }, { multi: true }, callback)
} }
function removeWithEmptyTo (callback) { function removeWithEmptyTo (callback) {
if (!callback) callback = function () {} if (!callback) callback = function () {}
this.remove({ to: { $size: 0 } }, callback) const query = {
where: {
id: {
$notIn: [
this.sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
]
}
}
}
this.destroy(query).asCallback(callback)
} }

View File

@ -0,0 +1,30 @@
'use strict'
// ---------------------------------------------------------------------------
module.exports = function (sequelize, DataTypes) {
const RequestToPod = sequelize.define('RequestToPod', {}, {
classMethods: {
removePodOf
}
})
return RequestToPod
}
// ---------------------------------------------------------------------------
function removePodOf (requestsIds, podId, callback) {
if (!callback) callback = function () {}
const query = {
where: {
requestId: {
$in: requestsIds
},
podId: podId
}
}
this.destroy(query).asCallback(callback)
}

View File

@ -1,60 +1,60 @@
const mongoose = require('mongoose')
const customUsersValidators = require('../helpers/custom-validators').users
const modelUtils = require('./utils') const modelUtils = require('./utils')
const peertubeCrypto = require('../helpers/peertube-crypto') const peertubeCrypto = require('../helpers/peertube-crypto')
const OAuthToken = mongoose.model('OAuthToken')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const UserSchema = mongoose.Schema({ module.exports = function (sequelize, DataTypes) {
createdDate: { const User = sequelize.define('User',
type: Date, {
default: Date.now password: {
}, type: DataTypes.STRING
password: String, },
username: String, username: {
role: String type: DataTypes.STRING
}) },
role: {
type: DataTypes.STRING
}
},
{
classMethods: {
associate,
UserSchema.path('password').required(customUsersValidators.isUserPasswordValid) countTotal,
UserSchema.path('username').required(customUsersValidators.isUserUsernameValid) getByUsername,
UserSchema.path('role').validate(customUsersValidators.isUserRoleValid) list,
listForApi,
loadById,
loadByUsername
},
instanceMethods: {
isPasswordMatch,
toFormatedJSON
},
hooks: {
beforeCreate: beforeCreateOrUpdate,
beforeUpdate: beforeCreateOrUpdate
}
}
)
UserSchema.methods = { return User
isPasswordMatch,
toFormatedJSON
} }
UserSchema.statics = { // TODO: Validation
countTotal, // UserSchema.path('password').required(customUsersValidators.isUserPasswordValid)
getByUsername, // UserSchema.path('username').required(customUsersValidators.isUserUsernameValid)
list, // UserSchema.path('role').validate(customUsersValidators.isUserRoleValid)
listForApi,
loadById,
loadByUsername
}
UserSchema.pre('save', function (next) { function beforeCreateOrUpdate (user, options, next) {
const user = this peertubeCrypto.cryptPassword(user.password, function (err, hash) {
peertubeCrypto.cryptPassword(this.password, function (err, hash) {
if (err) return next(err) if (err) return next(err)
user.password = hash user.password = hash
return next() return next()
}) })
}) }
UserSchema.pre('remove', function (next) {
const user = this
OAuthToken.removeByUserId(user._id, next)
})
mongoose.model('User', UserSchema)
// ------------------------------ METHODS ------------------------------ // ------------------------------ METHODS ------------------------------
@ -64,35 +64,63 @@ function isPasswordMatch (password, callback) {
function toFormatedJSON () { function toFormatedJSON () {
return { return {
id: this._id, id: this.id,
username: this.username, username: this.username,
role: this.role, role: this.role,
createdDate: this.createdDate createdAt: this.createdAt
} }
} }
// ------------------------------ STATICS ------------------------------ // ------------------------------ STATICS ------------------------------
function associate (models) {
this.hasMany(models.OAuthToken, {
foreignKey: 'userId',
onDelete: 'cascade'
})
}
function countTotal (callback) { function countTotal (callback) {
return this.count(callback) return this.count().asCallback(callback)
} }
function getByUsername (username) { function getByUsername (username) {
return this.findOne({ username: username }) const query = {
where: {
username: username
}
}
return this.findOne(query)
} }
function list (callback) { function list (callback) {
return this.find(callback) return this.find().asCallback(callback)
} }
function listForApi (start, count, sort, callback) { function listForApi (start, count, sort, callback) {
const query = {} const query = {
return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) offset: start,
limit: count,
order: [ modelUtils.getSort(sort) ]
}
return this.findAndCountAll(query).asCallback(function (err, result) {
if (err) return callback(err)
return callback(null, result.rows, result.count)
})
} }
function loadById (id, callback) { function loadById (id, callback) {
return this.findById(id, callback) return this.findById(id).asCallback(callback)
} }
function loadByUsername (username, callback) { function loadByUsername (username, callback) {
return this.findOne({ username: username }, callback) const query = {
where: {
username: username
}
}
return this.findOne(query).asCallback(callback)
} }

View File

@ -1,28 +1,23 @@
'use strict' 'use strict'
const parallel = require('async/parallel')
const utils = { const utils = {
listForApiWithCount getSort
} }
function listForApiWithCount (query, start, count, sort, callback) { // Translate for example "-name" to [ 'name', 'DESC' ]
const self = this function getSort (value) {
let field
let direction
parallel([ if (value.substring(0, 1) === '-') {
function (asyncCallback) { direction = 'DESC'
self.find(query).skip(start).limit(count).sort(sort).exec(asyncCallback) field = value.substring(1)
}, } else {
function (asyncCallback) { direction = 'ASC'
self.count(query, asyncCallback) field = value
} }
], function (err, results) {
if (err) return callback(err)
const data = results[0] return [ field, direction ]
const total = results[1]
return callback(null, data, total)
})
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -7,70 +7,134 @@ const magnetUtil = require('magnet-uri')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const parseTorrent = require('parse-torrent') const parseTorrent = require('parse-torrent')
const pathUtils = require('path') const pathUtils = require('path')
const mongoose = require('mongoose')
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
const customVideosValidators = require('../helpers/custom-validators').videos
const logger = require('../helpers/logger') const logger = require('../helpers/logger')
const modelUtils = require('./utils') const modelUtils = require('./utils')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
module.exports = function (sequelize, DataTypes) {
// TODO: add indexes on searchable columns // TODO: add indexes on searchable columns
const VideoSchema = mongoose.Schema({ const Video = sequelize.define('Video',
name: String, {
extname: { id: {
type: String, type: DataTypes.UUID,
enum: [ '.mp4', '.webm', '.ogv' ] defaultValue: DataTypes.UUIDV4,
}, primaryKey: true
remoteId: mongoose.Schema.Types.ObjectId, },
description: String, name: {
magnet: { type: DataTypes.STRING
infoHash: String },
}, extname: {
podHost: String, // TODO: enum?
author: String, type: DataTypes.STRING
duration: Number, },
tags: [ String ], remoteId: {
createdDate: { type: DataTypes.UUID
type: Date, },
default: Date.now description: {
type: DataTypes.STRING
},
infoHash: {
type: DataTypes.STRING
},
duration: {
type: DataTypes.INTEGER
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING)
}
},
{
classMethods: {
associate,
generateThumbnailFromBase64,
getDurationFromFile,
listForApi,
listByHostAndRemoteId,
listOwnedAndPopulateAuthor,
listOwnedByAuthor,
load,
loadAndPopulateAuthor,
loadAndPopulateAuthorAndPod,
searchAndPopulateAuthorAndPod
},
instanceMethods: {
generateMagnetUri,
getVideoFilename,
getThumbnailName,
getPreviewName,
getTorrentName,
isOwned,
toFormatedJSON,
toRemoteJSON
},
hooks: {
beforeCreate,
afterDestroy
}
}
)
return Video
}
// TODO: Validation
// VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
// VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
// VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
// VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
// VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
// VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
function beforeCreate (video, options, next) {
const tasks = []
if (video.isOwned()) {
const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
tasks.push(
// TODO: refractoring
function (callback) {
const options = {
announceList: [
[ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
],
urlList: [
constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
]
}
createTorrent(videoPath, options, function (err, torrent) {
if (err) return callback(err)
fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
if (err) return callback(err)
const parsedTorrent = parseTorrent(torrent)
video.infoHash = parsedTorrent.infoHash
callback(null)
})
})
},
function (callback) {
createThumbnail(video, videoPath, callback)
},
function (callback) {
createPreview(video, videoPath, callback)
}
)
return parallel(tasks, next)
} }
})
VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid) return next()
VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
VideoSchema.methods = {
generateMagnetUri,
getVideoFilename,
getThumbnailName,
getPreviewName,
getTorrentName,
isOwned,
toFormatedJSON,
toRemoteJSON
} }
VideoSchema.statics = { function afterDestroy (video, options, next) {
generateThumbnailFromBase64,
getDurationFromFile,
listForApi,
listByHostAndRemoteId,
listByHost,
listOwned,
listOwnedByAuthor,
listRemotes,
load,
search
}
VideoSchema.pre('remove', function (next) {
const video = this
const tasks = [] const tasks = []
tasks.push( tasks.push(
@ -94,59 +158,20 @@ VideoSchema.pre('remove', function (next) {
} }
parallel(tasks, next) parallel(tasks, next)
}) }
VideoSchema.pre('save', function (next) {
const video = this
const tasks = []
if (video.isOwned()) {
const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
this.podHost = constants.CONFIG.WEBSERVER.HOST
tasks.push(
// TODO: refractoring
function (callback) {
const options = {
announceList: [
[ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
],
urlList: [
constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
]
}
createTorrent(videoPath, options, function (err, torrent) {
if (err) return callback(err)
fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
if (err) return callback(err)
const parsedTorrent = parseTorrent(torrent)
video.magnet.infoHash = parsedTorrent.infoHash
callback(null)
})
})
},
function (callback) {
createThumbnail(video, videoPath, callback)
},
function (callback) {
createPreview(video, videoPath, callback)
}
)
return parallel(tasks, next)
}
return next()
})
mongoose.model('Video', VideoSchema)
// ------------------------------ METHODS ------------------------------ // ------------------------------ METHODS ------------------------------
function associate (models) {
this.belongsTo(models.Author, {
foreignKey: {
name: 'authorId',
allowNull: false
},
onDelete: 'cascade'
})
}
function generateMagnetUri () { function generateMagnetUri () {
let baseUrlHttp, baseUrlWs let baseUrlHttp, baseUrlWs
@ -154,8 +179,8 @@ function generateMagnetUri () {
baseUrlHttp = constants.CONFIG.WEBSERVER.URL baseUrlHttp = constants.CONFIG.WEBSERVER.URL
baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
} else { } else {
baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.Author.Pod.host
baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.podHost baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.Author.Pod.host
} }
const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName() const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
@ -166,7 +191,7 @@ function generateMagnetUri () {
xs, xs,
announce, announce,
urlList, urlList,
infoHash: this.magnet.infoHash, infoHash: this.infoHash,
name: this.name name: this.name
} }
@ -174,20 +199,20 @@ function generateMagnetUri () {
} }
function getVideoFilename () { function getVideoFilename () {
if (this.isOwned()) return this._id + this.extname if (this.isOwned()) return this.id + this.extname
return this.remoteId + this.extname return this.remoteId + this.extname
} }
function getThumbnailName () { function getThumbnailName () {
// We always have a copy of the thumbnail // We always have a copy of the thumbnail
return this._id + '.jpg' return this.id + '.jpg'
} }
function getPreviewName () { function getPreviewName () {
const extension = '.jpg' const extension = '.jpg'
if (this.isOwned()) return this._id + extension if (this.isOwned()) return this.id + extension
return this.remoteId + extension return this.remoteId + extension
} }
@ -195,7 +220,7 @@ function getPreviewName () {
function getTorrentName () { function getTorrentName () {
const extension = '.torrent' const extension = '.torrent'
if (this.isOwned()) return this._id + extension if (this.isOwned()) return this.id + extension
return this.remoteId + extension return this.remoteId + extension
} }
@ -205,18 +230,27 @@ function isOwned () {
} }
function toFormatedJSON () { function toFormatedJSON () {
let podHost
if (this.Author.Pod) {
podHost = this.Author.Pod.host
} else {
// It means it's our video
podHost = constants.CONFIG.WEBSERVER.HOST
}
const json = { const json = {
id: this._id, id: this.id,
name: this.name, name: this.name,
description: this.description, description: this.description,
podHost: this.podHost, podHost,
isLocal: this.isOwned(), isLocal: this.isOwned(),
magnetUri: this.generateMagnetUri(), magnetUri: this.generateMagnetUri(),
author: this.author, author: this.Author.name,
duration: this.duration, duration: this.duration,
tags: this.tags, tags: this.tags,
thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(), thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(),
createdDate: this.createdDate createdAt: this.createdAt
} }
return json return json
@ -236,13 +270,13 @@ function toRemoteJSON (callback) {
const remoteVideo = { const remoteVideo = {
name: self.name, name: self.name,
description: self.description, description: self.description,
magnet: self.magnet, infoHash: self.infoHash,
remoteId: self._id, remoteId: self.id,
author: self.author, author: self.Author.name,
duration: self.duration, duration: self.duration,
thumbnailBase64: new Buffer(thumbnailData).toString('base64'), thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
tags: self.tags, tags: self.tags,
createdDate: self.createdDate, createdAt: self.createdAt,
extname: self.extname extname: self.extname
} }
@ -273,50 +307,168 @@ function getDurationFromFile (videoPath, callback) {
} }
function listForApi (start, count, sort, callback) { function listForApi (start, count, sort, callback) {
const query = {} const query = {
return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) offset: start,
limit: count,
order: [ modelUtils.getSort(sort) ],
include: [
{
model: this.sequelize.models.Author,
include: [ this.sequelize.models.Pod ]
}
]
}
return this.findAndCountAll(query).asCallback(function (err, result) {
if (err) return callback(err)
return callback(null, result.rows, result.count)
})
} }
function listByHostAndRemoteId (fromHost, remoteId, callback) { function listByHostAndRemoteId (fromHost, remoteId, callback) {
this.find({ podHost: fromHost, remoteId: remoteId }, callback) const query = {
where: {
remoteId: remoteId
},
include: [
{
model: this.sequelize.models.Author,
include: [
{
model: this.sequelize.models.Pod,
where: {
host: fromHost
}
}
]
}
]
}
return this.findAll(query).asCallback(callback)
} }
function listByHost (fromHost, callback) { function listOwnedAndPopulateAuthor (callback) {
this.find({ podHost: fromHost }, callback)
}
function listOwned (callback) {
// If remoteId is null this is *our* video // If remoteId is null this is *our* video
this.find({ remoteId: null }, callback) const query = {
where: {
remoteId: null
},
include: [ this.sequelize.models.Author ]
}
return this.findAll(query).asCallback(callback)
} }
function listOwnedByAuthor (author, callback) { function listOwnedByAuthor (author, callback) {
this.find({ remoteId: null, author: author }, callback) const query = {
} where: {
remoteId: null
},
include: [
{
model: this.sequelize.models.Author,
where: {
name: author
}
}
]
}
function listRemotes (callback) { return this.findAll(query).asCallback(callback)
this.find({ remoteId: { $ne: null } }, callback)
} }
function load (id, callback) { function load (id, callback) {
this.findById(id, callback) return this.findById(id).asCallback(callback)
} }
function search (value, field, start, count, sort, callback) { function loadAndPopulateAuthor (id, callback) {
const query = {} const options = {
include: [ this.sequelize.models.Author ]
}
return this.findById(id, options).asCallback(callback)
}
function loadAndPopulateAuthorAndPod (id, callback) {
const options = {
include: [
{
model: this.sequelize.models.Author,
include: [ this.sequelize.models.Pod ]
}
]
}
return this.findById(id, options).asCallback(callback)
}
function searchAndPopulateAuthorAndPod (value, field, start, count, sort, callback) {
const podInclude = {
model: this.sequelize.models.Pod
}
const authorInclude = {
model: this.sequelize.models.Author,
include: [
podInclude
]
}
const query = {
where: {},
include: [
authorInclude
],
offset: start,
limit: count,
order: [ modelUtils.getSort(sort) ]
}
// TODO: include our pod for podHost searches (we are not stored in the database)
// Make an exact search with the magnet // Make an exact search with the magnet
if (field === 'magnetUri') { if (field === 'magnetUri') {
const infoHash = magnetUtil.decode(value).infoHash const infoHash = magnetUtil.decode(value).infoHash
query.magnet = { query.where.infoHash = infoHash
infoHash
}
} else if (field === 'tags') { } else if (field === 'tags') {
query[field] = value query.where[field] = value
} else if (field === 'host') {
const whereQuery = {
'$Author.Pod.host$': {
$like: '%' + value + '%'
}
}
// Include our pod? (not stored in the database)
if (constants.CONFIG.WEBSERVER.HOST.indexOf(value) !== -1) {
query.where = {
$or: [
whereQuery,
{
remoteId: null
}
]
}
} else {
query.where = whereQuery
}
} else if (field === 'author') {
query.where = {
'$Author.name$': {
$like: '%' + value + '%'
}
}
} else { } else {
query[field] = new RegExp(value, 'i') query.where[field] = {
$like: '%' + value + '%'
}
} }
modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) return this.findAndCountAll(query).asCallback(function (err, result) {
if (err) return callback(err)
return callback(null, result.rows, result.count)
})
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -465,7 +465,7 @@ describe('Test parameters validator', function () {
it('Should return 404 with an incorrect video', function (done) { it('Should return 404 with an incorrect video', function (done) {
request(server.url) request(server.url)
.get(path + '123456789012345678901234') .get(path + '4da6fde3-88f7-4d16-b119-108df5630b06')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect(404, done) .expect(404, done)
}) })
@ -490,7 +490,7 @@ describe('Test parameters validator', function () {
it('Should fail with a video which does not exist', function (done) { it('Should fail with a video which does not exist', function (done) {
request(server.url) request(server.url)
.delete(path + '123456789012345678901234') .delete(path + '4da6fde3-88f7-4d16-b119-108df5630b06')
.set('Authorization', 'Bearer ' + server.accessToken) .set('Authorization', 'Bearer ' + server.accessToken)
.expect(404, done) .expect(404, done)
}) })
@ -711,7 +711,7 @@ describe('Test parameters validator', function () {
it('Should return 404 with a non existing id', function (done) { it('Should return 404 with a non existing id', function (done) {
request(server.url) request(server.url)
.delete(path + '579f982228c99c221d8092b8') .delete(path + '45')
.set('Authorization', 'Bearer ' + server.accessToken) .set('Authorization', 'Bearer ' + server.accessToken)
.expect(404, done) .expect(404, done)
}) })

View File

@ -97,7 +97,7 @@ describe('Test basic friends', function () {
const pod = result[0] const pod = result[0]
expect(pod.host).to.equal(servers[2].host) expect(pod.host).to.equal(servers[2].host)
expect(pod.score).to.equal(20) expect(pod.score).to.equal(20)
expect(miscsUtils.dateIsValid(pod.createdDate)).to.be.true expect(miscsUtils.dateIsValid(pod.createdAt)).to.be.true
next() next()
}) })
@ -114,7 +114,7 @@ describe('Test basic friends', function () {
const pod = result[0] const pod = result[0]
expect(pod.host).to.equal(servers[1].host) expect(pod.host).to.equal(servers[1].host)
expect(pod.score).to.equal(20) expect(pod.score).to.equal(20)
expect(miscsUtils.dateIsValid(pod.createdDate)).to.be.true expect(miscsUtils.dateIsValid(pod.createdAt)).to.be.true
next() next()
}) })

View File

@ -104,7 +104,7 @@ describe('Test multiple pods', function () {
expect(video.magnetUri).to.exist expect(video.magnetUri).to.exist
expect(video.duration).to.equal(10) expect(video.duration).to.equal(10)
expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
if (server.url !== 'http://localhost:9001') { if (server.url !== 'http://localhost:9001') {
@ -166,7 +166,7 @@ describe('Test multiple pods', function () {
expect(video.magnetUri).to.exist expect(video.magnetUri).to.exist
expect(video.duration).to.equal(5) expect(video.duration).to.equal(5)
expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
if (server.url !== 'http://localhost:9002') { if (server.url !== 'http://localhost:9002') {
@ -246,7 +246,7 @@ describe('Test multiple pods', function () {
expect(video1.duration).to.equal(5) expect(video1.duration).to.equal(5)
expect(video1.tags).to.deep.equal([ 'tag1p3' ]) expect(video1.tags).to.deep.equal([ 'tag1p3' ])
expect(video1.author).to.equal('root') expect(video1.author).to.equal('root')
expect(miscsUtils.dateIsValid(video1.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video1.createdAt)).to.be.true
expect(video2.name).to.equal('my super name for pod 3-2') expect(video2.name).to.equal('my super name for pod 3-2')
expect(video2.description).to.equal('my super description for pod 3-2') expect(video2.description).to.equal('my super description for pod 3-2')
@ -255,7 +255,7 @@ describe('Test multiple pods', function () {
expect(video2.duration).to.equal(5) expect(video2.duration).to.equal(5)
expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
expect(video2.author).to.equal('root') expect(video2.author).to.equal('root')
expect(miscsUtils.dateIsValid(video2.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video2.createdAt)).to.be.true
if (server.url !== 'http://localhost:9003') { if (server.url !== 'http://localhost:9003') {
expect(video1.isLocal).to.be.false expect(video1.isLocal).to.be.false

View File

@ -69,7 +69,7 @@ describe('Test requests stats', function () {
}) })
}) })
it('Should have the correct request', function (done) { it('Should have the correct total request', function (done) {
this.timeout(15000) this.timeout(15000)
const server = servers[0] const server = servers[0]
@ -83,11 +83,7 @@ describe('Test requests stats', function () {
if (err) throw err if (err) throw err
const body = res.body const body = res.body
expect(body.requests).to.have.lengthOf(1) expect(body.totalRequests).to.equal(1)
const request = body.requests[0]
expect(request.to).to.have.lengthOf(1)
expect(request.request.type).to.equal('add')
// Wait one cycle // Wait one cycle
setTimeout(done, 10000) setTimeout(done, 10000)
@ -95,27 +91,6 @@ describe('Test requests stats', function () {
}) })
}) })
it('Should have the correct requests', function (done) {
const server = servers[0]
uploadVideo(server, function (err) {
if (err) throw err
getRequestsStats(server, function (err, res) {
if (err) throw err
const body = res.body
expect(body.requests).to.have.lengthOf(2)
const request = body.requests[1]
expect(request.to).to.have.lengthOf(1)
expect(request.request.type).to.equal('add')
done()
})
})
})
after(function (done) { after(function (done) {
process.kill(-servers[0].app.pid) process.kill(-servers[0].app.pid)

View File

@ -82,7 +82,7 @@ describe('Test a single pod', function () {
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) {
if (err) throw err if (err) throw err
@ -116,7 +116,7 @@ describe('Test a single pod', function () {
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) {
if (err) throw err if (err) throw err
@ -142,7 +142,7 @@ describe('Test a single pod', function () {
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) {
if (err) throw err if (err) throw err
@ -154,7 +154,7 @@ describe('Test a single pod', function () {
}) })
it('Should search the video by podHost', function (done) { it('Should search the video by podHost', function (done) {
videosUtils.searchVideo(server.url, '9001', 'podHost', function (err, res) { videosUtils.searchVideo(server.url, '9001', 'host', function (err, res) {
if (err) throw err if (err) throw err
expect(res.body.total).to.equal(1) expect(res.body.total).to.equal(1)
@ -168,7 +168,7 @@ describe('Test a single pod', function () {
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) {
if (err) throw err if (err) throw err
@ -194,7 +194,7 @@ describe('Test a single pod', function () {
expect(video.author).to.equal('root') expect(video.author).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(miscsUtils.dateIsValid(video.createdDate)).to.be.true expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) { videosUtils.testVideoImage(server.url, 'video_short.webm', video.thumbnailPath, function (err, test) {
if (err) throw err if (err) throw err
@ -425,7 +425,7 @@ describe('Test a single pod', function () {
}) })
it('Should search all the 9001 port videos', function (done) { it('Should search all the 9001 port videos', function (done) {
videosUtils.searchVideoWithPagination(server.url, '9001', 'podHost', 0, 15, function (err, res) { videosUtils.searchVideoWithPagination(server.url, '9001', 'host', 0, 15, function (err, res) {
if (err) throw err if (err) throw err
const videos = res.body.data const videos = res.body.data
@ -437,7 +437,7 @@ describe('Test a single pod', function () {
}) })
it('Should search all the localhost videos', function (done) { it('Should search all the localhost videos', function (done) {
videosUtils.searchVideoWithPagination(server.url, 'localhost', 'podHost', 0, 15, function (err, res) { videosUtils.searchVideoWithPagination(server.url, 'localhost', 'host', 0, 15, function (err, res) {
if (err) throw err if (err) throw err
const videos = res.body.data const videos = res.body.data

View File

@ -261,8 +261,8 @@ describe('Test users', function () {
}) })
}) })
it('Should list only the second user by createdDate desc', function (done) { it('Should list only the second user by createdAt desc', function (done) {
usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-createdDate', function (err, res) { usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-createdAt', function (err, res) {
if (err) throw err if (err) throw err
const result = res.body const result = res.body
@ -279,8 +279,8 @@ describe('Test users', function () {
}) })
}) })
it('Should list all the users by createdDate asc', function (done) { it('Should list all the users by createdAt asc', function (done) {
usersUtils.getUsersListPaginationAndSort(server.url, 0, 2, 'createdDate', function (err, res) { usersUtils.getUsersListPaginationAndSort(server.url, 0, 2, 'createdAt', function (err, res) {
if (err) throw err if (err) throw err
const result = res.body const result = res.body

View File

@ -60,12 +60,12 @@ function runServer (number, callback) {
// These actions are async so we need to be sure that they have both been done // These actions are async so we need to be sure that they have both been done
const serverRunString = { const serverRunString = {
'Connected to mongodb': false, 'Database is ready': false,
'Server listening on port': false 'Server listening on port': false
} }
const regexps = { const regexps = {
client_id: 'Client id: ([a-f0-9]+)', client_id: 'Client id: (.+)',
client_secret: 'Client secret: (.+)', client_secret: 'Client secret: (.+)',
user_username: 'Username: (.+)', user_username: 'Username: (.+)',
user_password: 'User password: (.+)' user_password: 'User password: (.+)'

View File

@ -25,7 +25,7 @@ function getAllVideosListBy (url, end) {
request(url) request(url)
.get(path) .get(path)
.query({ sort: 'createdDate' }) .query({ sort: 'createdAt' })
.query({ start: 0 }) .query({ start: 0 })
.query({ count: 10000 }) .query({ count: 10000 })
.set('Accept', 'application/json') .set('Accept', 'application/json')