mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 01:39:37 +02:00
Add ability to disable channel followers
This commit is contained in:
parent
ce28c64750
commit
031b61c466
17 changed files with 274 additions and 151 deletions
|
@ -453,6 +453,102 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-two-cols mt-4">
|
||||
<div class="title-col">
|
||||
<h2 i18n>FEDERATION</h2>
|
||||
<div i18n class="inner-form-description">
|
||||
Manage <a class="link-primary" routerLink="/admin/settings/follows">relations</a> with other platforms.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-col">
|
||||
|
||||
<ng-container formGroupName="followers">
|
||||
<ng-container formGroupName="channels">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followersChannelsEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Remote actors can follow channels of your platform"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<span i18n>This setting is not retroactive: current followers of channels of your platform will not be affected</span>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="instance">
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followersInstanceEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Remote actors can follow your platform"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<span i18n>This setting is not retroactive: current followers of your platform will not be affected</span>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followersInstanceManualApproval" formControlName="manualApproval"
|
||||
i18n-labelText labelText="Manually approve new followers that follow your platform"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="followings">
|
||||
<ng-container formGroupName="instance">
|
||||
|
||||
<ng-container formGroupName="autoFollowBack">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followingsInstanceAutoFollowBackEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Automatically follow back followers that follow your platform"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<span i18n>⚠️ This functionality requires a lot of attention and extra moderation</span>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="autoFollowIndex">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followingsInstanceAutoFollowIndexEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Automatically follow platforms of a public index"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<div i18n>⚠️ This functionality requires a lot of attention and extra moderation.</div>
|
||||
|
||||
<span i18n>
|
||||
See <a class="link-primary" href="https://docs.joinpeertube.org/admin/following-instances#automatically-follow-other-instances" rel="noopener noreferrer" target="_blank">the documentation</a> for more information about the expected URL
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container ngProjectAs="extra">
|
||||
<div [ngClass]="{ 'disabled-checkbox-extra': !isAutoFollowIndexEnabled() }">
|
||||
<label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label>
|
||||
<input
|
||||
type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control"
|
||||
formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors.followings.instance.autoFollowIndex.indexUrl }"
|
||||
>
|
||||
<div *ngIf="formErrors.followings.instance.autoFollowIndex.indexUrl" class="form-error" role="alert">{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-two-cols mt-4">
|
||||
<div class="title-col">
|
||||
<h2 i18n>PLAYER</h2>
|
||||
|
@ -651,83 +747,4 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-two-cols mt-4">
|
||||
<div class="title-col">
|
||||
<h2 i18n>FEDERATION</h2>
|
||||
<div i18n class="inner-form-description">
|
||||
Manage <a class="link-primary" routerLink="/admin/settings/follows">relations</a> with other platforms.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-col">
|
||||
|
||||
<ng-container formGroupName="followers">
|
||||
<ng-container formGroupName="instance">
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followersInstanceEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Remote actors can follow your platform"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followersInstanceManualApproval" formControlName="manualApproval"
|
||||
i18n-labelText labelText="Manually approve new followers that follow your platform"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="followings">
|
||||
<ng-container formGroupName="instance">
|
||||
|
||||
<ng-container formGroupName="autoFollowBack">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followingsInstanceAutoFollowBackEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Automatically follow back followers that follow your platform"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<span i18n>⚠️ This functionality requires a lot of attention and extra moderation</span>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="autoFollowIndex">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="followingsInstanceAutoFollowIndexEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Automatically follow platforms of a public index"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<div i18n>⚠️ This functionality requires a lot of attention and extra moderation.</div>
|
||||
|
||||
<span i18n>
|
||||
See <a class="link-primary" href="https://docs.joinpeertube.org/admin/following-instances#automatically-follow-other-instances" rel="noopener noreferrer" target="_blank">the documentation</a> for more information about the expected URL
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container ngProjectAs="extra">
|
||||
<div [ngClass]="{ 'disabled-checkbox-extra': !isAutoFollowIndexEnabled() }">
|
||||
<label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label>
|
||||
<input
|
||||
type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control"
|
||||
formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors.followings.instance.autoFollowIndex.indexUrl }"
|
||||
>
|
||||
<div *ngIf="formErrors.followings.instance.autoFollowIndex.indexUrl" class="form-error" role="alert">{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -136,6 +136,9 @@ type Form = {
|
|||
enabled: FormControl<boolean>
|
||||
manualApproval: FormControl<boolean>
|
||||
}>
|
||||
channels: FormGroup<{
|
||||
enabled: FormControl<boolean>
|
||||
}>
|
||||
}>
|
||||
|
||||
followings: FormGroup<{
|
||||
|
@ -367,6 +370,9 @@ export class AdminConfigGeneralComponent implements OnInit, OnDestroy, CanCompon
|
|||
instance: {
|
||||
enabled: null,
|
||||
manualApproval: null
|
||||
},
|
||||
channels: {
|
||||
enabled: null
|
||||
}
|
||||
},
|
||||
followings: {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<cdk-step i18n-label label="Configuration preview">
|
||||
<my-admin-config-wizard-preview
|
||||
[usageType]="usageType" [instanceInfo]="instanceInfo"
|
||||
[usageType]="usageType" [instanceInfo]="instanceInfo" [dryRun]="dryRun"
|
||||
[currentStep]="currentStep()" totalSteps="3"
|
||||
(back)="stepper.previous()" (next)="showWelcome ? stepper.next() : hide()" (hide)="hide()"
|
||||
></my-admin-config-wizard-preview>
|
||||
|
|
|
@ -39,9 +39,11 @@ export class AdminConfigWizardModalComponent implements OnInit {
|
|||
readonly created = output()
|
||||
|
||||
usageType: UsageType
|
||||
showWelcome: boolean
|
||||
instanceInfo: FormInfo
|
||||
|
||||
showWelcome: boolean
|
||||
dryRun: boolean
|
||||
|
||||
ngOnInit () {
|
||||
this.created.emit()
|
||||
}
|
||||
|
@ -49,6 +51,7 @@ export class AdminConfigWizardModalComponent implements OnInit {
|
|||
shouldAutoOpen (user: User) {
|
||||
if (this.modalService.hasOpenModals()) return false
|
||||
if (this.route.snapshot.fragment === 'admin-welcome-wizard') return true
|
||||
if (this.route.snapshot.fragment === 'admin-welcome-wizard-test') return true
|
||||
if (user.noWelcomeModal === true) return false
|
||||
if (peertubeLocalStorage.getItem(getNoWelcomeModalLocalStorageKey()) === 'true') return false
|
||||
|
||||
|
@ -57,6 +60,7 @@ export class AdminConfigWizardModalComponent implements OnInit {
|
|||
|
||||
show ({ showWelcome }: { showWelcome: boolean }) {
|
||||
this.showWelcome = showWelcome
|
||||
this.dryRun = this.route.snapshot.fragment === 'admin-welcome-wizard-test'
|
||||
|
||||
this.modalService.open(this.modal(), {
|
||||
centered: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { CdkStepperModule } from '@angular/cdk/stepper'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Component, inject, input, numberAttribute, OnChanges, output } from '@angular/core'
|
||||
import { booleanAttribute, Component, inject, input, numberAttribute, OnChanges, output } from '@angular/core'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { HtmlRendererService, Notifier, ServerService } from '@app/core'
|
||||
import { AdminConfigService } from '@app/shared/shared-admin/admin-config.service'
|
||||
|
@ -39,6 +39,7 @@ export class AdminConfigWizardPreviewComponent implements OnChanges {
|
|||
readonly totalSteps = input.required({ transform: numberAttribute })
|
||||
readonly usageType = input.required<UsageType>()
|
||||
readonly instanceInfo = input.required<FormInfo>()
|
||||
readonly dryRun = input.required({ transform: booleanAttribute })
|
||||
|
||||
readonly back = output()
|
||||
readonly next = output()
|
||||
|
@ -76,6 +77,10 @@ export class AdminConfigWizardPreviewComponent implements OnChanges {
|
|||
}
|
||||
|
||||
confirm () {
|
||||
if (this.dryRun()) {
|
||||
return this.next.emit()
|
||||
}
|
||||
|
||||
this.updating = true
|
||||
|
||||
this.adminConfig.updateCustomConfig(this.config)
|
||||
|
|
|
@ -3,7 +3,7 @@ import { CustomConfig, VideoCommentPolicy, VideoPrivacy } from '@peertube/peertu
|
|||
import { AttributesOnly } from '@peertube/peertube-typescript-utils'
|
||||
import { getBytes } from '@root-helpers/bytes'
|
||||
import merge from 'lodash-es/merge'
|
||||
import { PartialDeep } from 'type-fest'
|
||||
import { Jsonify, PartialDeep } from 'type-fest'
|
||||
|
||||
export type RegistrationType = 'open' | 'closed' | 'approval'
|
||||
export type EnabledDisabled = 'disabled' | 'enabled'
|
||||
|
@ -29,25 +29,32 @@ export class UsageType {
|
|||
private config: PartialDeep<CustomConfig> = {}
|
||||
private plugins: string[] = []
|
||||
|
||||
private constructor () {
|
||||
private constructor (options: Required<Jsonify<UsageType>>) {
|
||||
for (const [ key, value ] of Object.entries(options)) {
|
||||
;(this as any)[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
static initForCommunity () {
|
||||
const usageType = new UsageType()
|
||||
const usageType = new UsageType({
|
||||
registration: 'approval',
|
||||
remoteImport: 'disabled',
|
||||
live: 'enabled',
|
||||
videoQuota: 5 * 1024 * 1024 * 1024, // Default to 5GB,
|
||||
globalSearch: 'enabled',
|
||||
|
||||
usageType.registration = 'approval'
|
||||
usageType.remoteImport = 'disabled'
|
||||
usageType.live = 'enabled'
|
||||
usageType.videoQuota = 5 * 1024 * 1024 * 1024 // Default to 5GB
|
||||
usageType.globalSearch = 'enabled'
|
||||
defaultPrivacy: VideoPrivacy.PUBLIC,
|
||||
p2p: 'enabled',
|
||||
federation: 'enabled',
|
||||
keepOriginalVideo: 'disabled',
|
||||
allowReplaceFile: 'disabled',
|
||||
|
||||
usageType.defaultPrivacy = VideoPrivacy.PUBLIC
|
||||
usageType.p2p = 'enabled'
|
||||
usageType.federation = 'enabled'
|
||||
usageType.keepOriginalVideo = 'disabled'
|
||||
usageType.allowReplaceFile = 'disabled'
|
||||
|
||||
// Use current config for: defaultCommentPolicy, authType, preferDisplayName and transcription
|
||||
// Use current config
|
||||
defaultCommentPolicy: undefined,
|
||||
authType: undefined,
|
||||
preferDisplayName: undefined,
|
||||
transcription: undefined
|
||||
})
|
||||
|
||||
usageType.compute()
|
||||
|
||||
|
@ -55,22 +62,25 @@ export class UsageType {
|
|||
}
|
||||
|
||||
static initForPrivateInstance () {
|
||||
const usageType = new UsageType()
|
||||
const usageType = new UsageType({
|
||||
registration: 'closed',
|
||||
remoteImport: 'enabled',
|
||||
live: 'enabled',
|
||||
videoQuota: -1,
|
||||
globalSearch: 'disabled',
|
||||
|
||||
usageType.registration = 'closed'
|
||||
usageType.remoteImport = 'enabled'
|
||||
usageType.live = 'enabled'
|
||||
usageType.videoQuota = -1
|
||||
usageType.globalSearch = 'disabled'
|
||||
defaultPrivacy: VideoPrivacy.INTERNAL,
|
||||
p2p: 'disabled',
|
||||
federation: 'disabled',
|
||||
keepOriginalVideo: 'enabled',
|
||||
allowReplaceFile: 'enabled',
|
||||
preferDisplayName: 'enabled',
|
||||
|
||||
usageType.defaultPrivacy = VideoPrivacy.INTERNAL
|
||||
usageType.p2p = 'disabled'
|
||||
usageType.federation = 'disabled'
|
||||
usageType.keepOriginalVideo = 'enabled'
|
||||
usageType.allowReplaceFile = 'enabled'
|
||||
usageType.preferDisplayName = 'enabled'
|
||||
|
||||
// Use current config for: defaultCommentPolicy, authType and transcription
|
||||
// Use current config
|
||||
defaultCommentPolicy: undefined,
|
||||
authType: undefined,
|
||||
transcription: undefined
|
||||
})
|
||||
|
||||
usageType.compute()
|
||||
|
||||
|
@ -78,26 +88,27 @@ export class UsageType {
|
|||
}
|
||||
|
||||
static initForInstitution () {
|
||||
const usageType = new UsageType()
|
||||
const usageType = new UsageType({
|
||||
registration: 'closed',
|
||||
remoteImport: 'enabled',
|
||||
live: 'enabled',
|
||||
videoQuota: -1,
|
||||
globalSearch: 'disabled',
|
||||
|
||||
usageType.registration = 'closed'
|
||||
usageType.remoteImport = 'enabled'
|
||||
usageType.live = 'enabled'
|
||||
usageType.videoQuota = -1
|
||||
usageType.globalSearch = 'disabled'
|
||||
defaultPrivacy: VideoPrivacy.PUBLIC,
|
||||
p2p: 'disabled',
|
||||
keepOriginalVideo: 'enabled',
|
||||
allowReplaceFile: 'enabled',
|
||||
preferDisplayName: 'enabled',
|
||||
|
||||
usageType.defaultPrivacy = VideoPrivacy.PUBLIC
|
||||
usageType.p2p = 'disabled'
|
||||
usageType.keepOriginalVideo = 'enabled'
|
||||
usageType.allowReplaceFile = 'enabled'
|
||||
usageType.preferDisplayName = 'enabled'
|
||||
authType: 'local',
|
||||
transcription: 'enabled',
|
||||
|
||||
usageType.authType = 'local'
|
||||
usageType.transcription = 'enabled'
|
||||
defaultCommentPolicy: VideoCommentPolicy.REQUIRES_APPROVAL,
|
||||
|
||||
usageType.defaultCommentPolicy = VideoCommentPolicy.REQUIRES_APPROVAL
|
||||
|
||||
// Use current config for: federation
|
||||
// Use current config
|
||||
federation: undefined
|
||||
})
|
||||
|
||||
usageType.compute()
|
||||
|
||||
|
@ -322,6 +333,9 @@ export class UsageType {
|
|||
followers: {
|
||||
instance: {
|
||||
enabled: this.federation === 'enabled'
|
||||
},
|
||||
channels: {
|
||||
enabled: this.federation === 'enabled'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -977,10 +977,15 @@ services:
|
|||
|
||||
followers:
|
||||
instance:
|
||||
# Allow or not other instances to follow yours
|
||||
# Allow remote actors to follow your instance
|
||||
# This setting is not retroactive: current followers of your instance will not be affected
|
||||
enabled: true
|
||||
# Whether or not an administrator must manually validate a new follower
|
||||
manual_approval: false
|
||||
channels:
|
||||
# Allow remote actors to follow channels/accounts of your instance
|
||||
# This setting is not retroactive: current followers of local channels/accounts will not be affected
|
||||
enabled: true
|
||||
|
||||
followings:
|
||||
instance:
|
||||
|
|
|
@ -987,10 +987,15 @@ services:
|
|||
|
||||
followers:
|
||||
instance:
|
||||
# Allow or not other instances to follow yours
|
||||
# Allow remote actors to follow your instance
|
||||
# This setting is not retroactive: current followers of your instance will not be affected
|
||||
enabled: true
|
||||
# Whether or not an administrator must manually validate a new follower
|
||||
manual_approval: false
|
||||
channels:
|
||||
# Allow remote actors to follow channels/accounts of your instance
|
||||
# This setting is not retroactive: current followers of local channels/accounts will not be affected
|
||||
enabled: true
|
||||
|
||||
followings:
|
||||
instance:
|
||||
|
|
|
@ -292,6 +292,10 @@ export interface CustomConfig {
|
|||
enabled: boolean
|
||||
manualApproval: boolean
|
||||
}
|
||||
|
||||
channels: {
|
||||
enabled: boolean
|
||||
}
|
||||
}
|
||||
|
||||
followings: {
|
||||
|
|
|
@ -131,6 +131,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
|
|||
|
||||
expect(data.followers.instance.enabled).to.be.true
|
||||
expect(data.followers.instance.manualApproval).to.be.false
|
||||
expect(data.followers.channels.enabled).to.be.true
|
||||
|
||||
expect(data.followings.instance.autoFollowBack.enabled).to.be.false
|
||||
expect(data.followings.instance.autoFollowIndex.enabled).to.be.false
|
||||
|
@ -393,6 +394,9 @@ function buildNewCustomConfig (server: PeerTubeServer): CustomConfig {
|
|||
instance: {
|
||||
enabled: false,
|
||||
manualApproval: true
|
||||
},
|
||||
channels: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
followings: {
|
||||
|
|
|
@ -46,9 +46,7 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('With a followed instance', function () {
|
||||
|
||||
describe('With an unlogged user', function () {
|
||||
|
||||
it('Should get the local video', async function () {
|
||||
await servers[0].videos.get({ id: video1UUID })
|
||||
})
|
||||
|
@ -130,7 +128,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('With a non followed instance', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -138,7 +135,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('With an unlogged user', function () {
|
||||
|
||||
it('Should get the local video', async function () {
|
||||
await servers[0].videos.get({ id: video1UUID })
|
||||
})
|
||||
|
@ -196,7 +192,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('With a logged user', function () {
|
||||
|
||||
it('Should get the local video', async function () {
|
||||
await servers[0].videos.getWithToken({ token: userToken, id: video1UUID })
|
||||
})
|
||||
|
@ -238,7 +233,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('When following a remote account', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
|
@ -256,7 +250,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('When unfollowing a remote account', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
|
@ -277,7 +270,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('When following a remote channel', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
|
@ -295,7 +287,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('When unfollowing a remote channel', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
|
@ -316,7 +307,6 @@ describe('Test follow constraints', function () {
|
|||
})
|
||||
|
||||
describe('When disabling federation', function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60_000)
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import { ActorFollow, FollowState } from '@peertube/peertube-models'
|
||||
import {
|
||||
cleanupTests,
|
||||
|
@ -11,6 +9,8 @@ import {
|
|||
setAccessTokensToServers,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state = 'accepted') {
|
||||
const fns = [
|
||||
|
@ -97,7 +97,6 @@ describe('Test follows moderation', function () {
|
|||
})
|
||||
|
||||
describe('Default behaviour', function () {
|
||||
|
||||
it('Should have server 1 following server 2', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -121,8 +120,61 @@ describe('Test follows moderation', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Disabled/Enabled followers', function () {
|
||||
describe('Disabled/Enabled followers for the channel', function () {
|
||||
let channel: string
|
||||
|
||||
before(async function () {
|
||||
channel = `root_channel@${servers[1].host}`
|
||||
})
|
||||
|
||||
it('Should disable followers for channels on server 2', async function () {
|
||||
const subConfig = {
|
||||
followers: {
|
||||
channels: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await servers[1].config.updateExistingConfig({ newConfig: subConfig })
|
||||
|
||||
await commands[0].follow({ handles: [ channel ] })
|
||||
await waitJobs(servers)
|
||||
|
||||
for (const server of [ servers[0], servers[1] ]) {
|
||||
const { data, total } = await server.channels.listFollowers({ channelName: channel })
|
||||
expect(total).to.equal(0)
|
||||
expect(data).to.have.lengthOf(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('Should re enable followers for channels on server 2', async function () {
|
||||
const subConfig = {
|
||||
followers: {
|
||||
channels: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await servers[1].config.updateExistingConfig({ newConfig: subConfig })
|
||||
|
||||
await commands[0].follow({ handles: [ channel ] })
|
||||
await waitJobs(servers)
|
||||
|
||||
for (const server of [ servers[0], servers[1] ]) {
|
||||
const { data, total } = await server.channels.listFollowers({ channelName: channel })
|
||||
expect(total).to.equal(1)
|
||||
expect(data).to.have.lengthOf(1)
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await commands[0].unfollow({ target: channel })
|
||||
})
|
||||
})
|
||||
|
||||
describe('Disabled/Enabled followers for the instance', function () {
|
||||
it('Should disable followers on server 2', async function () {
|
||||
const subConfig = {
|
||||
followers: {
|
||||
|
@ -160,8 +212,7 @@ describe('Test follows moderation', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Manual approbation', function () {
|
||||
|
||||
describe('Manual approbation for the instance', function () {
|
||||
it('Should manually approve followers', async function () {
|
||||
this.timeout(20000)
|
||||
|
||||
|
@ -257,7 +308,6 @@ describe('Test follows moderation', function () {
|
|||
})
|
||||
|
||||
describe('Accept/reject state', function () {
|
||||
|
||||
it('Should not change the follow on refollow with and without auto accept', async function () {
|
||||
const run = async () => {
|
||||
await commands[0].follow({ hosts: [ servers[2].url ] })
|
||||
|
@ -331,7 +381,6 @@ describe('Test follows moderation', function () {
|
|||
})
|
||||
|
||||
describe('Muted servers', function () {
|
||||
|
||||
it('Should ignore follow requests of muted servers', async function () {
|
||||
await servers[1].blocklist.addToServerBlocklist({ server: servers[0].host })
|
||||
|
||||
|
|
|
@ -474,6 +474,9 @@ function customConfig (): CustomConfig {
|
|||
instance: {
|
||||
enabled: CONFIG.FOLLOWERS.INSTANCE.ENABLED,
|
||||
manualApproval: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL
|
||||
},
|
||||
channels: {
|
||||
enabled: CONFIG.FOLLOWERS.CHANNELS.ENABLED
|
||||
}
|
||||
},
|
||||
followings: {
|
||||
|
|
|
@ -146,6 +146,7 @@ export function checkMissedConfig () {
|
|||
'services.twitter.username',
|
||||
'followers.instance.enabled',
|
||||
'followers.instance.manual_approval',
|
||||
'followers.channels.enabled',
|
||||
'tracker.enabled',
|
||||
'tracker.private',
|
||||
'tracker.reject_too_many_announces',
|
||||
|
|
|
@ -974,6 +974,11 @@ const CONFIG = {
|
|||
get MANUAL_APPROVAL () {
|
||||
return config.get<boolean>('followers.instance.manual_approval')
|
||||
}
|
||||
},
|
||||
CHANNELS: {
|
||||
get ENABLED () {
|
||||
return config.get<boolean>('followers.channels.enabled')
|
||||
}
|
||||
}
|
||||
},
|
||||
FOLLOWINGS: {
|
||||
|
|
|
@ -90,12 +90,22 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
|||
}
|
||||
|
||||
async function rejectIfInstanceFollowDisabled (byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
|
||||
if (await isFollowingInstance(targetActor) && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
||||
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
||||
if (await isFollowingInstance(targetActor)) {
|
||||
if (CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
||||
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
||||
|
||||
sendReject(activityId, byActor, targetActor)
|
||||
sendReject(activityId, byActor, targetActor)
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if (CONFIG.FOLLOWERS.CHANNELS.ENABLED === false) {
|
||||
logger.info('Rejecting %s because channel followers are disabled.', targetActor.url)
|
||||
|
||||
sendReject(activityId, byActor, targetActor)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
@ -102,6 +102,7 @@ const customConfigUpdateValidator = [
|
|||
|
||||
body('followers.instance.enabled').isBoolean(),
|
||||
body('followers.instance.manualApproval').isBoolean(),
|
||||
body('followers.channels.enabled').isBoolean(),
|
||||
|
||||
body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue