aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+accounts/accounts.component.html7
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html2
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html6
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.html26
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.scss7
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.ts59
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.html27
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.scss7
-rw-r--r--client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.ts60
-rw-r--r--client/src/app/+my-account/my-account-routing.module.ts20
-rw-r--r--client/src/app/+my-account/my-account.component.html16
-rw-r--r--client/src/app/+my-account/my-account.component.scss2
-rw-r--r--client/src/app/+my-account/my-account.component.ts15
-rw-r--r--client/src/app/+my-account/my-account.module.ts6
-rw-r--r--client/src/app/shared/account/account.model.ts5
-rw-r--r--client/src/app/shared/blocklist/account-block.model.ts14
-rw-r--r--client/src/app/shared/blocklist/blocklist.service.ts79
-rw-r--r--client/src/app/shared/blocklist/index.ts2
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.html6
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.scss5
-rw-r--r--client/src/app/shared/moderation/user-moderation-dropdown.component.html7
-rw-r--r--client/src/app/shared/moderation/user-moderation-dropdown.component.ts121
-rw-r--r--client/src/app/shared/shared.module.ts2
-rw-r--r--server/controllers/api/users/my-blocklist.ts4
-rw-r--r--server/lib/blocklist.ts4
-rw-r--r--server/middlewares/validators/blocklist.ts45
-rw-r--r--server/models/account/account-blocklist.ts8
-rw-r--r--server/models/server/server-blocklist.ts4
-rw-r--r--server/tests/api/check-params/blocklist.ts20
-rw-r--r--server/tests/api/users/account-blocklist.ts14
-rw-r--r--shared/models/blocklist/account-block.model.ts2
-rw-r--r--shared/models/blocklist/server-block.model.ts2
33 files changed, 560 insertions, 46 deletions
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index 036e794d2..60dbcdf1d 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -10,8 +10,13 @@
10 <div class="actor-name">{{ account.nameWithHost }}</div> 10 <div class="actor-name">{{ account.nameWithHost }}</div>
11 11
12 <span *ngIf="user?.blocked" [ngbTooltip]="user.blockedReason" class="badge badge-danger" i18n>Banned</span> 12 <span *ngIf="user?.blocked" [ngbTooltip]="user.blockedReason" class="badge badge-danger" i18n>Banned</span>
13 <span *ngIf="account.muted" class="badge badge-danger" i18n>Muted</span>
14 <span *ngIf="account.mutedServer" class="badge badge-danger" i18n>Instance muted</span>
13 15
14 <my-user-moderation-dropdown buttonSize="small" [user]="user" (userChanged)="onUserChanged()" (userDeleted)="onUserDeleted()"> 16 <my-user-moderation-dropdown
17 buttonSize="small" [account]="account" [user]="user"
18 (userChanged)="onUserChanged()" (userDeleted)="onUserDeleted()"
19 >
15 </my-user-moderation-dropdown> 20 </my-user-moderation-dropdown>
16 </div> 21 </div>
17 <div i18n class="actor-followers">{{ account.followersCount }} subscribers</div> 22 <div i18n class="actor-followers">{{ account.followersCount }} subscribers</div>
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
index 287ab3e46..0374b70ef 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
@@ -9,7 +9,7 @@
9 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 9 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
10 <th i18n>Video</th> 10 <th i18n>Video</th>
11 <th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th> 11 <th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th>
12 <th style="width: 50px;"></th> 12 <th style="width: 120px;"></th>
13 </tr> 13 </tr>
14 </ng-template> 14 </ng-template>
15 15
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
index 0585e0490..ff4543b97 100644
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
@@ -8,7 +8,7 @@
8 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th> 8 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
9 <th i18n>Sensitive</th> 9 <th i18n>Sensitive</th>
10 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th> 10 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
11 <th style="width: 50px;"></th> 11 <th style="width: 120px;"></th>
12 </tr> 12 </tr>
13 </ng-template> 13 </ng-template>
14 14
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index afa9ccfe4..eb8d30e17 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -60,8 +60,10 @@
60 </td> 60 </td>
61 61
62 <td> 62 <td>
63 {{ user.username }} 63 <a i18n-title title="Go to the account page" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
64 <span *ngIf="user.blocked" class="banned-info">(banned)</span> 64 {{ user.username }}
65 <span i18n *ngIf="user.blocked" class="banned-info">(banned)</span>
66 </a>
65 </td> 67 </td>
66 <td>{{ user.email }}</td> 68 <td>{{ user.email }}</td>
67 <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td> 69 <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td>
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.html b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.html
new file mode 100644
index 000000000..a96a11f5e
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.html
@@ -0,0 +1,26 @@
1<div class="admin-sub-header">
2 <div i18n class="form-sub-title">Muted accounts</div>
3</div>
4
5<p-table
6 [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
8>
9
10 <ng-template pTemplate="header">
11 <tr>
12 <th i18n>Account</th>
13 <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
14 </tr>
15 </ng-template>
16
17 <ng-template pTemplate="body" let-accountBlock>
18 <tr>
19 <td>{{ accountBlock.blockedAccount.nameWithHost }}</td>
20 <td>{{ accountBlock.createdAt }}</td>
21 <td class="action-cell">
22 <button class="unblock-button" (click)="unblockAccount(accountBlock)" i18n>Unmute</button>
23 </td>
24 </tr>
25 </ng-template>
26</p-table>
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.scss b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.scss
new file mode 100644
index 000000000..6028b75ea
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.scss
@@ -0,0 +1,7 @@
1@import '_variables';
2@import '_mixins';
3
4.unblock-button {
5 @include peertube-button;
6 @include grey-button;
7} \ No newline at end of file
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.ts b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.ts
new file mode 100644
index 000000000..fbad28410
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-blocklist.component.ts
@@ -0,0 +1,59 @@
1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { RestPagination, RestTable } from '@app/shared'
5import { SortMeta } from 'primeng/components/common/sortmeta'
6import { BlocklistService, AccountBlock } from '@app/shared/blocklist'
7
8@Component({
9 selector: 'my-account-blocklist',
10 styleUrls: [ './my-account-blocklist.component.scss' ],
11 templateUrl: './my-account-blocklist.component.html'
12})
13export class MyAccountBlocklistComponent extends RestTable implements OnInit {
14 blockedAccounts: AccountBlock[] = []
15 totalRecords = 0
16 rowsPerPage = 10
17 sort: SortMeta = { field: 'createdAt', order: -1 }
18 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
19
20 constructor (
21 private notificationsService: NotificationsService,
22 private blocklistService: BlocklistService,
23 private i18n: I18n
24 ) {
25 super()
26 }
27
28 ngOnInit () {
29 this.initialize()
30 }
31
32 unblockAccount (accountBlock: AccountBlock) {
33 const blockedAccount = accountBlock.blockedAccount
34
35 this.blocklistService.unblockAccountByUser(blockedAccount)
36 .subscribe(
37 () => {
38 this.notificationsService.success(
39 this.i18n('Success'),
40 this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: blockedAccount.nameWithHost })
41 )
42
43 this.loadData()
44 }
45 )
46 }
47
48 protected loadData () {
49 return this.blocklistService.getUserAccountBlocklist(this.pagination, this.sort)
50 .subscribe(
51 resultList => {
52 this.blockedAccounts = resultList.data
53 this.totalRecords = resultList.total
54 },
55
56 err => this.notificationsService.error(this.i18n('Error'), err.message)
57 )
58 }
59}
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.html b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.html
new file mode 100644
index 000000000..680334740
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.html
@@ -0,0 +1,27 @@
1<div class="admin-sub-header">
2 <div i18n class="form-sub-title">Muted instances</div>
3</div>
4
5<p-table
6 [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
8>
9
10 <ng-template pTemplate="header">
11 <tr>
12 <th i18n>Instance</th>
13 <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
14 <th></th>
15 </tr>
16 </ng-template>
17
18 <ng-template pTemplate="body" let-serverBlock>
19 <tr>
20 <td>{{ serverBlock.blockedServer.host }}</td>
21 <td>{{ serverBlock.createdAt }}</td>
22 <td class="action-cell">
23 <button class="unblock-button" (click)="unblockServer(serverBlock)" i18n>Unmute</button>
24 </td>
25 </tr>
26 </ng-template>
27</p-table>
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.scss b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.scss
new file mode 100644
index 000000000..6028b75ea
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.scss
@@ -0,0 +1,7 @@
1@import '_variables';
2@import '_mixins';
3
4.unblock-button {
5 @include peertube-button;
6 @include grey-button;
7} \ No newline at end of file
diff --git a/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.ts b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.ts
new file mode 100644
index 000000000..b994c2c99
--- /dev/null
+++ b/client/src/app/+my-account/my-account-blocklist/my-account-server-blocklist.component.ts
@@ -0,0 +1,60 @@
1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { RestPagination, RestTable } from '@app/shared'
5import { SortMeta } from 'primeng/components/common/sortmeta'
6import { ServerBlock } from '../../../../../shared'
7import { BlocklistService } from '@app/shared/blocklist'
8
9@Component({
10 selector: 'my-account-server-blocklist',
11 styleUrls: [ './my-account-server-blocklist.component.scss' ],
12 templateUrl: './my-account-server-blocklist.component.html'
13})
14export class MyAccountServerBlocklistComponent extends RestTable implements OnInit {
15 blockedAccounts: ServerBlock[] = []
16 totalRecords = 0
17 rowsPerPage = 10
18 sort: SortMeta = { field: 'createdAt', order: -1 }
19 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
20
21 constructor (
22 private notificationsService: NotificationsService,
23 private blocklistService: BlocklistService,
24 private i18n: I18n
25 ) {
26 super()
27 }
28
29 ngOnInit () {
30 this.initialize()
31 }
32
33 unblockServer (serverBlock: ServerBlock) {
34 const host = serverBlock.blockedServer.host
35
36 this.blocklistService.unblockServerByUser(host)
37 .subscribe(
38 () => {
39 this.notificationsService.success(
40 this.i18n('Success'),
41 this.i18n('Instance {{host}} unmuted.', { host })
42 )
43
44 this.loadData()
45 }
46 )
47 }
48
49 protected loadData () {
50 return this.blocklistService.getUserServerBlocklist(this.pagination, this.sort)
51 .subscribe(
52 resultList => {
53 this.blockedAccounts = resultList.data
54 this.totalRecords = resultList.total
55 },
56
57 err => this.notificationsService.error(this.i18n('Error'), err.message)
58 )
59 }
60}
diff --git a/client/src/app/+my-account/my-account-routing.module.ts b/client/src/app/+my-account/my-account-routing.module.ts
index 4b2168e35..49f9c94a7 100644
--- a/client/src/app/+my-account/my-account-routing.module.ts
+++ b/client/src/app/+my-account/my-account-routing.module.ts
@@ -11,6 +11,8 @@ import { MyAccountVideoChannelUpdateComponent } from '@app/+my-account/my-accoun
11import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component' 11import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component'
12import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-subscriptions/my-account-subscriptions.component' 12import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-subscriptions/my-account-subscriptions.component'
13import { MyAccountOwnershipComponent } from '@app/+my-account/my-account-ownership/my-account-ownership.component' 13import { MyAccountOwnershipComponent } from '@app/+my-account/my-account-ownership/my-account-ownership.component'
14import { MyAccountBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-blocklist.component'
15import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component'
14 16
15const myAccountRoutes: Routes = [ 17const myAccountRoutes: Routes = [
16 { 18 {
@@ -94,6 +96,24 @@ const myAccountRoutes: Routes = [
94 title: 'Ownership changes' 96 title: 'Ownership changes'
95 } 97 }
96 } 98 }
99 },
100 {
101 path: 'blocklist/accounts',
102 component: MyAccountBlocklistComponent,
103 data: {
104 meta: {
105 title: 'Accounts blocklist'
106 }
107 }
108 },
109 {
110 path: 'blocklist/servers',
111 component: MyAccountServerBlocklistComponent,
112 data: {
113 meta: {
114 title: 'Instances blocklist'
115 }
116 }
97 } 117 }
98 ] 118 ]
99 } 119 }
diff --git a/client/src/app/+my-account/my-account.component.html b/client/src/app/+my-account/my-account.component.html
index b602fd69f..41333c25a 100644
--- a/client/src/app/+my-account/my-account.component.html
+++ b/client/src/app/+my-account/my-account.component.html
@@ -19,7 +19,21 @@
19 </div> 19 </div>
20 </div> 20 </div>
21 21
22 <a i18n routerLink="/my-account/ownership" routerLinkActive="active" class="title-page">Ownership changes</a> 22 <div ngbDropdown class="misc">
23 <span role="button" class="title-page" [ngClass]="{ active: miscLabel !== '' }" ngbDropdownToggle>
24 <ng-container i18n>Misc</ng-container>
25 <ng-container *ngIf="miscLabel"> - {{ miscLabel }}</ng-container>
26 </span>
27
28 <div ngbDropdownMenu>
29 <a class="dropdown-item" i18n routerLink="/my-account/blocklist/accounts">Muted accounts</a>
30
31 <a class="dropdown-item" i18n routerLink="/my-account/blocklist/servers">Muted instances</a>
32
33 <a class="dropdown-item" i18n routerLink="/my-account/ownership">Ownership changes</a>
34 </div>
35 </div>
36
23 </div> 37 </div>
24 38
25 <div class="margin-content"> 39 <div class="margin-content">
diff --git a/client/src/app/+my-account/my-account.component.scss b/client/src/app/+my-account/my-account.component.scss
index 20b2639b5..6243c6dcf 100644
--- a/client/src/app/+my-account/my-account.component.scss
+++ b/client/src/app/+my-account/my-account.component.scss
@@ -1,4 +1,4 @@
1.my-library { 1.my-library, .misc {
2 span[role=button] { 2 span[role=button] {
3 cursor: pointer; 3 cursor: pointer;
4 } 4 }
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index bad60a8fb..d728caf07 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -13,6 +13,7 @@ import { Subscription } from 'rxjs'
13export class MyAccountComponent implements OnInit, OnDestroy { 13export class MyAccountComponent implements OnInit, OnDestroy {
14 14
15 libraryLabel = '' 15 libraryLabel = ''
16 miscLabel = ''
16 17
17 private routeSub: Subscription 18 private routeSub: Subscription
18 19
@@ -23,11 +24,11 @@ export class MyAccountComponent implements OnInit, OnDestroy {
23 ) {} 24 ) {}
24 25
25 ngOnInit () { 26 ngOnInit () {
26 this.updateLibraryLabel(this.router.url) 27 this.updateLabels(this.router.url)
27 28
28 this.routeSub = this.router.events 29 this.routeSub = this.router.events
29 .pipe(filter(event => event instanceof NavigationStart)) 30 .pipe(filter(event => event instanceof NavigationStart))
30 .subscribe((event: NavigationStart) => this.updateLibraryLabel(event.url)) 31 .subscribe((event: NavigationStart) => this.updateLabels(event.url))
31 } 32 }
32 33
33 ngOnDestroy () { 34 ngOnDestroy () {
@@ -40,7 +41,7 @@ export class MyAccountComponent implements OnInit, OnDestroy {
40 return importConfig.http.enabled || importConfig.torrent.enabled 41 return importConfig.http.enabled || importConfig.torrent.enabled
41 } 42 }
42 43
43 private updateLibraryLabel (url: string) { 44 private updateLabels (url: string) {
44 const [ path ] = url.split('?') 45 const [ path ] = url.split('?')
45 46
46 if (path.startsWith('/my-account/video-channels')) { 47 if (path.startsWith('/my-account/video-channels')) {
@@ -54,5 +55,13 @@ export class MyAccountComponent implements OnInit, OnDestroy {
54 } else { 55 } else {
55 this.libraryLabel = '' 56 this.libraryLabel = ''
56 } 57 }
58
59 if (path.startsWith('/my-account/blocklist/accounts')) {
60 this.miscLabel = this.i18n('Muted accounts')
61 } else if (path.startsWith('/my-account/blocklist/servers')) {
62 this.miscLabel = this.i18n('Muted instances')
63 } else {
64 this.miscLabel = ''
65 }
57 } 66 }
58} 67}
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index ad21162a8..017ebd57d 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -19,6 +19,8 @@ import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-i
19import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component' 19import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component'
20import { MyAccountDangerZoneComponent } from '@app/+my-account/my-account-settings/my-account-danger-zone' 20import { MyAccountDangerZoneComponent } from '@app/+my-account/my-account-settings/my-account-danger-zone'
21import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-subscriptions/my-account-subscriptions.component' 21import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-subscriptions/my-account-subscriptions.component'
22import { MyAccountBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-blocklist.component'
23import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component'
22 24
23@NgModule({ 25@NgModule({
24 imports: [ 26 imports: [
@@ -45,7 +47,9 @@ import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-sub
45 ActorAvatarInfoComponent, 47 ActorAvatarInfoComponent,
46 MyAccountVideoImportsComponent, 48 MyAccountVideoImportsComponent,
47 MyAccountDangerZoneComponent, 49 MyAccountDangerZoneComponent,
48 MyAccountSubscriptionsComponent 50 MyAccountSubscriptionsComponent,
51 MyAccountBlocklistComponent,
52 MyAccountServerBlocklistComponent
49 ], 53 ],
50 54
51 exports: [ 55 exports: [
diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts
index 42f2cfeaf..0aba9428a 100644
--- a/client/src/app/shared/account/account.model.ts
+++ b/client/src/app/shared/account/account.model.ts
@@ -5,6 +5,8 @@ export class Account extends Actor implements ServerAccount {
5 displayName: string 5 displayName: string
6 description: string 6 description: string
7 nameWithHost: string 7 nameWithHost: string
8 muted: boolean
9 mutedServer: boolean
8 10
9 userId?: number 11 userId?: number
10 12
@@ -15,5 +17,8 @@ export class Account extends Actor implements ServerAccount {
15 this.description = hash.description 17 this.description = hash.description
16 this.userId = hash.userId 18 this.userId = hash.userId
17 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) 19 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host)
20
21 this.muted = false
22 this.mutedServer = false
18 } 23 }
19} 24}
diff --git a/client/src/app/shared/blocklist/account-block.model.ts b/client/src/app/shared/blocklist/account-block.model.ts
new file mode 100644
index 000000000..336680f65
--- /dev/null
+++ b/client/src/app/shared/blocklist/account-block.model.ts
@@ -0,0 +1,14 @@
1import { AccountBlock as AccountBlockServer } from '../../../../../shared'
2import { Account } from '../account/account.model'
3
4export class AccountBlock implements AccountBlockServer {
5 byAccount: Account
6 blockedAccount: Account
7 createdAt: Date | string
8
9 constructor (block: AccountBlockServer) {
10 this.byAccount = new Account(block.byAccount)
11 this.blockedAccount = new Account(block.blockedAccount)
12 this.createdAt = block.createdAt
13 }
14} \ No newline at end of file
diff --git a/client/src/app/shared/blocklist/blocklist.service.ts b/client/src/app/shared/blocklist/blocklist.service.ts
new file mode 100644
index 000000000..d9c318258
--- /dev/null
+++ b/client/src/app/shared/blocklist/blocklist.service.ts
@@ -0,0 +1,79 @@
1import { Injectable } from '@angular/core'
2import { environment } from '../../../environments/environment'
3import { HttpClient, HttpParams } from '@angular/common/http'
4import { RestExtractor, RestPagination, RestService } from '../rest'
5import { SortMeta } from 'primeng/api'
6import { catchError, map } from 'rxjs/operators'
7import { AccountBlock as AccountBlockServer, ResultList, ServerBlock } from '../../../../../shared'
8import { Account } from '@app/shared/account/account.model'
9import { AccountBlock } from '@app/shared/blocklist/account-block.model'
10
11@Injectable()
12export class BlocklistService {
13 static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist'
14
15 constructor (
16 private authHttp: HttpClient,
17 private restExtractor: RestExtractor,
18 private restService: RestService
19 ) { }
20
21 /*********************** User -> Account blocklist ***********************/
22
23 getUserAccountBlocklist (pagination: RestPagination, sort: SortMeta) {
24 let params = new HttpParams()
25 params = this.restService.addRestGetParams(params, pagination, sort)
26
27 return this.authHttp.get<ResultList<AccountBlock>>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts', { params })
28 .pipe(
29 map(res => this.restExtractor.convertResultListDateToHuman(res)),
30 map(res => this.restExtractor.applyToResultListData(res, this.formatAccountBlock.bind(this))),
31 catchError(err => this.restExtractor.handleError(err))
32 )
33 }
34
35 blockAccountByUser (account: Account) {
36 const body = { accountName: account.nameWithHost }
37
38 return this.authHttp.post(BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts', body)
39 .pipe(catchError(err => this.restExtractor.handleError(err)))
40 }
41
42 unblockAccountByUser (account: Account) {
43 const path = BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts/' + account.nameWithHost
44
45 return this.authHttp.delete(path)
46 .pipe(catchError(err => this.restExtractor.handleError(err)))
47 }
48
49 /*********************** User -> Server blocklist ***********************/
50
51 getUserServerBlocklist (pagination: RestPagination, sort: SortMeta) {
52 let params = new HttpParams()
53 params = this.restService.addRestGetParams(params, pagination, sort)
54
55 return this.authHttp.get<ResultList<ServerBlock>>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers', { params })
56 .pipe(
57 map(res => this.restExtractor.convertResultListDateToHuman(res)),
58 catchError(err => this.restExtractor.handleError(err))
59 )
60 }
61
62 blockServerByUser (host: string) {
63 const body = { host }
64
65 return this.authHttp.post(BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers', body)
66 .pipe(catchError(err => this.restExtractor.handleError(err)))
67 }
68
69 unblockServerByUser (host: string) {
70 const path = BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers/' + host
71
72 return this.authHttp.delete(path)
73 .pipe(catchError(err => this.restExtractor.handleError(err)))
74 }
75
76 private formatAccountBlock (accountBlock: AccountBlockServer) {
77 return new AccountBlock(accountBlock)
78 }
79}
diff --git a/client/src/app/shared/blocklist/index.ts b/client/src/app/shared/blocklist/index.ts
new file mode 100644
index 000000000..8cf6a55f7
--- /dev/null
+++ b/client/src/app/shared/blocklist/index.ts
@@ -0,0 +1,2 @@
1export * from './blocklist.service'
2export * from './account-block.model' \ No newline at end of file
diff --git a/client/src/app/shared/buttons/action-dropdown.component.html b/client/src/app/shared/buttons/action-dropdown.component.html
index 111627424..48230d6d8 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/buttons/action-dropdown.component.html
@@ -9,13 +9,13 @@
9 9
10 <div ngbDropdownMenu class="dropdown-menu"> 10 <div ngbDropdownMenu class="dropdown-menu">
11 <ng-container *ngFor="let action of actions"> 11 <ng-container *ngFor="let action of actions">
12 <div class="dropdown-item" *ngIf="action.isDisplayed === undefined || action.isDisplayed(entry) === true"> 12 <ng-container *ngIf="action.isDisplayed === undefined || action.isDisplayed(entry) === true">
13 <a *ngIf="action.linkBuilder" class="dropdown-item" [routerLink]="action.linkBuilder(entry)">{{ action.label }}</a> 13 <a *ngIf="action.linkBuilder" class="dropdown-item" [routerLink]="action.linkBuilder(entry)">{{ action.label }}</a>
14 14
15 <span *ngIf="!action.linkBuilder" class="custom-action" class="dropdown-item" (click)="action.handler(entry)" role="button"> 15 <span *ngIf="!action.linkBuilder" class="custom-action dropdown-item" (click)="action.handler(entry)" role="button">
16 {{ action.label }} 16 {{ action.label }}
17 </span> 17 </span>
18 </div> 18 </ng-container>
19 </ng-container> 19 </ng-container>
20 </div> 20 </div>
21</div> \ No newline at end of file 21</div> \ No newline at end of file
diff --git a/client/src/app/shared/buttons/action-dropdown.component.scss b/client/src/app/shared/buttons/action-dropdown.component.scss
index 0a9aa7b04..92c4d1d2c 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/buttons/action-dropdown.component.scss
@@ -46,5 +46,10 @@
46 .dropdown-item { 46 .dropdown-item {
47 cursor: pointer; 47 cursor: pointer;
48 color: #000 !important; 48 color: #000 !important;
49
50 a, span {
51 display: block;
52 width: 100%;
53 }
49 } 54 }
50} \ No newline at end of file 55} \ No newline at end of file
diff --git a/client/src/app/shared/moderation/user-moderation-dropdown.component.html b/client/src/app/shared/moderation/user-moderation-dropdown.component.html
index 01db7cd4a..7367a7e59 100644
--- a/client/src/app/shared/moderation/user-moderation-dropdown.component.html
+++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.html
@@ -1,5 +1,8 @@
1<ng-container *ngIf="user && userActions.length !== 0"> 1<ng-container *ngIf="userActions.length !== 0">
2 <my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal> 2 <my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal>
3 3
4 <my-action-dropdown [actions]="userActions" [entry]="user" [buttonSize]="buttonSize" [placement]="placement"></my-action-dropdown> 4 <my-action-dropdown
5 [actions]="userActions" [entry]="{ user: user, account: account }"
6 [buttonSize]="buttonSize" [placement]="placement"
7 ></my-action-dropdown>
5</ng-container> \ No newline at end of file 8</ng-container> \ No newline at end of file
diff --git a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
index 105c99d8b..2f4a55f37 100644
--- a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
@@ -1,4 +1,4 @@
1import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 4import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
@@ -6,16 +6,20 @@ import { UserBanModalComponent } from '@app/shared/moderation/user-ban-modal.com
6import { UserService } from '@app/shared/users' 6import { UserService } from '@app/shared/users'
7import { AuthService, ConfirmService } from '@app/core' 7import { AuthService, ConfirmService } from '@app/core'
8import { User, UserRight } from '../../../../../shared/models/users' 8import { User, UserRight } from '../../../../../shared/models/users'
9import { Account } from '@app/shared/account/account.model'
10import { BlocklistService } from '@app/shared/blocklist'
9 11
10@Component({ 12@Component({
11 selector: 'my-user-moderation-dropdown', 13 selector: 'my-user-moderation-dropdown',
12 templateUrl: './user-moderation-dropdown.component.html', 14 templateUrl: './user-moderation-dropdown.component.html',
13 styleUrls: [ './user-moderation-dropdown.component.scss' ] 15 styleUrls: [ './user-moderation-dropdown.component.scss' ]
14}) 16})
15export class UserModerationDropdownComponent implements OnInit { 17export class UserModerationDropdownComponent implements OnChanges {
16 @ViewChild('userBanModal') userBanModal: UserBanModalComponent 18 @ViewChild('userBanModal') userBanModal: UserBanModalComponent
17 19
18 @Input() user: User 20 @Input() user: User
21 @Input() account: Account
22
19 @Input() buttonSize: 'normal' | 'small' = 'normal' 23 @Input() buttonSize: 'normal' | 'small' = 'normal'
20 @Input() placement = 'left' 24 @Input() placement = 'left'
21 25
@@ -29,10 +33,11 @@ export class UserModerationDropdownComponent implements OnInit {
29 private notificationsService: NotificationsService, 33 private notificationsService: NotificationsService,
30 private confirmService: ConfirmService, 34 private confirmService: ConfirmService,
31 private userService: UserService, 35 private userService: UserService,
36 private blocklistService: BlocklistService,
32 private i18n: I18n 37 private i18n: I18n
33 ) { } 38 ) { }
34 39
35 ngOnInit () { 40 ngOnChanges () {
36 this.buildActions() 41 this.buildActions()
37 } 42 }
38 43
@@ -92,6 +97,74 @@ export class UserModerationDropdownComponent implements OnInit {
92 ) 97 )
93 } 98 }
94 99
100 blockAccountByUser (account: Account) {
101 this.blocklistService.blockAccountByUser(account)
102 .subscribe(
103 () => {
104 this.notificationsService.success(
105 this.i18n('Success'),
106 this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost })
107 )
108
109 this.account.muted = true
110 this.userChanged.emit()
111 },
112
113 err => this.notificationsService.error(this.i18n('Error'), err.message)
114 )
115 }
116
117 unblockAccountByUser (account: Account) {
118 this.blocklistService.unblockAccountByUser(account)
119 .subscribe(
120 () => {
121 this.notificationsService.success(
122 this.i18n('Success'),
123 this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost })
124 )
125
126 this.account.muted = false
127 this.userChanged.emit()
128 },
129
130 err => this.notificationsService.error(this.i18n('Error'), err.message)
131 )
132 }
133
134 blockServerByUser (host: string) {
135 this.blocklistService.blockServerByUser(host)
136 .subscribe(
137 () => {
138 this.notificationsService.success(
139 this.i18n('Success'),
140 this.i18n('Instance {{host}} muted.', { host })
141 )
142
143 this.account.mutedServer = true
144 this.userChanged.emit()
145 },
146
147 err => this.notificationsService.error(this.i18n('Error'), err.message)
148 )
149 }
150
151 unblockServerByUser (host: string) {
152 this.blocklistService.unblockServerByUser(host)
153 .subscribe(
154 () => {
155 this.notificationsService.success(
156 this.i18n('Success'),
157 this.i18n('Instance {{host}} unmuted.', { host })
158 )
159
160 this.account.mutedServer = false
161 this.userChanged.emit()
162 },
163
164 err => this.notificationsService.error(this.i18n('Error'), err.message)
165 )
166 }
167
95 getRouterUserEditLink (user: User) { 168 getRouterUserEditLink (user: User) {
96 return [ '/admin', 'users', 'update', user.id ] 169 return [ '/admin', 'users', 'update', user.id ]
97 } 170 }
@@ -102,25 +175,53 @@ export class UserModerationDropdownComponent implements OnInit {
102 if (this.authService.isLoggedIn()) { 175 if (this.authService.isLoggedIn()) {
103 const authUser = this.authService.getUser() 176 const authUser = this.authService.getUser()
104 177
105 if (authUser.hasRight(UserRight.MANAGE_USERS)) { 178 if (this.user && authUser.id === this.user.id) return
179
180 if (this.user && authUser.hasRight(UserRight.MANAGE_USERS)) {
106 this.userActions = this.userActions.concat([ 181 this.userActions = this.userActions.concat([
107 { 182 {
108 label: this.i18n('Edit'), 183 label: this.i18n('Edit'),
109 linkBuilder: this.getRouterUserEditLink 184 linkBuilder: ({ user }) => this.getRouterUserEditLink(user)
110 }, 185 },
111 { 186 {
112 label: this.i18n('Delete'), 187 label: this.i18n('Delete'),
113 handler: user => this.removeUser(user) 188 handler: ({ user }) => this.removeUser(user)
114 }, 189 },
115 { 190 {
116 label: this.i18n('Ban'), 191 label: this.i18n('Ban'),
117 handler: user => this.openBanUserModal(user), 192 handler: ({ user }) => this.openBanUserModal(user),
118 isDisplayed: user => !user.blocked 193 isDisplayed: ({ user }) => !user.muted
119 }, 194 },
120 { 195 {
121 label: this.i18n('Unban'), 196 label: this.i18n('Unban'),
122 handler: user => this.unbanUser(user), 197 handler: ({ user }) => this.unbanUser(user),
123 isDisplayed: user => user.blocked 198 isDisplayed: ({ user }) => user.muted
199 }
200 ])
201 }
202
203 // User actions on accounts/servers
204 if (this.account) {
205 this.userActions = this.userActions.concat([
206 {
207 label: this.i18n('Mute this account'),
208 isDisplayed: ({ account }) => account.muted === false,
209 handler: ({ account }) => this.blockAccountByUser(account)
210 },
211 {
212 label: this.i18n('Unmute this account'),
213 isDisplayed: ({ account }) => account.muted === true,
214 handler: ({ account }) => this.unblockAccountByUser(account)
215 },
216 {
217 label: this.i18n('Mute the instance'),
218 isDisplayed: ({ account }) => !account.userId && account.mutedServer === false,
219 handler: ({ account }) => this.blockServerByUser(account.host)
220 },
221 {
222 label: this.i18n('Unmute the instance'),
223 isDisplayed: ({ account }) => !account.userId && account.mutedServer === true,
224 handler: ({ account }) => this.unblockServerByUser(account.host)
124 } 225 }
125 ]) 226 ])
126 } 227 }
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 9647a7966..40e05fcc7 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -58,6 +58,7 @@ import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-fe
58import { OverviewService } from '@app/shared/overview' 58import { OverviewService } from '@app/shared/overview'
59import { UserBanModalComponent } from '@app/shared/moderation' 59import { UserBanModalComponent } from '@app/shared/moderation'
60import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component' 60import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component'
61import { BlocklistService } from '@app/shared/blocklist'
61 62
62@NgModule({ 63@NgModule({
63 imports: [ 64 imports: [
@@ -172,6 +173,7 @@ import { UserModerationDropdownComponent } from '@app/shared/moderation/user-mod
172 OverviewService, 173 OverviewService,
173 VideoChangeOwnershipValidatorsService, 174 VideoChangeOwnershipValidatorsService,
174 VideoAcceptOwnershipValidatorsService, 175 VideoAcceptOwnershipValidatorsService,
176 BlocklistService,
175 177
176 I18nPrimengCalendarService, 178 I18nPrimengCalendarService,
177 ScreenService, 179 ScreenService,
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts
index e955ffde9..95a4105ec 100644
--- a/server/controllers/api/users/my-blocklist.ts
+++ b/server/controllers/api/users/my-blocklist.ts
@@ -6,7 +6,6 @@ import {
6 asyncRetryTransactionMiddleware, 6 asyncRetryTransactionMiddleware,
7 authenticate, 7 authenticate,
8 paginationValidator, 8 paginationValidator,
9 serverGetValidator,
10 setDefaultPagination, 9 setDefaultPagination,
11 setDefaultSort, 10 setDefaultSort,
12 unblockAccountByAccountValidator 11 unblockAccountByAccountValidator
@@ -14,6 +13,7 @@ import {
14import { 13import {
15 accountsBlocklistSortValidator, 14 accountsBlocklistSortValidator,
16 blockAccountByAccountValidator, 15 blockAccountByAccountValidator,
16 blockServerByAccountValidator,
17 serversBlocklistSortValidator, 17 serversBlocklistSortValidator,
18 unblockServerByAccountValidator 18 unblockServerByAccountValidator
19} from '../../../middlewares/validators' 19} from '../../../middlewares/validators'
@@ -58,7 +58,7 @@ myBlocklistRouter.get('/me/blocklist/servers',
58 58
59myBlocklistRouter.post('/me/blocklist/servers', 59myBlocklistRouter.post('/me/blocklist/servers',
60 authenticate, 60 authenticate,
61 asyncMiddleware(serverGetValidator), 61 asyncMiddleware(blockServerByAccountValidator),
62 asyncRetryTransactionMiddleware(blockServer) 62 asyncRetryTransactionMiddleware(blockServer)
63) 63)
64 64
diff --git a/server/lib/blocklist.ts b/server/lib/blocklist.ts
index 394c24537..1633e500c 100644
--- a/server/lib/blocklist.ts
+++ b/server/lib/blocklist.ts
@@ -4,7 +4,7 @@ import { ServerBlocklistModel } from '../models/server/server-blocklist'
4 4
5function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { 5function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
6 return sequelizeTypescript.transaction(async t => { 6 return sequelizeTypescript.transaction(async t => {
7 return AccountBlocklistModel.create({ 7 return AccountBlocklistModel.upsert({
8 accountId: byAccountId, 8 accountId: byAccountId,
9 targetAccountId: targetAccountId 9 targetAccountId: targetAccountId
10 }, { transaction: t }) 10 }, { transaction: t })
@@ -13,7 +13,7 @@ function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
13 13
14function addServerInBlocklist (byAccountId: number, targetServerId: number) { 14function addServerInBlocklist (byAccountId: number, targetServerId: number) {
15 return sequelizeTypescript.transaction(async t => { 15 return sequelizeTypescript.transaction(async t => {
16 return ServerBlocklistModel.create({ 16 return ServerBlocklistModel.upsert({
17 accountId: byAccountId, 17 accountId: byAccountId,
18 targetServerId 18 targetServerId
19 }, { transaction: t }) 19 }, { transaction: t })
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index 9dbd5e512..25c054d6b 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -1,4 +1,4 @@
1import { param, body } from 'express-validator/check' 1import { body, param } from 'express-validator/check'
2import * as express from 'express' 2import * as express from 'express'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import { areValidationErrors } from './utils' 4import { areValidationErrors } from './utils'
@@ -7,6 +7,8 @@ import { UserModel } from '../../models/account/user'
7import { AccountBlocklistModel } from '../../models/account/account-blocklist' 7import { AccountBlocklistModel } from '../../models/account/account-blocklist'
8import { isHostValid } from '../../helpers/custom-validators/servers' 8import { isHostValid } from '../../helpers/custom-validators/servers'
9import { ServerBlocklistModel } from '../../models/server/server-blocklist' 9import { ServerBlocklistModel } from '../../models/server/server-blocklist'
10import { ServerModel } from '../../models/server/server'
11import { CONFIG } from '../../initializers'
10 12
11const blockAccountByAccountValidator = [ 13const blockAccountByAccountValidator = [
12 body('accountName').exists().withMessage('Should have an account name with host'), 14 body('accountName').exists().withMessage('Should have an account name with host'),
@@ -17,6 +19,17 @@ const blockAccountByAccountValidator = [
17 if (areValidationErrors(req, res)) return 19 if (areValidationErrors(req, res)) return
18 if (!await isAccountNameWithHostExist(req.body.accountName, res)) return 20 if (!await isAccountNameWithHostExist(req.body.accountName, res)) return
19 21
22 const user = res.locals.oauth.token.User as UserModel
23 const accountToBlock = res.locals.account
24
25 if (user.Account.id === accountToBlock.id) {
26 res.status(409)
27 .send({ error: 'You cannot block yourself.' })
28 .end()
29
30 return
31 }
32
20 return next() 33 return next()
21 } 34 }
22] 35]
@@ -38,6 +51,35 @@ const unblockAccountByAccountValidator = [
38 } 51 }
39] 52]
40 53
54const blockServerByAccountValidator = [
55 body('host').custom(isHostValid).withMessage('Should have a valid host'),
56
57 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
58 logger.debug('Checking serverGetValidator parameters', { parameters: req.body })
59
60 if (areValidationErrors(req, res)) return
61
62 const host: string = req.body.host
63
64 if (host === CONFIG.WEBSERVER.HOST) {
65 return res.status(409)
66 .send({ error: 'You cannot block your own server.' })
67 .end()
68 }
69
70 const server = await ServerModel.loadByHost(host)
71 if (!server) {
72 return res.status(404)
73 .send({ error: 'Server host not found.' })
74 .end()
75 }
76
77 res.locals.server = server
78
79 return next()
80 }
81]
82
41const unblockServerByAccountValidator = [ 83const unblockServerByAccountValidator = [
42 param('host').custom(isHostValid).withMessage('Should have an account name with host'), 84 param('host').custom(isHostValid).withMessage('Should have an account name with host'),
43 85
@@ -56,6 +98,7 @@ const unblockServerByAccountValidator = [
56// --------------------------------------------------------------------------- 98// ---------------------------------------------------------------------------
57 99
58export { 100export {
101 blockServerByAccountValidator,
59 blockAccountByAccountValidator, 102 blockAccountByAccountValidator,
60 unblockAccountByAccountValidator, 103 unblockAccountByAccountValidator,
61 unblockServerByAccountValidator 104 unblockServerByAccountValidator
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts
index bacd122e8..fa2819235 100644
--- a/server/models/account/account-blocklist.ts
+++ b/server/models/account/account-blocklist.ts
@@ -18,7 +18,7 @@ enum ScopeNames {
18 { 18 {
19 model: () => AccountModel, 19 model: () => AccountModel,
20 required: true, 20 required: true,
21 as: 'AccountBlocked' 21 as: 'BlockedAccount'
22 } 22 }
23 ] 23 ]
24 } 24 }
@@ -67,10 +67,10 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
67 name: 'targetAccountId', 67 name: 'targetAccountId',
68 allowNull: false 68 allowNull: false
69 }, 69 },
70 as: 'AccountBlocked', 70 as: 'BlockedAccount',
71 onDelete: 'CASCADE' 71 onDelete: 'CASCADE'
72 }) 72 })
73 AccountBlocked: AccountModel 73 BlockedAccount: AccountModel
74 74
75 static loadByAccountAndTarget (accountId: number, targetAccountId: number) { 75 static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
76 const query = { 76 const query = {
@@ -104,7 +104,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
104 toFormattedJSON (): AccountBlock { 104 toFormattedJSON (): AccountBlock {
105 return { 105 return {
106 byAccount: this.ByAccount.toFormattedJSON(), 106 byAccount: this.ByAccount.toFormattedJSON(),
107 accountBlocked: this.AccountBlocked.toFormattedJSON(), 107 blockedAccount: this.BlockedAccount.toFormattedJSON(),
108 createdAt: this.createdAt 108 createdAt: this.createdAt
109 } 109 }
110 } 110 }
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts
index 705ed2c6b..450f27152 100644
--- a/server/models/server/server-blocklist.ts
+++ b/server/models/server/server-blocklist.ts
@@ -72,7 +72,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
72 }, 72 },
73 onDelete: 'CASCADE' 73 onDelete: 'CASCADE'
74 }) 74 })
75 ServerBlocked: ServerModel 75 BlockedServer: ServerModel
76 76
77 static loadByAccountAndHost (accountId: number, host: string) { 77 static loadByAccountAndHost (accountId: number, host: string) {
78 const query = { 78 const query = {
@@ -114,7 +114,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
114 toFormattedJSON (): ServerBlock { 114 toFormattedJSON (): ServerBlock {
115 return { 115 return {
116 byAccount: this.ByAccount.toFormattedJSON(), 116 byAccount: this.ByAccount.toFormattedJSON(),
117 serverBlocked: this.ServerBlocked.toFormattedJSON(), 117 blockedServer: this.BlockedServer.toFormattedJSON(),
118 createdAt: this.createdAt 118 createdAt: this.createdAt
119 } 119 }
120 } 120 }
diff --git a/server/tests/api/check-params/blocklist.ts b/server/tests/api/check-params/blocklist.ts
index 8117c46a6..d24d9323f 100644
--- a/server/tests/api/check-params/blocklist.ts
+++ b/server/tests/api/check-params/blocklist.ts
@@ -85,6 +85,16 @@ describe('Test blocklist API validators', function () {
85 }) 85 })
86 }) 86 })
87 87
88 it('Should fail to block ourselves', async function () {
89 await makePostBodyRequest({
90 url: server.url,
91 token: server.accessToken,
92 path,
93 fields: { accountName: 'root' },
94 statusCodeExpected: 409
95 })
96 })
97
88 it('Should succeed with the correct params', async function () { 98 it('Should succeed with the correct params', async function () {
89 await makePostBodyRequest({ 99 await makePostBodyRequest({
90 url: server.url, 100 url: server.url,
@@ -170,6 +180,16 @@ describe('Test blocklist API validators', function () {
170 }) 180 })
171 }) 181 })
172 182
183 it('Should fail with our own server', async function () {
184 await makePostBodyRequest({
185 url: server.url,
186 token: server.accessToken,
187 path,
188 fields: { host: 'localhost:9001' },
189 statusCodeExpected: 409
190 })
191 })
192
173 it('Should succeed with the correct params', async function () { 193 it('Should succeed with the correct params', async function () {
174 await makePostBodyRequest({ 194 await makePostBodyRequest({
175 url: server.url, 195 url: server.url,
diff --git a/server/tests/api/users/account-blocklist.ts b/server/tests/api/users/account-blocklist.ts
index 00ad51461..026971331 100644
--- a/server/tests/api/users/account-blocklist.ts
+++ b/server/tests/api/users/account-blocklist.ts
@@ -183,9 +183,9 @@ describe('Test accounts blocklist', function () {
183 const block = blocks[0] 183 const block = blocks[0]
184 expect(block.byAccount.displayName).to.equal('root') 184 expect(block.byAccount.displayName).to.equal('root')
185 expect(block.byAccount.name).to.equal('root') 185 expect(block.byAccount.name).to.equal('root')
186 expect(block.accountBlocked.displayName).to.equal('user2') 186 expect(block.blockedAccount.displayName).to.equal('user2')
187 expect(block.accountBlocked.name).to.equal('user2') 187 expect(block.blockedAccount.name).to.equal('user2')
188 expect(block.accountBlocked.host).to.equal('localhost:9002') 188 expect(block.blockedAccount.host).to.equal('localhost:9002')
189 } 189 }
190 190
191 { 191 {
@@ -197,9 +197,9 @@ describe('Test accounts blocklist', function () {
197 const block = blocks[0] 197 const block = blocks[0]
198 expect(block.byAccount.displayName).to.equal('root') 198 expect(block.byAccount.displayName).to.equal('root')
199 expect(block.byAccount.name).to.equal('root') 199 expect(block.byAccount.name).to.equal('root')
200 expect(block.accountBlocked.displayName).to.equal('user1') 200 expect(block.blockedAccount.displayName).to.equal('user1')
201 expect(block.accountBlocked.name).to.equal('user1') 201 expect(block.blockedAccount.name).to.equal('user1')
202 expect(block.accountBlocked.host).to.equal('localhost:9001') 202 expect(block.blockedAccount.host).to.equal('localhost:9001')
203 } 203 }
204 }) 204 })
205 205
@@ -267,7 +267,7 @@ describe('Test accounts blocklist', function () {
267 const block = blocks[0] 267 const block = blocks[0]
268 expect(block.byAccount.displayName).to.equal('root') 268 expect(block.byAccount.displayName).to.equal('root')
269 expect(block.byAccount.name).to.equal('root') 269 expect(block.byAccount.name).to.equal('root')
270 expect(block.serverBlocked.host).to.equal('localhost:9002') 270 expect(block.blockedServer.host).to.equal('localhost:9002')
271 }) 271 })
272 272
273 it('Should unblock the remote server', async function () { 273 it('Should unblock the remote server', async function () {
diff --git a/shared/models/blocklist/account-block.model.ts b/shared/models/blocklist/account-block.model.ts
index d6f8840c5..a942ed614 100644
--- a/shared/models/blocklist/account-block.model.ts
+++ b/shared/models/blocklist/account-block.model.ts
@@ -2,6 +2,6 @@ import { Account } from '../actors'
2 2
3export interface AccountBlock { 3export interface AccountBlock {
4 byAccount: Account 4 byAccount: Account
5 accountBlocked: Account 5 blockedAccount: Account
6 createdAt: Date | string 6 createdAt: Date | string
7} 7}
diff --git a/shared/models/blocklist/server-block.model.ts b/shared/models/blocklist/server-block.model.ts
index efba672bd..a8b8af0b7 100644
--- a/shared/models/blocklist/server-block.model.ts
+++ b/shared/models/blocklist/server-block.model.ts
@@ -2,7 +2,7 @@ import { Account } from '../actors'
2 2
3export interface ServerBlock { 3export interface ServerBlock {
4 byAccount: Account 4 byAccount: Account
5 serverBlocked: { 5 blockedServer: {
6 host: string 6 host: string
7 } 7 }
8 createdAt: Date | string 8 createdAt: Date | string