aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-11-20 11:19:23 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-11-27 19:40:52 +0100
commit7e9334c34db23e5ad1e118151b24c720dd985984 (patch)
tree154f5d1db44ff652b793d8e0b7dc1b00a3ddaec1
parent892211e8493b1f992fce7616cb1e48b7ff87a1dc (diff)
downloadPeerTube-7e9334c34db23e5ad1e118151b24c720dd985984.tar.gz
PeerTube-7e9334c34db23e5ad1e118151b24c720dd985984.tar.zst
PeerTube-7e9334c34db23e5ad1e118151b24c720dd985984.zip
Add ability to unfollow a server
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html4
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html9
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts19
-rw-r--r--client/src/app/+admin/follows/shared/follow.service.ts8
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts8
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/lib/activitypub/process/process-undo.ts2
-rw-r--r--server/middlewares/validators/follows.ts6
-rw-r--r--server/models/account/account-follow-interface.ts11
-rw-r--r--server/models/account/account-follow.ts26
-rw-r--r--shared/models/accounts/follow.model.ts10
-rw-r--r--support/nginx/peertube3
-rw-r--r--support/nginx/peertube-https3
13 files changed, 85 insertions, 26 deletions
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index 24d75d2b3..549aacdf0 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -7,9 +7,9 @@
7 sortField="createdAt" (onLazyLoad)="loadLazy($event)" 7 sortField="createdAt" (onLazyLoad)="loadLazy($event)"
8 > 8 >
9 <p-column field="id" header="ID"></p-column> 9 <p-column field="id" header="ID"></p-column>
10 <p-column field="host" header="Host"></p-column> 10 <p-column field="follower.host" header="Host"></p-column>
11 <p-column field="email" header="Email"></p-column> 11 <p-column field="email" header="Email"></p-column>
12 <p-column field="score" header="Score"></p-column> 12 <p-column field="follower.score" header="Score"></p-column>
13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column> 13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
14 </p-dataTable> 14 </p-dataTable>
15 </div> 15 </div>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index fbcebfaa7..dcc03f4a5 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -7,10 +7,15 @@
7 sortField="createdAt" (onLazyLoad)="loadLazy($event)" 7 sortField="createdAt" (onLazyLoad)="loadLazy($event)"
8 > 8 >
9 <p-column field="id" header="ID"></p-column> 9 <p-column field="id" header="ID"></p-column>
10 <p-column field="host" header="Host"></p-column> 10 <p-column field="following.host" header="Host"></p-column>
11 <p-column field="email" header="Email"></p-column> 11 <p-column field="email" header="Email"></p-column>
12 <p-column field="score" header="Score"></p-column> 12 <p-column field="following.score" header="Score"></p-column>
13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column> 13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
14 <p-column header="Unfollow" styleClass="action-cell">
15 <ng-template pTemplate="body" let-following="rowData">
16 <span (click)="removeFollowing(following)" class="glyphicon glyphicon-remove glyphicon-black" title="Unfollow"></span>
17 </ng-template>
18 </p-column>
14 </p-dataTable> 19 </p-dataTable>
15 </div> 20 </div>
16</div> 21</div>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index a1dff1db3..411b8f640 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
3import { SortMeta } from 'primeng/primeng' 3import { SortMeta } from 'primeng/primeng'
4import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model' 4import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
5import { ConfirmService } from '../../../core/confirm/confirm.service'
5import { RestPagination, RestTable } from '../../../shared' 6import { RestPagination, RestTable } from '../../../shared'
6import { FollowService } from '../shared' 7import { FollowService } from '../shared'
7 8
@@ -18,11 +19,29 @@ export class FollowingListComponent extends RestTable {
18 19
19 constructor ( 20 constructor (
20 private notificationsService: NotificationsService, 21 private notificationsService: NotificationsService,
22 private confirmService: ConfirmService,
21 private followService: FollowService 23 private followService: FollowService
22 ) { 24 ) {
23 super() 25 super()
24 } 26 }
25 27
28 removeFollowing (follow: AccountFollow) {
29 this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow').subscribe(
30 res => {
31 if (res === false) return
32
33 this.followService.unfollow(follow).subscribe(
34 () => {
35 this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`)
36 this.loadData()
37 },
38
39 err => this.notificationsService.error('Error', err.message)
40 )
41 }
42 )
43 }
44
26 protected loadData () { 45 protected loadData () {
27 this.followService.getFollowing(this.pagination, this.sort) 46 this.followService.getFollowing(this.pagination, this.sort)
28 .subscribe( 47 .subscribe(
diff --git a/client/src/app/+admin/follows/shared/follow.service.ts b/client/src/app/+admin/follows/shared/follow.service.ts
index f66ed477d..0bfbe8eb6 100644
--- a/client/src/app/+admin/follows/shared/follow.service.ts
+++ b/client/src/app/+admin/follows/shared/follow.service.ts
@@ -37,7 +37,7 @@ export class FollowService {
37 .catch(res => this.restExtractor.handleError(res)) 37 .catch(res => this.restExtractor.handleError(res))
38 } 38 }
39 39
40 follow (notEmptyHosts: String[]) { 40 follow (notEmptyHosts: string[]) {
41 const body = { 41 const body = {
42 hosts: notEmptyHosts 42 hosts: notEmptyHosts
43 } 43 }
@@ -46,4 +46,10 @@ export class FollowService {
46 .map(this.restExtractor.extractDataBool) 46 .map(this.restExtractor.extractDataBool)
47 .catch(res => this.restExtractor.handleError(res)) 47 .catch(res => this.restExtractor.handleError(res))
48 } 48 }
49
50 unfollow (follow: AccountFollow) {
51 return this.authHttp.delete(FollowService.BASE_APPLICATION_URL + '/following/' + follow.following.id)
52 .map(this.restExtractor.extractDataBool)
53 .catch(res => this.restExtractor.handleError(res))
54 }
49} 55}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 6debf89be..1e8e1af49 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,4 +1,4 @@
1import { Component, OnInit } from '@angular/core' 1import { Component } from '@angular/core'
2import { SortMeta } from 'primeng/components/common/sortmeta' 2import { SortMeta } from 'primeng/components/common/sortmeta'
3 3
4import { NotificationsService } from 'angular2-notifications' 4import { NotificationsService } from 'angular2-notifications'
@@ -12,7 +12,7 @@ import { UserService } from '../shared'
12 templateUrl: './user-list.component.html', 12 templateUrl: './user-list.component.html',
13 styleUrls: [ './user-list.component.scss' ] 13 styleUrls: [ './user-list.component.scss' ]
14}) 14})
15export class UserListComponent extends RestTable implements OnInit { 15export class UserListComponent extends RestTable {
16 users: User[] = [] 16 users: User[] = []
17 totalRecords = 0 17 totalRecords = 0
18 rowsPerPage = 10 18 rowsPerPage = 10
@@ -27,10 +27,6 @@ export class UserListComponent extends RestTable implements OnInit {
27 super() 27 super()
28 } 28 }
29 29
30 ngOnInit () {
31 this.loadData()
32 }
33
34 removeUser (user: User) { 30 removeUser (user: User) {
35 if (user.username === 'root') { 31 if (user.username === 'root') {
36 this.notificationsService.error('Error', 'You cannot delete root.') 32 this.notificationsService.error('Error', 'You cannot delete root.')
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 9c7c31a61..c46043931 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -134,7 +134,7 @@ const CONSTRAINTS_FIELDS = {
134 VIEWS: { min: 0 }, 134 VIEWS: { min: 0 },
135 LIKES: { min: 0 }, 135 LIKES: { min: 0 },
136 DISLIKES: { min: 0 }, 136 DISLIKES: { min: 0 },
137 FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ }, 137 FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 10 /* 10Go */ },
138 URL: { min: 3, max: 2000 } // Length 138 URL: { min: 3, max: 2000 } // Length
139 }, 139 },
140 ACCOUNTS: { 140 ACCOUNTS: {
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index 5d09423e1..610b800fb 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -10,7 +10,7 @@ async function processUndoActivity (activity: ActivityUndo) {
10 const following = await db.Account.loadByUrl(activityToUndo.object) 10 const following = await db.Account.loadByUrl(activityToUndo.object)
11 const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id) 11 const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
12 12
13 if (!accountFollow) throw new Error(`'Unknown account follow (${follower.id} -> ${following.id}.`) 13 if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`)
14 14
15 await accountFollow.destroy() 15 await accountFollow.destroy()
16 16
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index e22349726..dfd6e7f03 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator/check' 2import { body, param } from 'express-validator/check'
3import { isTestInstance } from '../../helpers/core-utils' 3import { isTestInstance } from '../../helpers/core-utils'
4import { isAccountIdValid } from '../../helpers/custom-validators/activitypub/account'
5import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers' 4import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
6import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
7import { CONFIG, database as db } from '../../initializers' 6import { CONFIG, database as db } from '../../initializers'
8import { checkErrors } from './utils' 7import { checkErrors } from './utils'
9import { getServerAccount } from '../../helpers/utils' 8import { getServerAccount } from '../../helpers/utils'
9import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
10 10
11const followValidator = [ 11const followValidator = [
12 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), 12 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
@@ -28,7 +28,7 @@ const followValidator = [
28] 28]
29 29
30const removeFollowingValidator = [ 30const removeFollowingValidator = [
31 body('accountId').custom(isAccountIdValid).withMessage('Should have a valid account id'), 31 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
32 32
33 (req: express.Request, res: express.Response, next: express.NextFunction) => { 33 (req: express.Request, res: express.Response, next: express.NextFunction) => {
34 logger.debug('Checking follow parameters', { parameters: req.body }) 34 logger.debug('Checking follow parameters', { parameters: req.body })
diff --git a/server/models/account/account-follow-interface.ts b/server/models/account/account-follow-interface.ts
index 21fda98ce..6f228c790 100644
--- a/server/models/account/account-follow-interface.ts
+++ b/server/models/account/account-follow-interface.ts
@@ -1,18 +1,19 @@
1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
3import { FollowState } from '../../../shared/models/accounts/follow.model' 2import * as Sequelize from 'sequelize'
3import { AccountFollow, FollowState } from '../../../shared/models/accounts/follow.model'
4import { ResultList } from '../../../shared/models/result-list.model' 4import { ResultList } from '../../../shared/models/result-list.model'
5import { AccountInstance } from './account-interface' 5import { AccountInstance } from './account-interface'
6 6
7export namespace AccountFollowMethods { 7export namespace AccountFollowMethods {
8 export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance> 8 export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance>
9 9
10 export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > 10 export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
11 export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > 11 export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
12 12
13 export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> > 13 export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
14 export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> > 14 export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
15 export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> > 15 export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> >
16 export type ToFormattedJSON = (this: AccountFollowInstance) => AccountFollow
16} 17}
17 18
18export interface AccountFollowClass { 19export interface AccountFollowClass {
@@ -38,6 +39,8 @@ export interface AccountFollowInstance extends AccountFollowClass, AccountFollow
38 39
39 AccountFollower?: AccountInstance 40 AccountFollower?: AccountInstance
40 AccountFollowing?: AccountInstance 41 AccountFollowing?: AccountInstance
42
43 toFormattedJSON: AccountFollowMethods.ToFormattedJSON
41} 44}
42 45
43export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {} 46export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts
index f00c7dcd9..34ba3f8db 100644
--- a/server/models/account/account-follow.ts
+++ b/server/models/account/account-follow.ts
@@ -12,6 +12,7 @@ let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
12let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi 12let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
13let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi 13let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
14let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls 14let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
15let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
15 16
16export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { 17export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
17 AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', 18 AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
@@ -46,7 +47,10 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
46 listAcceptedFollowingUrlsForApi, 47 listAcceptedFollowingUrlsForApi,
47 listAcceptedFollowerSharedInboxUrls 48 listAcceptedFollowerSharedInboxUrls
48 ] 49 ]
49 addMethodsToModel(AccountFollow, classMethods) 50 const instanceMethods = [
51 toFormattedJSON
52 ]
53 addMethodsToModel(AccountFollow, classMethods, instanceMethods)
50 54
51 return AccountFollow 55 return AccountFollow
52} 56}
@@ -73,6 +77,22 @@ function associate (models) {
73 }) 77 })
74} 78}
75 79
80toFormattedJSON = function (this: AccountFollowInstance) {
81 const follower = this.AccountFollower.toFormattedJSON()
82 const following = this.AccountFollowing.toFormattedJSON()
83
84 const json = {
85 id: this.id,
86 follower,
87 following,
88 state: this.state,
89 createdAt: this.createdAt,
90 updatedAt: this.updatedAt
91 }
92
93 return json
94}
95
76loadByAccountAndTarget = function (accountId: number, targetAccountId: number) { 96loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
77 const query = { 97 const query = {
78 where: { 98 where: {
@@ -122,7 +142,7 @@ listFollowingForApi = function (id: number, start: number, count: number, sort:
122 142
123 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { 143 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
124 return { 144 return {
125 data: rows.map(r => r.AccountFollowing), 145 data: rows,
126 total: count 146 total: count
127 } 147 }
128 }) 148 })
@@ -154,7 +174,7 @@ listFollowersForApi = function (id: number, start: number, count: number, sort:
154 174
155 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { 175 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
156 return { 176 return {
157 data: rows.map(r => r.AccountFollower), 177 data: rows,
158 total: count 178 total: count
159 } 179 }
160 }) 180 })
diff --git a/shared/models/accounts/follow.model.ts b/shared/models/accounts/follow.model.ts
index 8094634c1..cdc3da560 100644
--- a/shared/models/accounts/follow.model.ts
+++ b/shared/models/accounts/follow.model.ts
@@ -1,8 +1,12 @@
1import { Account } from './account.model'
2
1export type FollowState = 'pending' | 'accepted' 3export type FollowState = 'pending' | 'accepted'
2 4
3export interface AccountFollow { 5export interface AccountFollow {
4 id: number 6 id: number
5 name: string 7 follower: Account
6 score?: number // Used for followers 8 following: Account
7 host: string 9 state: FollowState
10 createdAt: Date
11 updatedAt: Date
8} 12}
diff --git a/support/nginx/peertube b/support/nginx/peertube
index f7db3eea3..8120738f6 100644
--- a/support/nginx/peertube
+++ b/support/nginx/peertube
@@ -10,6 +10,9 @@ server {
10 10
11 # For the video upload 11 # For the video upload
12 client_max_body_size 2G; 12 client_max_body_size 2G;
13 proxy_connect_timeout 600;
14 proxy_send_timeout 600;
15 proxy_read_timeout 600;
13 } 16 }
14 17
15 # Bypass PeerTube webseed route for better performances 18 # Bypass PeerTube webseed route for better performances
diff --git a/support/nginx/peertube-https b/support/nginx/peertube-https
index 1b9c40e7f..f46442835 100644
--- a/support/nginx/peertube-https
+++ b/support/nginx/peertube-https
@@ -23,6 +23,9 @@ server {
23 23
24 # For the video upload 24 # For the video upload
25 client_max_body_size 2G; 25 client_max_body_size 2G;
26 proxy_connect_timeout 600;
27 proxy_send_timeout 600;
28 proxy_read_timeout 600;
26 } 29 }
27 30
28 # Bypass PeerTube webseed route for better performances 31 # Bypass PeerTube webseed route for better performances