private getFriends() {
this.friendService.getFriends().subscribe(
- friends => this.friends = friends,
+ res => this.friends = res.friends,
err => alert(err.text)
);
import { Observable } from 'rxjs/Observable';
import { Friend } from './friend.model';
-import { AuthHttp, RestExtractor } from '../../../shared';
+import { AuthHttp, RestExtractor, ResultList } from '../../../shared';
@Injectable()
export class FriendService {
private restExtractor: RestExtractor
) {}
- getFriends(): Observable<Friend[]> {
+ getFriends() {
return this.authHttp.get(FriendService.BASE_FRIEND_URL)
- // Not implemented as a data list by the server yet
- // .map(this.restExtractor.extractDataList)
- .map((res) => res.json())
+ .map(this.restExtractor.extractDataList)
+ .map(this.extractFriends)
.catch((res) => this.restExtractor.handleError(res));
}
.map(res => res.status)
.catch((res) => this.restExtractor.handleError(res));
}
+
+ private extractFriends(result: ResultList) {
+ const friends: Friend[] = result.data;
+ const totalFriends = result.total;
+
+ return { friends, totalFriends };
+ }
}
hostname: 'localhost'
port: 5432
suffix: '_dev'
- username: peertube
- password: peertube
+ username: 'peertube'
+ password: 'peertube'
# From the project root directory
storage:
customValidators.misc,
customValidators.pods,
customValidators.users,
- customValidators.videos
+ customValidators.videos,
+ customValidators.remote.videos
)
}))
const db = require('../../initializers/database')
const logger = require('../../helpers/logger')
+const utils = require('../../helpers/utils')
const friends = require('../../lib/friends')
const middlewares = require('../../middlewares')
const admin = middlewares.admin
)
// Post because this is a secured request
router.post('/remove',
- signatureValidator,
+ signatureValidator.signature,
checkSignature,
removePods
)
db.Pod.list(function (err, podsList) {
if (err) return next(err)
- res.json(getFormatedPods(podsList))
+ res.json(utils.getFormatedObjects(podsList, podsList.length))
})
}
res.type('json').status(204).end()
})
}
-
-// ---------------------------------------------------------------------------
-
-function getFormatedPods (pods) {
- const formatedPods = []
-
- pods.forEach(function (pod) {
- formatedPods.push(pod.toFormatedJSON())
- })
-
- return formatedPods
-}
const db = require('../../../initializers/database')
const middlewares = require('../../../middlewares')
const secureMiddleware = middlewares.secure
-const validators = middlewares.validators.remote
+const videosValidators = middlewares.validators.remote.videos
+const signatureValidators = middlewares.validators.remote.signature
const logger = require('../../../helpers/logger')
const router = express.Router()
router.post('/',
- validators.signature,
+ signatureValidators.signature,
secureMiddleware.checkSignature,
- validators.remoteVideos,
+ videosValidators.remoteVideos,
remoteVideos
)
// We need to process in the same order to keep consistency
// TODO: optimization
eachSeries(requests, function (request, callbackEach) {
- const videoData = request.data
+ const data = request.data
switch (request.type) {
case 'add':
- addRemoteVideo(videoData, fromPod, callbackEach)
+ addRemoteVideo(data, fromPod, callbackEach)
break
case 'update':
- updateRemoteVideo(videoData, fromPod, callbackEach)
+ updateRemoteVideo(data, fromPod, callbackEach)
break
case 'remove':
- removeRemoteVideo(videoData, fromPod, callbackEach)
+ removeRemoteVideo(data, fromPod, callbackEach)
+ break
+
+ case 'report-abuse':
+ reportAbuseRemoteVideo(data, fromPod, callbackEach)
break
default:
},
function findVideo (t, callback) {
- db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
- if (err || !videoInstance) {
- logger.error('Cannot load video from host and remote id.', { error: err.message })
- return callback(err)
- }
-
- return callback(null, t, videoInstance)
+ fetchVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
+ return callback(err, t, videoInstance)
})
},
function removeRemoteVideo (videoToRemoveData, fromPod, callback) {
// We need the instance because we have to remove some other stuffs (thumbnail etc)
- db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
+ fetchVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
+ if (err) return callback(err)
+
+ logger.debug('Removing remote video %s.', video.remoteId)
+ video.destroy().asCallback(callback)
+ })
+}
+
+function reportAbuseRemoteVideo (reportData, fromPod, callback) {
+ db.Video.load(reportData.videoRemoteId, function (err, video) {
if (err || !video) {
- logger.error('Cannot load video from host and remote id.', { error: err.message })
+ if (!err) err = new Error('video not found')
+
+ logger.error('Cannot load video from host and remote id.', { error: err })
return callback(err)
}
- logger.debug('Removing remote video %s.', video.remoteId)
- video.destroy().asCallback(callback)
+ logger.debug('Reporting remote abuse for video %s.', video.id)
+
+ const videoAbuseData = {
+ reporterUsername: reportData.reporterUsername,
+ reason: reportData.reportReason,
+ reporterPodId: fromPod.id,
+ videoId: video.id
+ }
+
+ db.VideoAbuse.create(videoAbuseData).asCallback(callback)
+ })
+}
+
+function fetchVideo (podHost, remoteId, callback) {
+ db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
+ if (err || !video) {
+ if (!err) err = new Error('video not found')
+
+ logger.error('Cannot load video from host and remote id.', { error: err })
+ return callback(err)
+ }
+
+ return callback(null, video)
})
}
const constants = require('../../initializers/constants')
const db = require('../../initializers/database')
const logger = require('../../helpers/logger')
+const utils = require('../../helpers/utils')
const middlewares = require('../../middlewares')
const admin = middlewares.admin
const oAuth = middlewares.oauth
db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
if (err) return next(err)
- res.json(getFormatedUsers(usersList, usersTotal))
+ res.json(utils.getFormatedObjects(usersList, usersTotal))
})
}
function success (req, res, next) {
res.end()
}
-
-// ---------------------------------------------------------------------------
-
-function getFormatedUsers (users, usersTotal) {
- const formatedUsers = []
-
- users.forEach(function (user) {
- formatedUsers.push(user.toFormatedJSON())
- })
-
- return {
- total: usersTotal,
- data: formatedUsers
- }
-}
const logger = require('../../helpers/logger')
const friends = require('../../lib/friends')
const middlewares = require('../../middlewares')
+const admin = middlewares.admin
const oAuth = middlewares.oauth
const pagination = middlewares.pagination
const validators = middlewares.validators
const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
+router.get('/abuse',
+ oAuth.authenticate,
+ admin.ensureIsAdmin,
+ validatorsPagination.pagination,
+ validatorsSort.videoAbusesSort,
+ sort.setVideoAbusesSort,
+ pagination.setPagination,
+ listVideoAbuses
+)
+router.post('/:id/abuse',
+ oAuth.authenticate,
+ validatorsVideos.videoAbuseReport,
+ reportVideoAbuse
+)
+
router.get('/',
validatorsPagination.pagination,
validatorsSort.videosSort,
db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
if (err) return next(err)
- res.json(getFormatedVideos(videosList, videosTotal))
+ res.json(utils.getFormatedObjects(videosList, videosTotal))
})
}
function (err, videosList, videosTotal) {
if (err) return next(err)
- res.json(getFormatedVideos(videosList, videosTotal))
+ res.json(utils.getFormatedObjects(videosList, videosTotal))
}
)
}
-// ---------------------------------------------------------------------------
-
-function getFormatedVideos (videos, videosTotal) {
- const formatedVideos = []
+function listVideoAbuses (req, res, next) {
+ db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) {
+ if (err) return next(err)
- videos.forEach(function (video) {
- formatedVideos.push(video.toFormatedJSON())
+ res.json(utils.getFormatedObjects(abusesList, abusesTotal))
})
+}
- return {
- total: videosTotal,
- data: formatedVideos
+function reportVideoAbuse (req, res, next) {
+ const videoInstance = res.locals.video
+ const reporterUsername = res.locals.oauth.token.User.username
+
+ const abuse = {
+ reporterUsername,
+ reason: req.body.reason,
+ videoId: videoInstance.id,
+ reporterPodId: null // This is our pod that reported this abuse
}
+
+ db.VideoAbuse.create(abuse).asCallback(function (err) {
+ if (err) return next(err)
+
+ // We send the information to the destination pod
+ if (videoInstance.isOwned() === false) {
+ const reportData = {
+ reporterUsername,
+ reportReason: abuse.reason,
+ videoRemoteId: videoInstance.remoteId
+ }
+
+ friends.reportAbuseVideoToFriend(reportData, videoInstance)
+ }
+
+ return res.type('json').status(204).end()
+ })
}
+
const miscValidators = require('./misc')
const podsValidators = require('./pods')
+const remoteValidators = require('./remote')
const usersValidators = require('./users')
const videosValidators = require('./videos')
const validators = {
misc: miscValidators,
pods: podsValidators,
+ remote: remoteValidators,
users: usersValidators,
videos: videosValidators
}
--- /dev/null
+'use strict'
+
+const remoteVideosValidators = require('./videos')
+
+const validators = {
+ videos: remoteVideosValidators
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = validators
--- /dev/null
+'use strict'
+
+const videosValidators = require('../videos')
+const miscValidators = require('../misc')
+
+const remoteVideosValidators = {
+ isEachRemoteRequestVideosValid
+}
+
+function isEachRemoteRequestVideosValid (requests) {
+ return miscValidators.isArray(requests) &&
+ requests.every(function (request) {
+ const video = request.data
+ return (
+ isRequestTypeAddValid(request.type) &&
+ videosValidators.isVideoAuthorValid(video.author) &&
+ videosValidators.isVideoDateValid(video.createdAt) &&
+ videosValidators.isVideoDateValid(video.updatedAt) &&
+ videosValidators.isVideoDescriptionValid(video.description) &&
+ videosValidators.isVideoDurationValid(video.duration) &&
+ videosValidators.isVideoInfoHashValid(video.infoHash) &&
+ videosValidators.isVideoNameValid(video.name) &&
+ videosValidators.isVideoTagsValid(video.tags) &&
+ videosValidators.isVideoThumbnailDataValid(video.thumbnailData) &&
+ videosValidators.isVideoRemoteIdValid(video.remoteId) &&
+ videosValidators.isVideoExtnameValid(video.extname)
+ ) ||
+ (
+ isRequestTypeUpdateValid(request.type) &&
+ videosValidators.isVideoDateValid(video.createdAt) &&
+ videosValidators.isVideoDateValid(video.updatedAt) &&
+ videosValidators.isVideoDescriptionValid(video.description) &&
+ videosValidators.isVideoDurationValid(video.duration) &&
+ videosValidators.isVideoInfoHashValid(video.infoHash) &&
+ videosValidators.isVideoNameValid(video.name) &&
+ videosValidators.isVideoTagsValid(video.tags) &&
+ videosValidators.isVideoRemoteIdValid(video.remoteId) &&
+ videosValidators.isVideoExtnameValid(video.extname)
+ ) ||
+ (
+ isRequestTypeRemoveValid(request.type) &&
+ videosValidators.isVideoNameValid(video.name) &&
+ videosValidators.isVideoRemoteIdValid(video.remoteId)
+ ) ||
+ (
+ isRequestTypeReportAbuseValid(request.type) &&
+ videosValidators.isVideoRemoteIdValid(request.data.videoRemoteId) &&
+ videosValidators.isVideoAbuseReasonValid(request.data.reportReason) &&
+ videosValidators.isVideoAbuseReporterUsernameValid(request.data.reporterUsername)
+ )
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = remoteVideosValidators
+
+// ---------------------------------------------------------------------------
+
+function isRequestTypeAddValid (value) {
+ return value === 'add'
+}
+
+function isRequestTypeUpdateValid (value) {
+ return value === 'update'
+}
+
+function isRequestTypeRemoveValid (value) {
+ return value === 'remove'
+}
+
+function isRequestTypeReportAbuseValid (value) {
+ return value === 'report-abuse'
+}
const usersValidators = require('./users')
const miscValidators = require('./misc')
const VIDEOS_CONSTRAINTS_FIELDS = constants.CONSTRAINTS_FIELDS.VIDEOS
+const VIDEO_ABUSES_CONSTRAINTS_FIELDS = constants.CONSTRAINTS_FIELDS.VIDEO_ABUSES
const videosValidators = {
- isEachRemoteVideosValid,
isVideoAuthorValid,
isVideoDateValid,
isVideoDescriptionValid,
isVideoNameValid,
isVideoTagsValid,
isVideoThumbnailValid,
- isVideoThumbnailDataValid
-}
-
-function isEachRemoteVideosValid (requests) {
- return miscValidators.isArray(requests) &&
- requests.every(function (request) {
- const video = request.data
- return (
- isRequestTypeAddValid(request.type) &&
- isVideoAuthorValid(video.author) &&
- isVideoDateValid(video.createdAt) &&
- isVideoDateValid(video.updatedAt) &&
- isVideoDescriptionValid(video.description) &&
- isVideoDurationValid(video.duration) &&
- isVideoInfoHashValid(video.infoHash) &&
- isVideoNameValid(video.name) &&
- isVideoTagsValid(video.tags) &&
- isVideoThumbnailDataValid(video.thumbnailData) &&
- isVideoRemoteIdValid(video.remoteId) &&
- isVideoExtnameValid(video.extname)
- ) ||
- (
- isRequestTypeUpdateValid(request.type) &&
- isVideoDateValid(video.createdAt) &&
- isVideoDateValid(video.updatedAt) &&
- isVideoDescriptionValid(video.description) &&
- isVideoDurationValid(video.duration) &&
- isVideoInfoHashValid(video.infoHash) &&
- isVideoNameValid(video.name) &&
- isVideoTagsValid(video.tags) &&
- isVideoRemoteIdValid(video.remoteId) &&
- isVideoExtnameValid(video.extname)
- ) ||
- (
- isRequestTypeRemoveValid(request.type) &&
- isVideoNameValid(video.name) &&
- isVideoRemoteIdValid(video.remoteId)
- )
- })
+ isVideoThumbnailDataValid,
+ isVideoExtnameValid,
+ isVideoRemoteIdValid,
+ isVideoAbuseReasonValid,
+ isVideoAbuseReporterUsernameValid
}
function isVideoAuthorValid (value) {
return validator.isUUID(value, 4)
}
-// ---------------------------------------------------------------------------
-
-module.exports = videosValidators
-
-// ---------------------------------------------------------------------------
-
-function isRequestTypeAddValid (value) {
- return value === 'add'
+function isVideoAbuseReasonValid (value) {
+ return validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
}
-function isRequestTypeUpdateValid (value) {
- return value === 'update'
+function isVideoAbuseReporterUsernameValid (value) {
+ return usersValidators.isUserUsernameValid(value)
}
-function isRequestTypeRemoveValid (value) {
- return value === 'remove'
-}
+// ---------------------------------------------------------------------------
+
+module.exports = videosValidators
badRequest,
cleanForExit,
generateRandomString,
- isTestInstance
+ isTestInstance,
+ getFormatedObjects
}
function badRequest (req, res, next) {
return (process.env.NODE_ENV === 'test')
}
+function getFormatedObjects (objects, objectsTotal) {
+ const formatedObjects = []
+
+ objects.forEach(function (object) {
+ formatedObjects.push(object.toFormatedJSON())
+ })
+
+ return {
+ total: objectsTotal,
+ data: formatedObjects
+ }
+}
+
// ---------------------------------------------------------------------------
module.exports = utils
// Sortable columns per schema
const SORTABLE_COLUMNS = {
USERS: [ 'username', '-username', 'createdAt', '-createdAt' ],
+ VIDEO_ABUSES: [ 'createdAt', '-createdAt' ],
VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdAt', '-createdAt' ]
}
USERNAME: { min: 3, max: 20 }, // Length
PASSWORD: { min: 6, max: 255 } // Length
},
+ VIDEO_ABUSES: {
+ REASON: { min: 2, max: 300 } // Length
+ },
VIDEOS: {
NAME: { min: 3, max: 50 }, // Length
DESCRIPTION: { min: 3, max: 250 }, // Length
const friends = {
addVideoToFriends,
updateVideoToFriends,
+ reportAbuseVideoToFriend,
hasFriends,
getMyCertificate,
makeFriends,
sendOwnedVideosToPod
}
-function addVideoToFriends (video) {
- createRequest('add', constants.REQUEST_ENDPOINTS.VIDEOS, video)
+function addVideoToFriends (videoData) {
+ createRequest('add', constants.REQUEST_ENDPOINTS.VIDEOS, videoData)
}
-function updateVideoToFriends (video) {
- createRequest('update', constants.REQUEST_ENDPOINTS.VIDEOS, video)
+function updateVideoToFriends (videoData) {
+ createRequest('update', constants.REQUEST_ENDPOINTS.VIDEOS, videoData)
+}
+
+function removeVideoToFriends (videoParams) {
+ createRequest('remove', constants.REQUEST_ENDPOINTS.VIDEOS, videoParams)
+}
+
+function reportAbuseVideoToFriend (reportData, video) {
+ createRequest('report-abuse', constants.REQUEST_ENDPOINTS.VIDEOS, reportData, [ video.Author.podId ])
}
function hasFriends (callback) {
})
}
-function removeVideoToFriends (videoParams) {
- createRequest('remove', constants.REQUEST_ENDPOINTS.VIDEOS, videoParams)
-}
-
function sendOwnedVideosToPod (podId) {
db.Video.listOwnedAndPopulateAuthorAndTags(function (err, videosList) {
if (err) {
// ---------------------------------------------------------------------------
function computeForeignPodsList (host, podsScore, callback) {
- getForeignPodsList(host, function (err, foreignPodsList) {
+ getForeignPodsList(host, function (err, res) {
if (err) return callback(err)
- if (!foreignPodsList) foreignPodsList = []
+ const foreignPodsList = res.data
// Let's give 1 point to the pod we ask the friends list
foreignPodsList.push({ host })
})
}
-// Wrapper that populate "to" argument with all our friends if it is not specified
-function createRequest (type, endpoint, data, to) {
- if (to) return _createRequest(type, endpoint, data, to)
+// Wrapper that populate "toIds" argument with all our friends if it is not specified
+function createRequest (type, endpoint, data, toIds) {
+ if (toIds) return _createRequest(type, endpoint, data, toIds)
- // If the "to" pods is not specified, we send the request to all our friends
+ // If the "toIds" pods is not specified, we send the request to all our friends
db.Pod.listAllIds(function (err, podIds) {
if (err) {
logger.error('Cannot get pod ids', { error: err })
})
}
-function _createRequest (type, endpoint, data, to) {
+function _createRequest (type, endpoint, data, toIds) {
const pods = []
// If there are no destination pods abort
- if (to.length === 0) return
+ if (toIds.length === 0) return
- to.forEach(function (toPod) {
+ toIds.forEach(function (toPod) {
pods.push(db.Pod.build({ id: toPod }))
})
const sortMiddleware = {
setUsersSort,
+ setVideoAbusesSort,
setVideosSort
}
return next()
}
+function setVideoAbusesSort (req, res, next) {
+ if (!req.query.sort) req.query.sort = '-createdAt'
+
+ return next()
+}
+
function setVideosSort (req, res, next) {
if (!req.query.sort) req.query.sort = '-createdAt'
+++ /dev/null
-'use strict'
-
-const checkErrors = require('./utils').checkErrors
-const logger = require('../../helpers/logger')
-
-const validatorsRemote = {
- remoteVideos,
- signature
-}
-
-function remoteVideos (req, res, next) {
- req.checkBody('data').isEachRemoteVideosValid()
-
- logger.debug('Checking remoteVideos parameters', { parameters: req.body })
-
- checkErrors(req, res, next)
-}
-
-function signature (req, res, next) {
- req.checkBody('signature.host', 'Should have a signature host').isURL()
- req.checkBody('signature.signature', 'Should have a signature').notEmpty()
-
- logger.debug('Checking signature parameters', { parameters: { signatureHost: req.body.signature.host } })
-
- checkErrors(req, res, next)
-}
-
-// ---------------------------------------------------------------------------
-
-module.exports = validatorsRemote
--- /dev/null
+'use strict'
+
+const remoteSignatureValidators = require('./signature')
+const remoteVideosValidators = require('./videos')
+
+const validators = {
+ signature: remoteSignatureValidators,
+ videos: remoteVideosValidators
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = validators
--- /dev/null
+'use strict'
+
+const checkErrors = require('../utils').checkErrors
+const logger = require('../../../helpers/logger')
+
+const validatorsRemoteSignature = {
+ signature
+}
+
+function signature (req, res, next) {
+ req.checkBody('signature.host', 'Should have a signature host').isURL()
+ req.checkBody('signature.signature', 'Should have a signature').notEmpty()
+
+ logger.debug('Checking signature parameters', { parameters: { signatureHost: req.body.signature.host } })
+
+ checkErrors(req, res, next)
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = validatorsRemoteSignature
--- /dev/null
+'use strict'
+
+const checkErrors = require('../utils').checkErrors
+const logger = require('../../../helpers/logger')
+
+const validatorsRemoteVideos = {
+ remoteVideos
+}
+
+function remoteVideos (req, res, next) {
+ req.checkBody('data').isEachRemoteRequestVideosValid()
+
+ logger.debug('Checking remoteVideos parameters', { parameters: req.body })
+
+ checkErrors(req, res, next)
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = validatorsRemoteVideos
const validatorsSort = {
usersSort,
+ videoAbusesSort,
videosSort
}
function usersSort (req, res, next) {
const sortableColumns = constants.SORTABLE_COLUMNS.USERS
- req.checkQuery('sort', 'Should have correct sortable column').optional().isIn(sortableColumns)
+ checkSort(req, res, next, sortableColumns)
+}
- logger.debug('Checking sort parameters', { parameters: req.query })
+function videoAbusesSort (req, res, next) {
+ const sortableColumns = constants.SORTABLE_COLUMNS.VIDEO_ABUSES
- checkErrors(req, res, next)
+ checkSort(req, res, next, sortableColumns)
}
function videosSort (req, res, next) {
const sortableColumns = constants.SORTABLE_COLUMNS.VIDEOS
+ checkSort(req, res, next, sortableColumns)
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = validatorsSort
+
+// ---------------------------------------------------------------------------
+
+function checkSort (req, res, next, sortableColumns) {
req.checkQuery('sort', 'Should have correct sortable column').optional().isIn(sortableColumns)
logger.debug('Checking sort parameters', { parameters: req.query })
checkErrors(req, res, next)
}
-
-// ---------------------------------------------------------------------------
-
-module.exports = validatorsSort
videosUpdate,
videosGet,
videosRemove,
- videosSearch
+ videosSearch,
+
+ videoAbuseReport
}
function videosAdd (req, res, next) {
checkErrors(req, res, next)
}
+function videoAbuseReport (req, res, next) {
+ req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+ req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid()
+
+ logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
+
+ checkErrors(req, res, function () {
+ checkVideoExists(req.params.id, res, next)
+ })
+}
+
// ---------------------------------------------------------------------------
module.exports = validatorsVideos
--- /dev/null
+'use strict'
+
+// ---------------------------------------------------------------------------
+
+module.exports = function (sequelize, DataTypes) {
+ const RequestToPod = sequelize.define('RequestToPod', {}, {
+ indexes: [
+ {
+ fields: [ 'requestId' ]
+ },
+ {
+ fields: [ 'podId' ]
+ },
+ {
+ fields: [ 'requestId', 'podId' ],
+ unique: true
+ }
+ ],
+ 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)
+}
+++ /dev/null
-'use strict'
-
-// ---------------------------------------------------------------------------
-
-module.exports = function (sequelize, DataTypes) {
- const RequestToPod = sequelize.define('RequestToPod', {}, {
- indexes: [
- {
- fields: [ 'requestId' ]
- },
- {
- fields: [ 'podId' ]
- },
- {
- fields: [ 'requestId', 'podId' ],
- unique: true
- }
- ],
- 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)
-}
--- /dev/null
+'use strict'
+
+const constants = require('../initializers/constants')
+const modelUtils = require('./utils')
+const customVideosValidators = require('../helpers/custom-validators').videos
+
+module.exports = function (sequelize, DataTypes) {
+ const VideoAbuse = sequelize.define('VideoAbuse',
+ {
+ reporterUsername: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ reporterUsernameValid: function (value) {
+ const res = customVideosValidators.isVideoAbuseReporterUsernameValid(value)
+ if (res === false) throw new Error('Video abuse reporter username is not valid.')
+ }
+ }
+ },
+ reason: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ reasonValid: function (value) {
+ const res = customVideosValidators.isVideoAbuseReasonValid(value)
+ if (res === false) throw new Error('Video abuse reason is not valid.')
+ }
+ }
+ }
+ },
+ {
+ indexes: [
+ {
+ fields: [ 'videoId' ]
+ },
+ {
+ fields: [ 'reporterPodId' ]
+ }
+ ],
+ classMethods: {
+ associate,
+
+ listForApi
+ },
+ instanceMethods: {
+ toFormatedJSON
+ }
+ }
+ )
+
+ return VideoAbuse
+}
+
+// ---------------------------------------------------------------------------
+
+function associate (models) {
+ this.belongsTo(models.Pod, {
+ foreignKey: {
+ name: 'reporterPodId',
+ allowNull: true
+ },
+ onDelete: 'cascade'
+ })
+
+ this.belongsTo(models.Video, {
+ foreignKey: {
+ name: 'videoId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+}
+
+function listForApi (start, count, sort, callback) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ modelUtils.getSort(sort) ],
+ include: [
+ {
+ model: this.sequelize.models.Pod,
+ required: false
+ }
+ ]
+ }
+
+ return this.findAndCountAll(query).asCallback(function (err, result) {
+ if (err) return callback(err)
+
+ return callback(null, result.rows, result.count)
+ })
+}
+
+function toFormatedJSON () {
+ let reporterPodHost
+
+ if (this.Pod) {
+ reporterPodHost = this.Pod.host
+ } else {
+ // It means it's our video
+ reporterPodHost = constants.CONFIG.WEBSERVER.HOST
+ }
+
+ const json = {
+ id: this.id,
+ reporterPodHost,
+ reason: this.reason,
+ reporterUsername: this.reporterUsername,
+ videoId: this.videoId
+ }
+
+ return json
+}
--- /dev/null
+'use strict'
+
+// ---------------------------------------------------------------------------
+
+module.exports = function (sequelize, DataTypes) {
+ const VideoTag = sequelize.define('VideoTag', {}, {
+ indexes: [
+ {
+ fields: [ 'videoId' ]
+ },
+ {
+ fields: [ 'tagId' ]
+ }
+ ]
+ })
+
+ return VideoTag
+}
through: models.VideoTag,
onDelete: 'cascade'
})
+
+ this.hasMany(models.VideoAbuse, {
+ foreignKey: {
+ name: 'videoId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
}
function generateMagnetUri () {
+++ /dev/null
-'use strict'
-
-// ---------------------------------------------------------------------------
-
-module.exports = function (sequelize, DataTypes) {
- const VideoTag = sequelize.define('VideoTag', {}, {
- indexes: [
- {
- fields: [ 'videoId' ]
- },
- {
- fields: [ 'tagId' ]
- }
- ]
- })
-
- return VideoTag
-}
require('./users')
require('./requests')
require('./videos')
+require('./video-abuses')
it('Should check when removing a video')
})
+ describe('When reporting abuse on a video', function () {
+ it('Should check when reporting a video abuse')
+ })
+
after(function (done) {
process.kill(-server.app.pid)
--- /dev/null
+'use strict'
+
+const request = require('supertest')
+const series = require('async/series')
+
+const loginUtils = require('../../utils/login')
+const requestsUtils = require('../../utils/requests')
+const serversUtils = require('../../utils/servers')
+const usersUtils = require('../../utils/users')
+const videosUtils = require('../../utils/videos')
+
+describe('Test video abuses API validators', function () {
+ let server = null
+ let userAccessToken = null
+
+ // ---------------------------------------------------------------
+
+ before(function (done) {
+ this.timeout(20000)
+
+ series([
+ function (next) {
+ serversUtils.flushTests(next)
+ },
+ function (next) {
+ serversUtils.runServer(1, function (server1) {
+ server = server1
+
+ next()
+ })
+ },
+ function (next) {
+ loginUtils.loginAndGetAccessToken(server, function (err, token) {
+ if (err) throw err
+ server.accessToken = token
+
+ next()
+ })
+ },
+ function (next) {
+ const username = 'user1'
+ const password = 'my super password'
+
+ usersUtils.createUser(server.url, server.accessToken, username, password, next)
+ },
+ function (next) {
+ const user = {
+ username: 'user1',
+ password: 'my super password'
+ }
+
+ loginUtils.getUserAccessToken(server, user, function (err, accessToken) {
+ if (err) throw err
+
+ userAccessToken = accessToken
+
+ next()
+ })
+ },
+ // Upload some videos on each pods
+ function (next) {
+ const name = 'my super name for pod'
+ const description = 'my super description for pod'
+ const tags = [ 'tag' ]
+ const file = 'video_short2.webm'
+ videosUtils.uploadVideo(server.url, server.accessToken, name, description, tags, file, next)
+ },
+ function (next) {
+ videosUtils.getVideosList(server.url, function (err, res) {
+ if (err) throw err
+
+ const videos = res.body.data
+ server.video = videos[0]
+
+ next()
+ })
+ }
+ ], done)
+ })
+
+ describe('When listing video abuses', function () {
+ const path = '/api/v1/videos/abuse'
+
+ it('Should fail with a bad start pagination', function (done) {
+ request(server.url)
+ .get(path)
+ .query({ start: 'hello' })
+ .set('Authorization', 'Bearer ' + server.accessToken)
+ .set('Accept', 'application/json')
+ .expect(400, done)
+ })
+
+ it('Should fail with a bad count pagination', function (done) {
+ request(server.url)
+ .get(path)
+ .query({ count: 'hello' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + server.accessToken)
+ .expect(400, done)
+ })
+
+ it('Should fail with an incorrect sort', function (done) {
+ request(server.url)
+ .get(path)
+ .query({ sort: 'hello' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + server.accessToken)
+ .expect(400, done)
+ })
+
+ it('Should fail with a non authenticated user', function (done) {
+ request(server.url)
+ .get(path)
+ .query({ sort: 'hello' })
+ .set('Accept', 'application/json')
+ .expect(401, done)
+ })
+
+ it('Should fail with a non admin user', function (done) {
+ request(server.url)
+ .get(path)
+ .query({ sort: 'hello' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + userAccessToken)
+ .expect(403, done)
+ })
+ })
+
+ describe('When reporting a video abuse', function () {
+ const basePath = '/api/v1/videos/'
+
+ it('Should fail with nothing', function (done) {
+ const path = basePath + server.video + '/abuse'
+ const data = {}
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done)
+ })
+
+ it('Should fail with a wrong video', function (done) {
+ const wrongPath = '/api/v1/videos/blabla/abuse'
+ const data = {}
+ requestsUtils.makePostBodyRequest(server.url, wrongPath, server.accessToken, data, done)
+ })
+
+ it('Should fail with a non authenticated user', function (done) {
+ const data = {}
+ const path = basePath + server.video + '/abuse'
+ requestsUtils.makePostBodyRequest(server.url, path, 'hello', data, done, 401)
+ })
+
+ it('Should fail with a reason too short', function (done) {
+ const data = {
+ reason: 'h'
+ }
+ const path = basePath + server.video + '/abuse'
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done)
+ })
+
+ it('Should fail with a reason too big', function (done) {
+ const data = {
+ reason: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' +
+ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' +
+ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' +
+ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
+ }
+ const path = basePath + server.video + '/abuse'
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done)
+ })
+ })
+
+ after(function (done) {
+ process.kill(-server.app.pid)
+
+ // Keep the logs if the test failed
+ if (this.ok) {
+ serversUtils.flushTests(done)
+ } else {
+ done()
+ }
+ })
+})
getFriendsList(5, function (err, res) {
if (err) throw err
- expect(res.body.length).to.equal(0)
+ expect(res.body.data.length).to.equal(0)
done()
})
getFriendsList(i, function (err, res) {
if (err) throw err
- expect(res.body.length).to.equal(0)
+ expect(res.body.data.length).to.equal(0)
callback()
})
getFriendsList(i, function (err, res) {
if (err) throw err
- expect(res.body.length).to.equal(3)
+ expect(res.body.data.length).to.equal(3)
callback()
})
if (err) throw err
// Pod 4 didn't know pod 1 and 2 removed it
- expect(res.body.length).to.equal(3)
+ expect(res.body.data.length).to.equal(3)
next()
})
},
if (err) throw err
// Pod 4 should not be our friend
- const result = res.body
+ const result = res.body.data
expect(result.length).to.equal(3)
for (const pod of result) {
expect(pod.host).not.equal(servers[3].host)
podsUtils.getFriendsList(serverToTest.url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(2)
podsUtils.getFriendsList(server.url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(0)
callback()
podsUtils.getFriendsList(servers[1].url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(1)
podsUtils.getFriendsList(servers[2].url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(1)
podsUtils.getFriendsList(servers[1].url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(0)
podsUtils.getFriendsList(url, function (err, res) {
if (err) throw err
- const result = res.body
+ const result = res.body.data
expect(result).to.be.an('array')
expect(result.length).to.equal(1)
expect(result[0].host).not.to.be.equal(servers[1].host)
--- /dev/null
+'use strict'
+
+const chai = require('chai')
+const each = require('async/each')
+const expect = chai.expect
+const series = require('async/series')
+
+const loginUtils = require('../utils/login')
+const podsUtils = require('../utils/pods')
+const serversUtils = require('../utils/servers')
+const videosUtils = require('../utils/videos')
+const videoAbusesUtils = require('../utils/video-abuses')
+
+describe('Test video abuses', function () {
+ let servers = []
+
+ before(function (done) {
+ this.timeout(30000)
+
+ series([
+ // Run servers
+ function (next) {
+ serversUtils.flushAndRunMultipleServers(2, function (serversRun) {
+ servers = serversRun
+ next()
+ })
+ },
+ // Get the access tokens
+ function (next) {
+ each(servers, function (server, callbackEach) {
+ loginUtils.loginAndGetAccessToken(server, function (err, accessToken) {
+ if (err) return callbackEach(err)
+
+ server.accessToken = accessToken
+ callbackEach()
+ })
+ }, next)
+ },
+ // Pod 1 make friends too
+ function (next) {
+ const server = servers[0]
+ podsUtils.makeFriends(server.url, server.accessToken, next)
+ },
+ // Upload some videos on each pods
+ function (next) {
+ const name = 'my super name for pod 1'
+ const description = 'my super description for pod 1'
+ const tags = [ 'tag' ]
+ const file = 'video_short2.webm'
+ videosUtils.uploadVideo(servers[0].url, servers[0].accessToken, name, description, tags, file, next)
+ },
+ function (next) {
+ const name = 'my super name for pod 2'
+ const description = 'my super description for pod 2'
+ const tags = [ 'tag' ]
+ const file = 'video_short2.webm'
+ videosUtils.uploadVideo(servers[1].url, servers[1].accessToken, name, description, tags, file, next)
+ },
+ // Wait videos propagation
+ function (next) {
+ setTimeout(next, 11000)
+ },
+ function (next) {
+ videosUtils.getVideosList(servers[0].url, function (err, res) {
+ if (err) throw err
+
+ const videos = res.body.data
+
+ expect(videos.length).to.equal(2)
+
+ servers[0].video = videos.find(function (video) { return video.name === 'my super name for pod 1' })
+ servers[1].video = videos.find(function (video) { return video.name === 'my super name for pod 2' })
+
+ next()
+ })
+ }
+ ], done)
+ })
+
+ it('Should not have video abuses', function (done) {
+ videoAbusesUtils.getVideoAbusesList(servers[0].url, servers[0].accessToken, function (err, res) {
+ if (err) throw err
+
+ expect(res.body.total).to.equal(0)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data.length).to.equal(0)
+
+ done()
+ })
+ })
+
+ it('Should report abuse on a local video', function (done) {
+ this.timeout(15000)
+
+ const reason = 'my super bad reason'
+ videoAbusesUtils.reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[0].video.id, reason, function (err) {
+ if (err) throw err
+
+ // We wait requests propagation, even if the pod 1 is not supposed to make a request to pod 2
+ setTimeout(done, 11000)
+ })
+ })
+
+ it('Should have 1 video abuses on pod 1 and 0 on pod 2', function (done) {
+ videoAbusesUtils.getVideoAbusesList(servers[0].url, servers[0].accessToken, function (err, res) {
+ if (err) throw err
+
+ expect(res.body.total).to.equal(1)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data.length).to.equal(1)
+
+ const abuse = res.body.data[0]
+ expect(abuse.reason).to.equal('my super bad reason')
+ expect(abuse.reporterUsername).to.equal('root')
+ expect(abuse.reporterPodHost).to.equal('localhost:9001')
+ expect(abuse.videoId).to.equal(servers[0].video.id)
+
+ videoAbusesUtils.getVideoAbusesList(servers[1].url, servers[1].accessToken, function (err, res) {
+ if (err) throw err
+
+ expect(res.body.total).to.equal(0)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data.length).to.equal(0)
+
+ done()
+ })
+ })
+ })
+
+ it('Should report abuse on a remote video', function (done) {
+ this.timeout(15000)
+
+ const reason = 'my super bad reason 2'
+ videoAbusesUtils.reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[1].video.id, reason, function (err) {
+ if (err) throw err
+
+ // We wait requests propagation
+ setTimeout(done, 11000)
+ })
+ })
+
+ it('Should have 2 video abuse on pod 1 and 1 on pod 2', function (done) {
+ videoAbusesUtils.getVideoAbusesList(servers[0].url, servers[0].accessToken, function (err, res) {
+ if (err) throw err
+
+ expect(res.body.total).to.equal(2)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data.length).to.equal(2)
+
+ let abuse = res.body.data[0]
+ expect(abuse.reason).to.equal('my super bad reason')
+ expect(abuse.reporterUsername).to.equal('root')
+ expect(abuse.reporterPodHost).to.equal('localhost:9001')
+ expect(abuse.videoId).to.equal(servers[0].video.id)
+
+ abuse = res.body.data[1]
+ expect(abuse.reason).to.equal('my super bad reason 2')
+ expect(abuse.reporterUsername).to.equal('root')
+ expect(abuse.reporterPodHost).to.equal('localhost:9001')
+ expect(abuse.videoId).to.equal(servers[1].video.id)
+
+ videoAbusesUtils.getVideoAbusesList(servers[1].url, servers[1].accessToken, function (err, res) {
+ if (err) throw err
+
+ expect(res.body.total).to.equal(1)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data.length).to.equal(1)
+
+ let abuse = res.body.data[0]
+ expect(abuse.reason).to.equal('my super bad reason 2')
+ expect(abuse.reporterUsername).to.equal('root')
+ expect(abuse.reporterPodHost).to.equal('localhost:9001')
+
+ done()
+ })
+ })
+ })
+
+ after(function (done) {
+ servers.forEach(function (server) {
+ process.kill(-server.app.pid)
+ })
+
+ // Keep the logs if the test failed
+ if (this.ok) {
+ serversUtils.flushTests(done)
+ } else {
+ done()
+ }
+ })
+})
--- /dev/null
+'use strict'
+
+const request = require('supertest')
+
+const videosUtils = {
+ getVideoAbusesList,
+ getVideoAbusesListPagination,
+ getVideoAbusesListSort,
+ reportVideoAbuse
+}
+
+// ---------------------- Export functions --------------------
+
+function reportVideoAbuse (url, token, videoId, reason, specialStatus, end) {
+ if (!end) {
+ end = specialStatus
+ specialStatus = 204
+ }
+
+ const path = '/api/v1/videos/' + videoId + '/abuse'
+
+ request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .send({ reason })
+ .expect(specialStatus)
+ .end(end)
+}
+
+function getVideoAbusesList (url, token, end) {
+ const path = '/api/v1/videos/abuse'
+
+ request(url)
+ .get(path)
+ .query({ sort: 'createdAt' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(200)
+ .expect('Content-Type', /json/)
+ .end(end)
+}
+
+function getVideoAbusesListPagination (url, token, start, count, end) {
+ const path = '/api/v1/videos/abuse'
+
+ request(url)
+ .get(path)
+ .query({ start: start })
+ .query({ count: count })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(200)
+ .expect('Content-Type', /json/)
+ .end(end)
+}
+
+function getVideoAbusesListSort (url, token, sort, end) {
+ const path = '/api/v1/videos/abuse'
+
+ request(url)
+ .get(path)
+ .query({ sort: sort })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(200)
+ .expect('Content-Type', /json/)
+ .end(end)
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = videosUtils