aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--client/src/app/+admin/friends/friend-list/friend-list.component.html13
-rw-r--r--client/src/app/+admin/friends/friend-list/friend-list.component.ts19
-rw-r--r--client/src/app/+admin/friends/shared/friend.service.ts15
-rw-r--r--server/controllers/api/pods.ts53
-rw-r--r--server/controllers/api/remote/pods.ts45
-rw-r--r--server/initializers/constants.ts1
-rw-r--r--server/lib/friends.ts6
-rw-r--r--server/middlewares/sort.ts7
-rw-r--r--server/middlewares/validators/pods.ts29
-rw-r--r--server/middlewares/validators/remote/index.ts1
-rw-r--r--server/middlewares/validators/remote/pods.ts38
-rw-r--r--server/middlewares/validators/sort.ts3
-rw-r--r--server/middlewares/validators/utils.ts4
-rw-r--r--server/models/pod/pod-interface.ts4
-rw-r--r--server/models/pod/pod.ts19
-rw-r--r--server/tests/api/check-params/pods.ts32
-rw-r--r--server/tests/api/friends-basic.ts19
-rw-r--r--server/tests/utils/pods.ts16
19 files changed, 232 insertions, 94 deletions
diff --git a/README.md b/README.md
index c908d7bcb..8248e3a7c 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ Decentralized video streaming platform using P2P (BitTorrent) directly in the we
59Want to see in action? 59Want to see in action?
60 60
61 * [Demo server](http://peertube.cpy.re) 61 * [Demo server](http://peertube.cpy.re)
62 * [Video](https://vimeo.com/164881662 "Yes Vimeo, please don't judge me") to see how the "decentralization feature" looks like 62 * [Video](https://peertube.cpy.re/videos/watch/f78a97f8-a142-4ce1-a5bd-154bf9386504) to see how the "decentralization feature" looks like
63 * Experimental demo servers that share videos (they are in the same network): [peertube2](http://peertube2.cpy.re), [peertube3](http://peertube3.cpy.re). Since I do experiments with them, sometimes they might not work correctly. 63 * Experimental demo servers that share videos (they are in the same network): [peertube2](http://peertube2.cpy.re), [peertube3](http://peertube3.cpy.re). Since I do experiments with them, sometimes they might not work correctly.
64 64
65## Why 65## Why
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.html b/client/src/app/+admin/friends/friend-list/friend-list.component.html
index 7887bc5e3..7e92ced54 100644
--- a/client/src/app/+admin/friends/friend-list/friend-list.component.html
+++ b/client/src/app/+admin/friends/friend-list/friend-list.component.html
@@ -2,12 +2,15 @@
2 <div class="content-padding"> 2 <div class="content-padding">
3 <h3>Friends list</h3> 3 <h3>Friends list</h3>
4 4
5 <p-dataTable [value]="friends"> 5 <p-dataTable
6 <p-column field="id" header="ID"></p-column> 6 [value]="friends" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 <p-column field="host" header="Host"></p-column> 7 sortField="id" (onLazyLoad)="loadLazy($event)"
8 >
9 <p-column field="id" header="ID" [sortable]="true"></p-column>
10 <p-column field="host" header="Host" [sortable]="true"></p-column>
8 <p-column field="email" header="Email"></p-column> 11 <p-column field="email" header="Email"></p-column>
9 <p-column field="score" header="Score"></p-column> 12 <p-column field="score" header="Score" [sortable]="true"></p-column>
10 <p-column field="createdAt" header="Created date"></p-column> 13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
11 <p-column header="Delete" styleClass="action-cell"> 14 <p-column header="Delete" styleClass="action-cell">
12 <ng-template pTemplate="body" let-pod="rowData"> 15 <ng-template pTemplate="body" let-pod="rowData">
13 <span (click)="removeFriend(pod)" class="glyphicon glyphicon-remove glyphicon-black" title="Remove this pod"></span> 16 <span (click)="removeFriend(pod)" class="glyphicon glyphicon-remove glyphicon-black" title="Remove this pod"></span>
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.ts b/client/src/app/+admin/friends/friend-list/friend-list.component.ts
index 4af39c47e..5a1ecd280 100644
--- a/client/src/app/+admin/friends/friend-list/friend-list.component.ts
+++ b/client/src/app/+admin/friends/friend-list/friend-list.component.ts
@@ -1,24 +1,32 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2 2
3import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/primeng'
4 5
5import { ConfirmService } from '../../../core' 6import { ConfirmService } from '../../../core'
6import { FriendService } from '../shared' 7import { RestTable, RestPagination } from '../../../shared'
7import { Pod } from '../../../../../../shared' 8import { Pod } from '../../../../../../shared'
9import { FriendService } from '../shared'
8 10
9@Component({ 11@Component({
10 selector: 'my-friend-list', 12 selector: 'my-friend-list',
11 templateUrl: './friend-list.component.html', 13 templateUrl: './friend-list.component.html',
12 styleUrls: ['./friend-list.component.scss'] 14 styleUrls: ['./friend-list.component.scss']
13}) 15})
14export class FriendListComponent implements OnInit { 16export class FriendListComponent extends RestTable implements OnInit {
15 friends: Pod[] = [] 17 friends: Pod[] = []
18 totalRecords = 0
19 rowsPerPage = 10
20 sort: SortMeta = { field: 'id', order: 1 }
21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
16 22
17 constructor ( 23 constructor (
18 private notificationsService: NotificationsService, 24 private notificationsService: NotificationsService,
19 private confirmService: ConfirmService, 25 private confirmService: ConfirmService,
20 private friendService: FriendService 26 private friendService: FriendService
21 ) {} 27 ) {
28 super()
29 }
22 30
23 ngOnInit () { 31 ngOnInit () {
24 this.loadData() 32 this.loadData()
@@ -65,11 +73,12 @@ export class FriendListComponent implements OnInit {
65 ) 73 )
66 } 74 }
67 75
68 private loadData () { 76 protected loadData () {
69 this.friendService.getFriends() 77 this.friendService.getFriends(this.pagination, this.sort)
70 .subscribe( 78 .subscribe(
71 resultList => { 79 resultList => {
72 this.friends = resultList.data 80 this.friends = resultList.data
81 this.totalRecords = resultList.total
73 }, 82 },
74 83
75 err => this.notificationsService.error('Error', err.message) 84 err => this.notificationsService.error('Error', err.message)
diff --git a/client/src/app/+admin/friends/shared/friend.service.ts b/client/src/app/+admin/friends/shared/friend.service.ts
index 274373e3b..a32cdcc88 100644
--- a/client/src/app/+admin/friends/shared/friend.service.ts
+++ b/client/src/app/+admin/friends/shared/friend.service.ts
@@ -1,9 +1,12 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Observable } from 'rxjs/Observable'
3import 'rxjs/add/operator/catch' 4import 'rxjs/add/operator/catch'
4import 'rxjs/add/operator/map' 5import 'rxjs/add/operator/map'
5 6
6import { RestExtractor } from '../../../shared' 7import { SortMeta } from 'primeng/primeng'
8
9import { RestExtractor, RestPagination, RestService } from '../../../shared'
7import { Pod, ResultList } from '../../../../../../shared' 10import { Pod, ResultList } from '../../../../../../shared'
8 11
9@Injectable() 12@Injectable()
@@ -12,11 +15,15 @@ export class FriendService {
12 15
13 constructor ( 16 constructor (
14 private authHttp: HttpClient, 17 private authHttp: HttpClient,
18 private restService: RestService,
15 private restExtractor: RestExtractor 19 private restExtractor: RestExtractor
16 ) {} 20 ) {}
17 21
18 getFriends () { 22 getFriends (pagination: RestPagination, sort: SortMeta): Observable<ResultList<Pod>> {
19 return this.authHttp.get<ResultList<Pod>>(FriendService.BASE_FRIEND_URL) 23 let params = new HttpParams()
24 params = this.restService.addRestGetParams(params, pagination, sort)
25
26 return this.authHttp.get<ResultList<Pod>>(FriendService.BASE_FRIEND_URL, { params })
20 .map(res => this.restExtractor.convertResultListDateToHuman(res)) 27 .map(res => this.restExtractor.convertResultListDateToHuman(res))
21 .catch(res => this.restExtractor.handleError(res)) 28 .catch(res => this.restExtractor.handleError(res))
22 } 29 }
diff --git a/server/controllers/api/pods.ts b/server/controllers/api/pods.ts
index e1e8ff6ca..ec94efc35 100644
--- a/server/controllers/api/pods.ts
+++ b/server/controllers/api/pods.ts
@@ -1,39 +1,33 @@
1import * as express from 'express' 1import * as express from 'express'
2 2
3import { database as db } from '../../initializers/database' 3import { database as db } from '../../initializers/database'
4import { CONFIG } from '../../initializers' 4import { logger, getFormattedObjects } from '../../helpers'
5import { 5import {
6 logger,
7 getMyPublicCert,
8 getFormattedObjects
9} from '../../helpers'
10import {
11 sendOwnedVideosToPod,
12 makeFriends, 6 makeFriends,
13 quitFriends, 7 quitFriends,
14 removeFriend 8 removeFriend
15} from '../../lib' 9} from '../../lib'
16import { 10import {
17 podsAddValidator,
18 authenticate, 11 authenticate,
19 ensureIsAdmin, 12 ensureIsAdmin,
20 makeFriendsValidator, 13 makeFriendsValidator,
21 setBodyHostPort,
22 setBodyHostsPort, 14 setBodyHostsPort,
23 podRemoveValidator 15 podRemoveValidator,
16 paginationValidator,
17 setPagination,
18 setPodsSort,
19 podsSortValidator
24} from '../../middlewares' 20} from '../../middlewares'
25import { 21import { PodInstance } from '../../models'
26 PodInstance
27} from '../../models'
28import { Pod as FormattedPod } from '../../../shared'
29 22
30const podsRouter = express.Router() 23const podsRouter = express.Router()
31 24
32podsRouter.get('/', listPods) 25podsRouter.get('/',
33podsRouter.post('/', 26 paginationValidator,
34 setBodyHostPort, // We need to modify the host before running the validator! 27 podsSortValidator,
35 podsAddValidator, 28 setPodsSort,
36 addPods 29 setPagination,
30 listPods
37) 31)
38podsRouter.post('/make-friends', 32podsRouter.post('/make-friends',
39 authenticate, 33 authenticate,
@@ -62,26 +56,9 @@ export {
62 56
63// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
64 58
65function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
66 const informations = req.body
67
68 const pod = db.Pod.build(informations)
69 pod.save()
70 .then(podCreated => {
71 return sendOwnedVideosToPod(podCreated.id)
72 })
73 .then(() => {
74 return getMyPublicCert()
75 })
76 .then(cert => {
77 return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
78 })
79 .catch(err => next(err))
80}
81
82function listPods (req: express.Request, res: express.Response, next: express.NextFunction) { 59function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
83 db.Pod.list() 60 db.Pod.listForApi(req.query.start, req.query.count, req.query.sort)
84 .then(podsList => res.json(getFormattedObjects<FormattedPod, PodInstance>(podsList, podsList.length))) 61 .then(resultList => res.json(getFormattedObjects(resultList.data, resultList.total)))
85 .catch(err => next(err)) 62 .catch(err => next(err))
86} 63}
87 64
diff --git a/server/controllers/api/remote/pods.ts b/server/controllers/api/remote/pods.ts
index 69bbd4378..6f7b5f651 100644
--- a/server/controllers/api/remote/pods.ts
+++ b/server/controllers/api/remote/pods.ts
@@ -1,18 +1,34 @@
1import * as express from 'express' 1import * as express from 'express'
2 2
3import { database as db } from '../../../initializers/database' 3import { database as db } from '../../../initializers/database'
4import { checkSignature, signatureValidator } from '../../../middlewares' 4import {
5import { PodSignature } from '../../../../shared' 5 checkSignature,
6 signatureValidator,
7 setBodyHostPort,
8 remotePodsAddValidator
9} from '../../../middlewares'
10import { sendOwnedVideosToPod } from '../../../lib'
11import { getMyPublicCert, getFormattedObjects } from '../../../helpers'
12import { CONFIG } from '../../../initializers'
13import { PodInstance } from '../../../models'
14import { PodSignature, Pod as FormattedPod } from '../../../../shared'
6 15
7const remotePodsRouter = express.Router() 16const remotePodsRouter = express.Router()
8 17
9// Post because this is a secured request
10remotePodsRouter.post('/remove', 18remotePodsRouter.post('/remove',
11 signatureValidator, 19 signatureValidator,
12 checkSignature, 20 checkSignature,
13 removePods 21 removePods
14) 22)
15 23
24remotePodsRouter.post('/list', remotePodsList)
25
26remotePodsRouter.post('/add',
27 setBodyHostPort, // We need to modify the host before running the validator!
28 remotePodsAddValidator,
29 addPods
30)
31
16// --------------------------------------------------------------------------- 32// ---------------------------------------------------------------------------
17 33
18export { 34export {
@@ -21,6 +37,29 @@ export {
21 37
22// --------------------------------------------------------------------------- 38// ---------------------------------------------------------------------------
23 39
40function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
41 const information = req.body
42
43 const pod = db.Pod.build(information)
44 pod.save()
45 .then(podCreated => {
46 return sendOwnedVideosToPod(podCreated.id)
47 })
48 .then(() => {
49 return getMyPublicCert()
50 })
51 .then(cert => {
52 return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
53 })
54 .catch(err => next(err))
55}
56
57function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) {
58 db.Pod.list()
59 .then(podsList => res.json(getFormattedObjects<FormattedPod, PodInstance>(podsList, podsList.length)))
60 .catch(err => next(err))
61}
62
24function removePods (req: express.Request, res: express.Response, next: express.NextFunction) { 63function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
25 const signature: PodSignature = req.body.signature 64 const signature: PodSignature = req.body.signature
26 const host = signature.host 65 const host = signature.host
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 491fb78f9..132164746 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -32,6 +32,7 @@ const SEARCHABLE_COLUMNS = {
32 32
33// Sortable columns per schema 33// Sortable columns per schema
34const SORTABLE_COLUMNS = { 34const SORTABLE_COLUMNS = {
35 PODS: [ 'id', 'host', 'score', 'createdAt' ],
35 USERS: [ 'id', 'username', 'createdAt' ], 36 USERS: [ 'id', 'username', 'createdAt' ],
36 VIDEO_ABUSES: [ 'id', 'createdAt' ], 37 VIDEO_ABUSES: [ 'id', 'createdAt' ],
37 VIDEOS: [ 'name', 'duration', 'createdAt', 'views', 'likes' ], 38 VIDEOS: [ 'name', 'duration', 'createdAt', 'views', 'likes' ],
diff --git a/server/lib/friends.ts b/server/lib/friends.ts
index c0dd24c53..4cc4a82bf 100644
--- a/server/lib/friends.ts
+++ b/server/lib/friends.ts
@@ -334,9 +334,9 @@ function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: num
334 334
335function getForeignPodsList (host: string) { 335function getForeignPodsList (host: string) {
336 return new Promise< ResultList<FormattedPod> >((res, rej) => { 336 return new Promise< ResultList<FormattedPod> >((res, rej) => {
337 const path = '/api/' + API_VERSION + '/pods' 337 const path = '/api/' + API_VERSION + '/remote/pods/list'
338 338
339 request.get(REMOTE_SCHEME.HTTP + '://' + host + path, (err, response, body) => { 339 request.post(REMOTE_SCHEME.HTTP + '://' + host + path, (err, response, body) => {
340 if (err) return rej(err) 340 if (err) return rej(err)
341 341
342 try { 342 try {
@@ -357,7 +357,7 @@ function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
357 357
358 return Promise.map(podsList, pod => { 358 return Promise.map(podsList, pod => {
359 const params = { 359 const params = {
360 url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/pods/', 360 url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/remote/pods/add',
361 method: 'POST' as 'POST', 361 method: 'POST' as 'POST',
362 json: { 362 json: {
363 host: CONFIG.WEBSERVER.HOST, 363 host: CONFIG.WEBSERVER.HOST,
diff --git a/server/middlewares/sort.ts b/server/middlewares/sort.ts
index 687ce097b..2c70ff5f0 100644
--- a/server/middlewares/sort.ts
+++ b/server/middlewares/sort.ts
@@ -4,6 +4,12 @@ import * as express from 'express'
4import { SortType } from '../helpers' 4import { SortType } from '../helpers'
5import { database } from '../initializers' 5import { database } from '../initializers'
6 6
7function setPodsSort (req: express.Request, res: express.Response, next: express.NextFunction) {
8 if (!req.query.sort) req.query.sort = '-createdAt'
9
10 return next()
11}
12
7function setUsersSort (req: express.Request, res: express.Response, next: express.NextFunction) { 13function setUsersSort (req: express.Request, res: express.Response, next: express.NextFunction) {
8 if (!req.query.sort) req.query.sort = '-createdAt' 14 if (!req.query.sort) req.query.sort = '-createdAt'
9 15
@@ -46,6 +52,7 @@ function setBlacklistSort (req: express.Request, res: express.Response, next: ex
46// --------------------------------------------------------------------------- 52// ---------------------------------------------------------------------------
47 53
48export { 54export {
55 setPodsSort,
49 setUsersSort, 56 setUsersSort,
50 setVideoAbusesSort, 57 setVideoAbusesSort,
51 setVideosSort, 58 setVideosSort,
diff --git a/server/middlewares/validators/pods.ts b/server/middlewares/validators/pods.ts
index ab7702e78..575c36526 100644
--- a/server/middlewares/validators/pods.ts
+++ b/server/middlewares/validators/pods.ts
@@ -3,7 +3,7 @@ import * as express from 'express'
3 3
4import { database as db } from '../../initializers/database' 4import { database as db } from '../../initializers/database'
5import { checkErrors } from './utils' 5import { checkErrors } from './utils'
6import { logger, isEachUniqueHostValid, isHostValid } from '../../helpers' 6import { logger, isEachUniqueHostValid } from '../../helpers'
7import { CONFIG } from '../../initializers' 7import { CONFIG } from '../../initializers'
8import { hasFriends } from '../../lib' 8import { hasFriends } from '../../lib'
9import { isTestInstance } from '../../helpers' 9import { isTestInstance } from '../../helpers'
@@ -41,32 +41,6 @@ const makeFriendsValidator = [
41 } 41 }
42] 42]
43 43
44const podsAddValidator = [
45 body('host').custom(isHostValid).withMessage('Should have a host'),
46 body('email').isEmail().withMessage('Should have an email'),
47 body('publicKey').not().isEmpty().withMessage('Should have a public key'),
48
49 (req: express.Request, res: express.Response, next: express.NextFunction) => {
50 logger.debug('Checking podsAdd parameters', { parameters: req.body })
51
52 checkErrors(req, res, () => {
53 db.Pod.loadByHost(req.body.host)
54 .then(pod => {
55 // Pod with this host already exists
56 if (pod) {
57 return res.sendStatus(409)
58 }
59
60 return next()
61 })
62 .catch(err => {
63 logger.error('Cannot load pod by host.', err)
64 res.sendStatus(500)
65 })
66 })
67 }
68]
69
70const podRemoveValidator = [ 44const podRemoveValidator = [
71 param('id').isNumeric().not().isEmpty().withMessage('Should have a valid id'), 45 param('id').isNumeric().not().isEmpty().withMessage('Should have a valid id'),
72 46
@@ -96,6 +70,5 @@ const podRemoveValidator = [
96 70
97export { 71export {
98 makeFriendsValidator, 72 makeFriendsValidator,
99 podsAddValidator,
100 podRemoveValidator 73 podRemoveValidator
101} 74}
diff --git a/server/middlewares/validators/remote/index.ts b/server/middlewares/validators/remote/index.ts
index d0d7740b1..f1f26043e 100644
--- a/server/middlewares/validators/remote/index.ts
+++ b/server/middlewares/validators/remote/index.ts
@@ -1,2 +1,3 @@
1export * from './pods'
1export * from './signature' 2export * from './signature'
2export * from './videos' 3export * from './videos'
diff --git a/server/middlewares/validators/remote/pods.ts b/server/middlewares/validators/remote/pods.ts
new file mode 100644
index 000000000..f917b61ee
--- /dev/null
+++ b/server/middlewares/validators/remote/pods.ts
@@ -0,0 +1,38 @@
1import { body } from 'express-validator/check'
2import * as express from 'express'
3
4import { database as db } from '../../../initializers'
5import { isHostValid, logger } from '../../../helpers'
6import { checkErrors } from '../utils'
7
8const remotePodsAddValidator = [
9 body('host').custom(isHostValid).withMessage('Should have a host'),
10 body('email').isEmail().withMessage('Should have an email'),
11 body('publicKey').not().isEmpty().withMessage('Should have a public key'),
12
13 (req: express.Request, res: express.Response, next: express.NextFunction) => {
14 logger.debug('Checking podsAdd parameters', { parameters: req.body })
15
16 checkErrors(req, res, () => {
17 db.Pod.loadByHost(req.body.host)
18 .then(pod => {
19 // Pod with this host already exists
20 if (pod) {
21 return res.sendStatus(409)
22 }
23
24 return next()
25 })
26 .catch(err => {
27 logger.error('Cannot load pod by host.', err)
28 res.sendStatus(500)
29 })
30 })
31 }
32]
33
34// ---------------------------------------------------------------------------
35
36export {
37 remotePodsAddValidator
38}
diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts
index a6f5ccb6b..227f309ad 100644
--- a/server/middlewares/validators/sort.ts
+++ b/server/middlewares/validators/sort.ts
@@ -6,11 +6,13 @@ import { logger } from '../../helpers'
6import { SORTABLE_COLUMNS } from '../../initializers' 6import { SORTABLE_COLUMNS } from '../../initializers'
7 7
8// Initialize constants here for better performances 8// Initialize constants here for better performances
9const SORTABLE_PODS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.PODS)
9const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS) 10const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS)
10const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES) 11const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES)
11const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS) 12const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS)
12const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS) 13const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS)
13 14
15const podsSortValidator = checkSort(SORTABLE_PODS_COLUMNS)
14const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS) 16const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
15const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS) 17const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS)
16const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS) 18const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS)
@@ -19,6 +21,7 @@ const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS)
19// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
20 22
21export { 23export {
24 podsSortValidator,
22 usersSortValidator, 25 usersSortValidator,
23 videoAbusesSortValidator, 26 videoAbusesSortValidator,
24 videosSortValidator, 27 videosSortValidator,
diff --git a/server/middlewares/validators/utils.ts b/server/middlewares/validators/utils.ts
index 8845f8399..ea107bbe8 100644
--- a/server/middlewares/validators/utils.ts
+++ b/server/middlewares/validators/utils.ts
@@ -3,12 +3,12 @@ import * as express from 'express'
3 3
4import { logger } from '../../helpers' 4import { logger } from '../../helpers'
5 5
6function checkErrors (req: express.Request, res: express.Response, next: express.NextFunction, statusCode = 400) { 6function checkErrors (req: express.Request, res: express.Response, next: express.NextFunction) {
7 const errors = validationResult(req) 7 const errors = validationResult(req)
8 8
9 if (!errors.isEmpty()) { 9 if (!errors.isEmpty()) {
10 logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) 10 logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() })
11 return res.status(statusCode).json({ errors: errors.mapped() }) 11 return res.status(400).json({ errors: errors.mapped() })
12 } 12 }
13 13
14 return next() 14 return next()
diff --git a/server/models/pod/pod-interface.ts b/server/models/pod/pod-interface.ts
index fc763acac..7e095d424 100644
--- a/server/models/pod/pod-interface.ts
+++ b/server/models/pod/pod-interface.ts
@@ -3,6 +3,7 @@ import * as Promise from 'bluebird'
3 3
4// Don't use barrel, import just what we need 4// Don't use barrel, import just what we need
5import { Pod as FormattedPod } from '../../../shared/models/pods/pod.model' 5import { Pod as FormattedPod } from '../../../shared/models/pods/pod.model'
6import { ResultList } from '../../../shared/models/result-list.model'
6 7
7export namespace PodMethods { 8export namespace PodMethods {
8 export type ToFormattedJSON = (this: PodInstance) => FormattedPod 9 export type ToFormattedJSON = (this: PodInstance) => FormattedPod
@@ -13,6 +14,8 @@ export namespace PodMethods {
13 14
14 export type List = () => Promise<PodInstance[]> 15 export type List = () => Promise<PodInstance[]>
15 16
17 export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<PodInstance> >
18
16 export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]> 19 export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
17 20
18 export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]> 21 export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
@@ -32,6 +35,7 @@ export interface PodClass {
32 countAll: PodMethods.CountAll 35 countAll: PodMethods.CountAll
33 incrementScores: PodMethods.IncrementScores 36 incrementScores: PodMethods.IncrementScores
34 list: PodMethods.List 37 list: PodMethods.List
38 listForApi: PodMethods.ListForApi
35 listAllIds: PodMethods.ListAllIds 39 listAllIds: PodMethods.ListAllIds
36 listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest 40 listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
37 listBadPods: PodMethods.ListBadPods 41 listBadPods: PodMethods.ListBadPods
diff --git a/server/models/pod/pod.ts b/server/models/pod/pod.ts
index 1440ac9b4..e4d7db48a 100644
--- a/server/models/pod/pod.ts
+++ b/server/models/pod/pod.ts
@@ -4,7 +4,7 @@ import * as Sequelize from 'sequelize'
4import { FRIEND_SCORE, PODS_SCORE } from '../../initializers' 4import { FRIEND_SCORE, PODS_SCORE } from '../../initializers'
5import { logger, isHostValid } from '../../helpers' 5import { logger, isHostValid } from '../../helpers'
6 6
7import { addMethodsToModel } from '../utils' 7import { addMethodsToModel, getSort } from '../utils'
8import { 8import {
9 PodInstance, 9 PodInstance,
10 PodAttributes, 10 PodAttributes,
@@ -17,6 +17,7 @@ let toFormattedJSON: PodMethods.ToFormattedJSON
17let countAll: PodMethods.CountAll 17let countAll: PodMethods.CountAll
18let incrementScores: PodMethods.IncrementScores 18let incrementScores: PodMethods.IncrementScores
19let list: PodMethods.List 19let list: PodMethods.List
20let listForApi: PodMethods.ListForApi
20let listAllIds: PodMethods.ListAllIds 21let listAllIds: PodMethods.ListAllIds
21let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest 22let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
22let listBadPods: PodMethods.ListBadPods 23let listBadPods: PodMethods.ListBadPods
@@ -78,6 +79,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
78 countAll, 79 countAll,
79 incrementScores, 80 incrementScores,
80 list, 81 list,
82 listForApi,
81 listAllIds, 83 listAllIds,
82 listRandomPodIdsWithRequest, 84 listRandomPodIdsWithRequest,
83 listBadPods, 85 listBadPods,
@@ -142,6 +144,21 @@ list = function () {
142 return Pod.findAll() 144 return Pod.findAll()
143} 145}
144 146
147listForApi = function (start: number, count: number, sort: string) {
148 const query = {
149 offset: start,
150 limit: count,
151 order: [ getSort(sort) ]
152 }
153
154 return Pod.findAndCountAll(query).then(({ rows, count }) => {
155 return {
156 data: rows,
157 total: count
158 }
159 })
160}
161
145listAllIds = function (transaction: Sequelize.Transaction) { 162listAllIds = function (transaction: Sequelize.Transaction) {
146 const query = { 163 const query = {
147 attributes: [ 'id' ], 164 attributes: [ 'id' ],
diff --git a/server/tests/api/check-params/pods.ts b/server/tests/api/check-params/pods.ts
index a897e4dcd..9f9c2e4f0 100644
--- a/server/tests/api/check-params/pods.ts
+++ b/server/tests/api/check-params/pods.ts
@@ -15,7 +15,6 @@ import {
15} from '../../utils' 15} from '../../utils'
16 16
17describe('Test pods API validators', function () { 17describe('Test pods API validators', function () {
18 const path = '/api/v1/pods/'
19 let server: ServerInfo 18 let server: ServerInfo
20 19
21 // --------------------------------------------------------------- 20 // ---------------------------------------------------------------
@@ -30,6 +29,7 @@ describe('Test pods API validators', function () {
30 }) 29 })
31 30
32 describe('When managing friends', function () { 31 describe('When managing friends', function () {
32 const path = '/api/v1/pods/'
33 let userAccessToken = null 33 let userAccessToken = null
34 34
35 before(async function () { 35 before(async function () {
@@ -110,6 +110,32 @@ describe('Test pods API validators', function () {
110 }) 110 })
111 }) 111 })
112 112
113 describe('When listing friends', function () {
114 it('Should fail with a bad start pagination', async function () {
115 await request(server.url)
116 .get(path)
117 .query({ start: 'hello' })
118 .set('Accept', 'application/json')
119 .expect(400)
120 })
121
122 it('Should fail with a bad count pagination', async function () {
123 await request(server.url)
124 .get(path)
125 .query({ count: 'hello' })
126 .set('Accept', 'application/json')
127 .expect(400)
128 })
129
130 it('Should fail with an incorrect sort', async function () {
131 await request(server.url)
132 .get(path)
133 .query({ sort: 'hello' })
134 .set('Accept', 'application/json')
135 .expect(400)
136 })
137 })
138
113 describe('When quitting friends', function () { 139 describe('When quitting friends', function () {
114 it('Should fail with an invalid token', async function () { 140 it('Should fail with an invalid token', async function () {
115 await request(server.url) 141 await request(server.url)
@@ -175,7 +201,9 @@ describe('Test pods API validators', function () {
175 }) 201 })
176 }) 202 })
177 203
178 describe('When adding a pod', function () { 204 describe('When adding a pod from remote', function () {
205 const path = '/api/v1/remote/pods/add'
206
179 it('Should fail with nothing', async function () { 207 it('Should fail with nothing', async function () {
180 const fields = {} 208 const fields = {}
181 await makePostBodyRequest({ url: server.url, path, fields }) 209 await makePostBodyRequest({ url: server.url, path, fields })
diff --git a/server/tests/api/friends-basic.ts b/server/tests/api/friends-basic.ts
index 13edf6273..efca4fda2 100644
--- a/server/tests/api/friends-basic.ts
+++ b/server/tests/api/friends-basic.ts
@@ -15,7 +15,8 @@ import {
15 makeFriends, 15 makeFriends,
16 getFriendsList, 16 getFriendsList,
17 dateIsValid, 17 dateIsValid,
18 quitOneFriend 18 quitOneFriend,
19 getPodsListPaginationAndSort
19} from '../utils' 20} from '../utils'
20 21
21describe('Test basic friends', function () { 22describe('Test basic friends', function () {
@@ -120,6 +121,22 @@ describe('Test basic friends', function () {
120 await makeFriends(server.url, server.accessToken, 409) 121 await makeFriends(server.url, server.accessToken, 409)
121 }) 122 })
122 123
124 it('Should list friends correctly', async function () {
125 const start = 1
126 const count = 1
127 const sort = '-host'
128
129 const res = await getPodsListPaginationAndSort(servers[0].url, start, count, sort)
130 expect(res.body.total).to.equal(2)
131 expect(res.body.data).to.have.lengthOf(1)
132
133 const pod = res.body.data[0]
134 expect(pod.host).to.equal('localhost:9002')
135 expect(pod.email).to.equal('admin2@example.com')
136 expect(pod.score).to.equal(20)
137 expect(dateIsValid(pod.createdAt)).to.be.true
138 })
139
123 it('Should quit friends of pod 2', async function () { 140 it('Should quit friends of pod 2', async function () {
124 this.timeout(10000) 141 this.timeout(10000)
125 142
diff --git a/server/tests/utils/pods.ts b/server/tests/utils/pods.ts
index a86dd20d9..52e807e70 100644
--- a/server/tests/utils/pods.ts
+++ b/server/tests/utils/pods.ts
@@ -12,6 +12,19 @@ function getFriendsList (url: string) {
12 .expect('Content-Type', /json/) 12 .expect('Content-Type', /json/)
13} 13}
14 14
15function getPodsListPaginationAndSort (url: string, start: number, count: number, sort: string) {
16 const path = '/api/v1/pods/'
17
18 return request(url)
19 .get(path)
20 .query({ start })
21 .query({ count })
22 .query({ sort })
23 .set('Accept', 'application/json')
24 .expect(200)
25 .expect('Content-Type', /json/)
26}
27
15async function makeFriends (url: string, accessToken: string, expectedStatus = 204) { 28async function makeFriends (url: string, accessToken: string, expectedStatus = 204) {
16 // Which pod makes friends with which pod 29 // Which pod makes friends with which pod
17 const friendsMatrix = { 30 const friendsMatrix = {
@@ -85,5 +98,6 @@ export {
85 getFriendsList, 98 getFriendsList,
86 makeFriends, 99 makeFriends,
87 quitFriends, 100 quitFriends,
88 quitOneFriend 101 quitOneFriend,
102 getPodsListPaginationAndSort
89} 103}