mirror of https://github.com/Chocobozzz/PeerTube
Handle rejected follows in client
Also add quick filters so it's easier to find pending followspull/5163/head
parent
3267d381f4
commit
073deef886
|
@ -13,7 +13,7 @@
|
||||||
<ng-template pTemplate="caption">
|
<ng-template pTemplate="caption">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<div class="ms-auto">
|
<div class="ms-auto">
|
||||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
<my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -31,12 +31,10 @@
|
||||||
<ng-template pTemplate="body" let-follow>
|
<ng-template pTemplate="body" let-follow>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="action-cell">
|
<td class="action-cell">
|
||||||
<ng-container *ngIf="follow.state === 'pending'">
|
<my-button *ngIf="follow.state !== 'accepted'" i18n-title title="Accept" icon="tick" (click)="acceptFollower(follow)"></my-button>
|
||||||
<my-button i18n-title title="Accept" icon="tick" (click)="acceptFollower(follow)"></my-button>
|
<my-button *ngIf="follow.state !== 'rejected'" i18n-title title="Refuse" icon="cross" (click)="rejectFollower(follow)"></my-button>
|
||||||
<my-button i18n-title title="Refuse" icon="cross" (click)="rejectFollower(follow)"></my-button>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<my-delete-button label *ngIf="follow.state === 'accepted'" (click)="deleteFollower(follow)"></my-delete-button>
|
<my-delete-button *ngIf="follow.state === 'rejected'" (click)="deleteFollower(follow)"></my-delete-button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer">
|
<a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer">
|
||||||
|
@ -45,11 +43,10 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td *ngIf="follow.state === 'accepted'">
|
<td>
|
||||||
<span class="pt-badge badge-green" i18n>Accepted</span>
|
<span *ngIf="follow.state === 'accepted'" class="pt-badge badge-green" i18n>Accepted</span>
|
||||||
</td>
|
<span *ngIf="follow.state === 'pending'" class="pt-badge badge-yellow" i18n>Pending</span>
|
||||||
<td *ngIf="follow.state === 'pending'">
|
<span *ngIf="follow.state === 'rejected'" class="pt-badge badge-red" i18n>Rejected</span>
|
||||||
<span class="pt-badge badge-yellow" i18n>Pending</span>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ follow.score }}</td>
|
<td>{{ follow.score }}</td>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
|
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { InstanceFollowService } from '@app/shared/shared-instance'
|
import { InstanceFollowService } from '@app/shared/shared-instance'
|
||||||
import { ActorFollow } from '@shared/models'
|
import { ActorFollow } from '@shared/models'
|
||||||
|
|
||||||
|
@ -15,12 +16,16 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
sort: SortMeta = { field: 'createdAt', order: -1 }
|
sort: SortMeta = { field: 'createdAt', order: -1 }
|
||||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
|
searchFilters: AdvancedInputFilter[]
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private followService: InstanceFollowService
|
private followService: InstanceFollowService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
this.searchFilters = this.followService.buildFollowsListFilters()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
@ -70,7 +75,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFollower (follow: ActorFollow) {
|
async deleteFollower (follow: ActorFollow) {
|
||||||
const message = $localize`Do you really want to delete this follower?`
|
const message = $localize`Do you really want to delete this follower? It will be able to send again another follow request.`
|
||||||
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
||||||
if (res === false) return
|
if (res === false) return
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ms-auto">
|
<div class="ms-auto">
|
||||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
<my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -47,11 +47,10 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td *ngIf="follow.state === 'accepted'">
|
<td>
|
||||||
<span class="pt-badge badge-green" i18n>Accepted</span>
|
<span *ngIf="follow.state === 'accepted'" class="pt-badge badge-green" i18n>Accepted</span>
|
||||||
</td>
|
<span *ngIf="follow.state === 'pending'" class="pt-badge badge-yellow" i18n>Pending</span>
|
||||||
<td *ngIf="follow.state === 'pending'">
|
<span *ngIf="follow.state === 'rejected'" class="pt-badge badge-red" i18n>Rejected</span>
|
||||||
<span class="pt-badge badge-yellow" i18n>Pending</span>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ follow.createdAt | date: 'short' }}</td>
|
<td>{{ follow.createdAt | date: 'short' }}</td>
|
||||||
|
@ -66,7 +65,7 @@
|
||||||
|
|
||||||
<ng-template pTemplate="emptymessage">
|
<ng-template pTemplate="emptymessage">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">
|
<td colspan="5">
|
||||||
<div class="no-results">
|
<div class="no-results">
|
||||||
<ng-container *ngIf="search" i18n>No host found matching current filters.</ng-container>
|
<ng-container *ngIf="search" i18n>No host found matching current filters.</ng-container>
|
||||||
<ng-container *ngIf="!search" i18n>Your instance is not following anyone.</ng-container>
|
<ng-container *ngIf="!search" i18n>Your instance is not following anyone.</ng-container>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
|
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { InstanceFollowService } from '@app/shared/shared-instance'
|
import { InstanceFollowService } from '@app/shared/shared-instance'
|
||||||
import { ActorFollow } from '@shared/models'
|
import { ActorFollow } from '@shared/models'
|
||||||
import { FollowModalComponent } from './follow-modal.component'
|
import { FollowModalComponent } from './follow-modal.component'
|
||||||
|
@ -17,12 +18,16 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
||||||
sort: SortMeta = { field: 'createdAt', order: -1 }
|
sort: SortMeta = { field: 'createdAt', order: -1 }
|
||||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
|
searchFilters: AdvancedInputFilter[]
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private followService: InstanceFollowService
|
private followService: InstanceFollowService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
this.searchFilters = this.followService.buildFollowsListFilters()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||||
import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
|
import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
import { AdvancedInputFilter } from '../shared-forms'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InstanceFollowService {
|
export class InstanceFollowService {
|
||||||
|
@ -30,7 +31,10 @@ export class InstanceFollowService {
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
params = this.restService.addRestGetParams(params, pagination, sort)
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
if (search) params = params.append('search', search)
|
if (search) {
|
||||||
|
params = this.restService.addObjectParams(params, this.parseFollowsListFilters(search))
|
||||||
|
}
|
||||||
|
|
||||||
if (state) params = params.append('state', state)
|
if (state) params = params.append('state', state)
|
||||||
if (actorType) params = params.append('actorType', actorType)
|
if (actorType) params = params.append('actorType', actorType)
|
||||||
|
|
||||||
|
@ -53,7 +57,10 @@ export class InstanceFollowService {
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
params = this.restService.addRestGetParams(params, pagination, sort)
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
if (search) params = params.append('search', search)
|
if (search) {
|
||||||
|
params = this.restService.addObjectParams(params, this.parseFollowsListFilters(search))
|
||||||
|
}
|
||||||
|
|
||||||
if (state) params = params.append('state', state)
|
if (state) params = params.append('state', state)
|
||||||
if (actorType) params = params.append('actorType', actorType)
|
if (actorType) params = params.append('actorType', actorType)
|
||||||
|
|
||||||
|
@ -101,4 +108,34 @@ export class InstanceFollowService {
|
||||||
return this.authHttp.delete(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}`)
|
return this.authHttp.delete(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}`)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFollowsListFilters (): AdvancedInputFilter[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: $localize`Advanced filters`,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'state:accepted',
|
||||||
|
label: $localize`Accepted follows`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'state:rejected',
|
||||||
|
label: $localize`Rejected follows`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'state:pending',
|
||||||
|
label: $localize`Pending follows`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseFollowsListFilters (search: string) {
|
||||||
|
return this.restService.parseQueryStringFilter(search, {
|
||||||
|
state: {
|
||||||
|
prefix: 'state:'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { Transaction } from 'sequelize/types'
|
||||||
|
import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
|
||||||
|
import { AccountModel } from '@server/models/account/account'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { ActivityFollow } from '../../../../shared/models/activitypub'
|
import { ActivityFollow } from '../../../../shared/models/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
|
@ -8,7 +11,7 @@ import { getAPId } from '../../../lib/activitypub/activity'
|
||||||
import { ActorModel } from '../../../models/actor/actor'
|
import { ActorModel } from '../../../models/actor/actor'
|
||||||
import { ActorFollowModel } from '../../../models/actor/actor-follow'
|
import { ActorFollowModel } from '../../../models/actor/actor-follow'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorFollowActors, MActorSignature } from '../../../types/models'
|
import { MActorFollow, MActorFull, MActorId, MActorSignature } from '../../../types/models'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { autoFollowBackIfNeeded } from '../follow'
|
import { autoFollowBackIfNeeded } from '../follow'
|
||||||
import { sendAccept, sendReject } from '../send'
|
import { sendAccept, sendReject } from '../send'
|
||||||
|
@ -31,22 +34,14 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processFollow (byActor: MActorSignature, activityId: string, targetActorURL: string) {
|
async function processFollow (byActor: MActorSignature, activityId: string, targetActorURL: string) {
|
||||||
const { actorFollow, created, isFollowingInstance, targetActor } = await sequelizeTypescript.transaction(async t => {
|
const { actorFollow, created, targetActor } = await sequelizeTypescript.transaction(async t => {
|
||||||
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
||||||
|
|
||||||
if (!targetActor) throw new Error('Unknown actor')
|
if (!targetActor) throw new Error('Unknown actor')
|
||||||
if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
|
if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
if (rejectIfInstanceFollowDisabled(byActor, activityId, targetActor)) return { actorFollow: undefined }
|
||||||
const isFollowingInstance = targetActor.id === serverActor.id
|
if (await rejectIfMuted(byActor, activityId, targetActor)) return { actorFollow: undefined }
|
||||||
|
|
||||||
if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
|
||||||
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
|
||||||
|
|
||||||
sendReject(activityId, byActor, targetActor)
|
|
||||||
|
|
||||||
return { actorFollow: undefined as MActorFollowActors }
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ actorFollow, created ] = await ActorFollowModel.findOrCreateCustom({
|
const [ actorFollow, created ] = await ActorFollowModel.findOrCreateCustom({
|
||||||
byActor,
|
byActor,
|
||||||
|
@ -58,24 +53,11 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
||||||
transaction: t
|
transaction: t
|
||||||
})
|
})
|
||||||
|
|
||||||
// Already rejected
|
if (rejectIfAlreadyRejected(actorFollow, byActor, activityId, targetActor)) return { actorFollow: undefined }
|
||||||
if (actorFollow.state === 'rejected') {
|
|
||||||
return { actorFollow: undefined as MActorFollowActors }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the follow as accepted if the remote actor follows a channel or account
|
await acceptIfNeeded(actorFollow, targetActor, t)
|
||||||
// Or if the instance automatically accepts followers
|
|
||||||
if (actorFollow.state !== 'accepted' && (isFollowingInstance === false || CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false)) {
|
|
||||||
actorFollow.state = 'accepted'
|
|
||||||
|
|
||||||
await actorFollow.save({ transaction: t })
|
await fixFollowURLIfNeeded(actorFollow, activityId, t)
|
||||||
}
|
|
||||||
|
|
||||||
// Before PeerTube V3 we did not save the follow ID. Try to fix these old follows
|
|
||||||
if (!actorFollow.url) {
|
|
||||||
actorFollow.url = activityId
|
|
||||||
await actorFollow.save({ transaction: t })
|
|
||||||
}
|
|
||||||
|
|
||||||
actorFollow.ActorFollower = byActor
|
actorFollow.ActorFollower = byActor
|
||||||
actorFollow.ActorFollowing = targetActor
|
actorFollow.ActorFollowing = targetActor
|
||||||
|
@ -87,7 +69,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
||||||
await autoFollowBackIfNeeded(actorFollow, t)
|
await autoFollowBackIfNeeded(actorFollow, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { actorFollow, created, isFollowingInstance, targetActor }
|
return { actorFollow, created, targetActor }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Rejected
|
// Rejected
|
||||||
|
@ -97,7 +79,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
||||||
const follower = await ActorModel.loadFull(byActor.id)
|
const follower = await ActorModel.loadFull(byActor.id)
|
||||||
const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower })
|
const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower })
|
||||||
|
|
||||||
if (isFollowingInstance) {
|
if (isFollowingInstance(targetActor)) {
|
||||||
Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull)
|
Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull)
|
||||||
} else {
|
} else {
|
||||||
Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
|
Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
|
||||||
|
@ -106,3 +88,69 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
||||||
|
|
||||||
logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
|
logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rejectIfInstanceFollowDisabled (byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
|
||||||
|
if (isFollowingInstance(targetActor) && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
||||||
|
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
||||||
|
|
||||||
|
sendReject(activityId, byActor, targetActor)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rejectIfMuted (byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
|
||||||
|
const followerAccount = await AccountModel.load(byActor.Account.id)
|
||||||
|
const followingAccountId = targetActor.Account
|
||||||
|
|
||||||
|
if (followerAccount && await isBlockedByServerOrAccount(followerAccount, followingAccountId)) {
|
||||||
|
logger.info('Rejecting %s because follower is muted.', byActor.url)
|
||||||
|
|
||||||
|
sendReject(activityId, byActor, targetActor)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejectIfAlreadyRejected (actorFollow: MActorFollow, byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
|
||||||
|
// Already rejected
|
||||||
|
if (actorFollow.state === 'rejected') {
|
||||||
|
logger.info('Rejecting %s because follow is already rejected.', byActor.url)
|
||||||
|
|
||||||
|
sendReject(activityId, byActor, targetActor)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function acceptIfNeeded (actorFollow: MActorFollow, targetActor: MActorFull, transaction: Transaction) {
|
||||||
|
// Set the follow as accepted if the remote actor follows a channel or account
|
||||||
|
// Or if the instance automatically accepts followers
|
||||||
|
if (actorFollow.state === 'accepted') return
|
||||||
|
if (!isFollowingInstance(targetActor)) return
|
||||||
|
if (CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === true) return
|
||||||
|
|
||||||
|
actorFollow.state = 'accepted'
|
||||||
|
|
||||||
|
await actorFollow.save({ transaction })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixFollowURLIfNeeded (actorFollow: MActorFollow, activityId: string, transaction: Transaction) {
|
||||||
|
// Before PeerTube V3 we did not save the follow ID. Try to fix these old follows
|
||||||
|
if (!actorFollow.url) {
|
||||||
|
actorFollow.url = activityId
|
||||||
|
await actorFollow.save({ transaction })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isFollowingInstance (targetActor: MActorId) {
|
||||||
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
|
return targetActor.id === serverActor.id
|
||||||
|
}
|
||||||
|
|
|
@ -33,42 +33,39 @@ async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state =
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkFollows (options: {
|
async function checkFollows (options: {
|
||||||
follower: {
|
follower: PeerTubeServer
|
||||||
server: PeerTubeServer
|
followerState: FollowState | 'deleted'
|
||||||
state?: FollowState // if not provided, it means it does not exist
|
|
||||||
}
|
|
||||||
following: {
|
|
||||||
server: PeerTubeServer
|
|
||||||
state?: FollowState // if not provided, it means it does not exist
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
const { follower, following } = options
|
|
||||||
|
|
||||||
const followerUrl = follower.server.url + '/accounts/peertube'
|
following: PeerTubeServer
|
||||||
const followingUrl = following.server.url + '/accounts/peertube'
|
followingState: FollowState | 'deleted'
|
||||||
|
}) {
|
||||||
|
const { follower, followerState, followingState, following } = options
|
||||||
|
|
||||||
|
const followerUrl = follower.url + '/accounts/peertube'
|
||||||
|
const followingUrl = following.url + '/accounts/peertube'
|
||||||
const finder = (d: ActorFollow) => d.follower.url === followerUrl && d.following.url === followingUrl
|
const finder = (d: ActorFollow) => d.follower.url === followerUrl && d.following.url === followingUrl
|
||||||
|
|
||||||
{
|
{
|
||||||
const { data } = await follower.server.follows.getFollowings()
|
const { data } = await follower.follows.getFollowings()
|
||||||
const follow = data.find(finder)
|
const follow = data.find(finder)
|
||||||
|
|
||||||
if (!follower.state) {
|
if (followerState === 'deleted') {
|
||||||
expect(follow).to.not.exist
|
expect(follow).to.not.exist
|
||||||
} else {
|
} else {
|
||||||
expect(follow.state).to.equal(follower.state)
|
expect(follow.state).to.equal(followerState)
|
||||||
expect(follow.follower.url).to.equal(followerUrl)
|
expect(follow.follower.url).to.equal(followerUrl)
|
||||||
expect(follow.following.url).to.equal(followingUrl)
|
expect(follow.following.url).to.equal(followingUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const { data } = await following.server.follows.getFollowers()
|
const { data } = await following.follows.getFollowers()
|
||||||
const follow = data.find(finder)
|
const follow = data.find(finder)
|
||||||
|
|
||||||
if (!following.state) {
|
if (followingState === 'deleted') {
|
||||||
expect(follow).to.not.exist
|
expect(follow).to.not.exist
|
||||||
} else {
|
} else {
|
||||||
expect(follow.state).to.equal(following.state)
|
expect(follow.state).to.equal(followingState)
|
||||||
expect(follow.follower.url).to.equal(followerUrl)
|
expect(follow.follower.url).to.equal(followerUrl)
|
||||||
expect(follow.following.url).to.equal(followingUrl)
|
expect(follow.following.url).to.equal(followingUrl)
|
||||||
}
|
}
|
||||||
|
@ -256,14 +253,10 @@ describe('Test follows moderation', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkFollows({
|
await checkFollows({
|
||||||
follower: {
|
follower: servers[0],
|
||||||
server: servers[0],
|
followerState: 'rejected',
|
||||||
state: 'rejected'
|
following: servers[2],
|
||||||
},
|
followingState: 'rejected'
|
||||||
following: {
|
|
||||||
server: servers[2],
|
|
||||||
state: 'rejected'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,13 +272,10 @@ describe('Test follows moderation', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkFollows({
|
await checkFollows({
|
||||||
follower: {
|
follower: servers[0],
|
||||||
server: servers[0]
|
followerState: 'deleted',
|
||||||
},
|
following: servers[2],
|
||||||
following: {
|
followingState: 'rejected'
|
||||||
server: servers[2],
|
|
||||||
state: 'rejected'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -297,14 +287,10 @@ describe('Test follows moderation', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkFollows({
|
await checkFollows({
|
||||||
follower: {
|
follower: servers[0],
|
||||||
server: servers[0],
|
followerState: 'pending',
|
||||||
state: 'pending'
|
following: servers[2],
|
||||||
},
|
followingState: 'pending'
|
||||||
following: {
|
|
||||||
server: servers[2],
|
|
||||||
state: 'pending'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -313,14 +299,10 @@ describe('Test follows moderation', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkFollows({
|
await checkFollows({
|
||||||
follower: {
|
follower: servers[0],
|
||||||
server: servers[0],
|
followerState: 'rejected',
|
||||||
state: 'rejected'
|
following: servers[1],
|
||||||
},
|
followingState: 'rejected'
|
||||||
following: {
|
|
||||||
server: servers[1],
|
|
||||||
state: 'rejected'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -329,19 +311,36 @@ describe('Test follows moderation', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkFollows({
|
await checkFollows({
|
||||||
follower: {
|
follower: servers[0],
|
||||||
server: servers[0],
|
followerState: 'accepted',
|
||||||
state: 'accepted'
|
following: servers[1],
|
||||||
},
|
followingState: 'accepted'
|
||||||
following: {
|
|
||||||
server: servers[1],
|
|
||||||
state: 'accepted'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should ignore follow requests of muted servers', async function () {
|
it('Should ignore follow requests of muted servers', async function () {
|
||||||
|
await servers[1].blocklist.addToServerBlocklist({ server: servers[0].host })
|
||||||
|
|
||||||
|
await commands[0].unfollow({ target: servers[1] })
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: servers[0],
|
||||||
|
followerState: 'deleted',
|
||||||
|
following: servers[1],
|
||||||
|
followingState: 'deleted'
|
||||||
|
})
|
||||||
|
|
||||||
|
await commands[0].follow({ hosts: [ servers[1].host ] })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: servers[0],
|
||||||
|
followerState: 'rejected',
|
||||||
|
following: servers[1],
|
||||||
|
followingState: 'deleted'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
Loading…
Reference in New Issue