1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-05 19:42:24 +02:00

Add defaults values config in web admin

This commit is contained in:
Chocobozzz 2025-06-13 15:36:19 +02:00
parent eb11e5793f
commit e9bb222b6c
No known key found for this signature in database
GPG key ID: 583A612D890159BE
9 changed files with 241 additions and 40 deletions

View file

@ -2,7 +2,7 @@ import { inject } from '@angular/core'
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, Routes } from '@angular/router' import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, Routes } from '@angular/router'
import { CanDeactivateGuard, ServerService, UserRightGuard } from '@app/core' import { CanDeactivateGuard, ServerService, UserRightGuard } from '@app/core'
import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service' import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service'
import { CustomConfig, UserRight, VideoConstant } from '@peertube/peertube-models' import { CustomConfig, UserRight, VideoCommentPolicyType, VideoConstant, VideoPrivacyType } from '@peertube/peertube-models'
import { map } from 'rxjs' import { map } from 'rxjs'
import { AdminConfigComponent } from './admin-config.component' import { AdminConfigComponent } from './admin-config.component'
import { import {
@ -33,6 +33,24 @@ export const languagesResolver: ResolveFn<VideoConstant<string>[]> = (_route: Ac
return inject(ServerService).getVideoLanguages() return inject(ServerService).getVideoLanguages()
} }
export const licencesResolver: ResolveFn<VideoConstant<number>[]> = (_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) => {
return inject(ServerService).getVideoLicences()
}
export const privaciesResolver: ResolveFn<VideoConstant<VideoPrivacyType>[]> = (
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot
) => {
return inject(ServerService).getVideoPrivacies()
}
export const commentPoliciesResolver: ResolveFn<VideoConstant<VideoCommentPolicyType>[]> = (
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot
) => {
return inject(ServerService).getCommentPolicies()
}
export const configRoutes: Routes = [ export const configRoutes: Routes = [
{ {
path: 'config', path: 'config',
@ -97,6 +115,11 @@ export const configRoutes: Routes = [
path: 'general', path: 'general',
component: AdminConfigGeneralComponent, component: AdminConfigGeneralComponent,
canDeactivate: [ CanDeactivateGuard ], canDeactivate: [ CanDeactivateGuard ],
resolve: {
privacies: privaciesResolver,
licences: licencesResolver,
commentPolicies: commentPoliciesResolver
},
data: { data: {
meta: { meta: {
title: $localize`General configuration` title: $localize`General configuration`

View file

@ -53,9 +53,9 @@
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
@if (countExternalAuth() === 0) { @if (countExternalAuth() === 0) {
<span *ngIf="" i18n>⚠️ You don't have any external auth plugin enabled.</span> <span i18n>⚠️ You don't have any external auth plugin enabled</span>
} @else if (countExternalAuth() > 1) { } @else if (countExternalAuth() > 1) {
<span i18n>⚠️ You have multiple external auth plugins enabled.</span> <span i18n>⚠️ You have multiple external auth plugins enabled</span>
} }
</ng-container> </ng-container>
</my-peertube-checkbox> </my-peertube-checkbox>
@ -137,7 +137,7 @@
i18n-labelText labelText="Enable Signup" i18n-labelText labelText="Enable Signup"
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
<span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span> <span i18n>⚠️ This functionality requires a lot of attention and extra moderation</span>
<my-alert type="primary" class="d-block mt-2" *ngIf="signupAlertMessage">{{ signupAlertMessage }}</my-alert> <my-alert type="primary" class="d-block mt-2" *ngIf="signupAlertMessage">{{ signupAlertMessage }}</my-alert>
</ng-container> </ng-container>
@ -171,7 +171,7 @@
<div *ngIf="formErrors.signup.limit" class="form-error" role="alert">{{ formErrors.signup.limit }}</div> <div *ngIf="formErrors.signup.limit" class="form-error" role="alert">{{ formErrors.signup.limit }}</div>
<small i18n *ngIf="hasUnlimitedSignup()" class="muted small">Signup won't be limited to a fixed number of users.</small> <small i18n *ngIf="hasUnlimitedSignup()" class="muted small">Signup won't be limited to a fixed number of users</small>
</div> </div>
<div [ngClass]="getDisabledSignupClass()" class="mt-3"> <div [ngClass]="getDisabledSignupClass()" class="mt-3">
@ -253,7 +253,7 @@
<div class="form-group"> <div class="form-group">
<label i18n for="importConcurrency">Import jobs concurrency</label> <label i18n for="importConcurrency">Import jobs concurrency</label>
<span i18n class="small muted ms-1">allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart.</span> <span i18n class="small muted ms-1">allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart</span>
<div class="number-with-unit"> <div class="number-with-unit">
<input type="number" id="importConcurrency" formControlName="concurrency" /> <input type="number" id="importConcurrency" formControlName="concurrency" />
@ -399,6 +399,35 @@
</my-peertube-checkbox> </my-peertube-checkbox>
</div> </div>
</ng-container> </ng-container>
<ng-container formGroupName="defaults">
<ng-container formGroupName="publish">
<div class="form-group">
<label i18n for="defaultsPublishPrivacy">Default video privacy</label>
<my-select-options inputId="defaultsPublishPrivacy" [items]="privacyOptions" formControlName="privacy"></my-select-options>
<div *ngIf="formErrors.defaults.publish.privacy" class="form-error" role="alert">{{ formErrors.defaults.publish.privacy }}</div>
</div>
<div class="form-group">
<label i18n for="defaultsPublishLicence">Default video licence</label>
<my-select-options inputId="defaultsPublishLicence" [items]="licenceOptions" formControlName="licence"></my-select-options>
<div *ngIf="formErrors.defaults.publish.licence" class="form-error" role="alert">{{ formErrors.defaults.publish.licence }}</div>
</div>
<div class="form-group">
<label i18n for="defaultsPublishCommentsPolicy">Default comment policy</label>
<my-select-options inputId="defaultsPublishCommentsPolicy" [items]="commentPoliciesOptions" formControlName="commentsPolicy"></my-select-options>
<div *ngIf="formErrors.defaults.publish.commentsPolicy" class="form-error" role="alert">{{ formErrors.defaults.publish.commentsPolicy }}</div>
</div>
</ng-container>
</ng-container>
</div> </div>
</div> </div>
@ -424,6 +453,41 @@
</div> </div>
</div> </div>
<div class="pt-two-cols mt-4">
<div class="title-col">
<h2 i18n>PLAYER</h2>
</div>
<div class="content-col">
<ng-container formGroupName="defaults">
<div class="form-group" formGroupName="player">
<my-peertube-checkbox
inputName="defaultsPlayerAutoplay" formControlName="autoPlay"
i18n-labelText labelText="Automatically play videos in the player"
></my-peertube-checkbox>
</div>
<ng-container formGroupName="p2p">
<div class="form-group" formGroupName="webapp">
<my-peertube-checkbox
inputName="defaultsP2PWebappEnabled" formControlName="enabled"
i18n-labelText labelText="Enable P2P streaming by default on your platform"
></my-peertube-checkbox>
</div>
<div class="form-group" formGroupName="embed">
<my-peertube-checkbox
inputName="defaultsP2PEmbedEnabled" formControlName="enabled"
i18n-labelText labelText="Enable P2P streaming by default for videos embedded on external websites"
></my-peertube-checkbox>
</div>
</ng-container>
</ng-container>
</div>
</div>
<div class="pt-two-cols mt-4"> <div class="pt-two-cols mt-4">
<div class="title-col"> <div class="title-col">
<h2 i18n>SEARCH</h2> <h2 i18n>SEARCH</h2>
@ -465,7 +529,7 @@
i18n-labelText labelText="Enable global search" i18n-labelText labelText="Enable global search"
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
<div i18n>⚠️ This functionality depends heavily on the moderation of platforms followed by the search index you select.</div> <div i18n>⚠️ This functionality depends heavily on the moderation of platforms followed by the search index you select</div>
</ng-container> </ng-container>
<ng-container ngProjectAs="extra"> <ng-container ngProjectAs="extra">
@ -573,7 +637,7 @@
<my-select-options inputId="exportUsersExportExpiration" [items]="exportExpirationOptions" formControlName="exportExpiration"></my-select-options> <my-select-options inputId="exportUsersExportExpiration" [items]="exportExpirationOptions" formControlName="exportExpiration"></my-select-options>
<div i18n class="mt-1 small muted">The archive file is deleted after this period.</div> <div i18n class="mt-1 small muted">The archive file is deleted after this period</div>
<div *ngIf="formErrors.export.users.exportExpiration" class="form-error" role="alert">{{ formErrors.export.users.exportExpiration }}</div> <div *ngIf="formErrors.export.users.exportExpiration" class="form-error" role="alert">{{ formErrors.export.users.exportExpiration }}</div>
</div> </div>
@ -626,7 +690,7 @@
i18n-labelText labelText="Automatically follow back followers that follow your platform" i18n-labelText labelText="Automatically follow back followers that follow your platform"
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
<span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span> <span i18n>⚠️ This functionality requires a lot of attention and extra moderation</span>
</ng-container> </ng-container>
</my-peertube-checkbox> </my-peertube-checkbox>
</div> </div>

View file

@ -23,7 +23,8 @@ import {
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators' import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service' import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component' import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { BroadcastMessageLevel, CustomConfig } from '@peertube/peertube-models' import { VideoService } from '@app/shared/shared-main/video/video.service'
import { BroadcastMessageLevel, CustomConfig, VideoCommentPolicyType, VideoConstant, VideoPrivacyType } from '@peertube/peertube-models'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { pairwise } from 'rxjs/operators' import { pairwise } from 'rxjs/operators'
import { SelectOptionsItem } from 'src/types/select-options-item.model' import { SelectOptionsItem } from 'src/types/select-options-item.model'
@ -172,6 +173,28 @@ type Form = {
storyboards: FormGroup<{ storyboards: FormGroup<{
enabled: FormControl<boolean> enabled: FormControl<boolean>
}> }>
defaults: FormGroup<{
publish: FormGroup<{
commentsPolicy: FormControl<VideoCommentPolicyType>
privacy: FormControl<VideoPrivacyType>
licence: FormControl<number>
}>
p2p: FormGroup<{
webapp: FormGroup<{
enabled: FormControl<boolean>
}>
embed: FormGroup<{
enabled: FormControl<boolean>
}>
}>
player: FormGroup<{
autoPlay: FormControl<boolean>
}>
}>
} }
@Component({ @Component({
@ -198,6 +221,7 @@ export class AdminConfigGeneralComponent implements OnInit, OnDestroy, CanCompon
private route = inject(ActivatedRoute) private route = inject(ActivatedRoute)
private formReactiveService = inject(FormReactiveService) private formReactiveService = inject(FormReactiveService)
private adminConfigService = inject(AdminConfigService) private adminConfigService = inject(AdminConfigService)
private videoService = inject(VideoService)
form: FormGroup<Form> form: FormGroup<Form>
formErrors: FormReactiveErrorsTyped<Form> = {} formErrors: FormReactiveErrorsTyped<Form> = {}
@ -209,12 +233,26 @@ export class AdminConfigGeneralComponent implements OnInit, OnDestroy, CanCompon
exportExpirationOptions: SelectOptionsItem[] = [] exportExpirationOptions: SelectOptionsItem[] = []
exportMaxUserVideoQuotaOptions: SelectOptionsItem[] = [] exportMaxUserVideoQuotaOptions: SelectOptionsItem[] = []
privacyOptions: SelectOptionsItem[] = []
commentPoliciesOptions: SelectOptionsItem[] = []
licenceOptions: SelectOptionsItem[] = []
private customConfig: CustomConfig private customConfig: CustomConfig
private customConfigSub: Subscription private customConfigSub: Subscription
ngOnInit () { ngOnInit () {
this.customConfig = this.route.parent.snapshot.data['customConfig'] this.customConfig = this.route.parent.snapshot.data['customConfig']
const data = this.route.snapshot.data as {
licences: VideoConstant<number>[]
privacies: VideoConstant<VideoPrivacyType>[]
commentPolicies: VideoConstant<VideoCommentPolicyType>[]
}
this.privacyOptions = this.videoService.explainedPrivacyLabels(data.privacies).videoPrivacies
this.licenceOptions = data.licences
this.commentPoliciesOptions = data.commentPolicies
this.buildLandingPageOptions() this.buildLandingPageOptions()
this.exportExpirationOptions = [ this.exportExpirationOptions = [
@ -360,9 +398,26 @@ export class AdminConfigGeneralComponent implements OnInit, OnDestroy, CanCompon
isDefaultSearch: null isDefaultSearch: null
} }
}, },
storyboards: { storyboards: {
enabled: null enabled: null
},
defaults: {
publish: {
commentsPolicy: null,
privacy: null,
licence: null
},
p2p: {
webapp: {
enabled: null
},
embed: {
enabled: null
}
},
player: {
autoPlay: null
}
} }
} }

View file

@ -1,5 +1,5 @@
import { exists } from '@peertube/peertube-core-utils' import { exists } from '@peertube/peertube-core-utils'
import { CustomConfig, VideoPrivacy } from '@peertube/peertube-models' import { CustomConfig, VideoCommentPolicy, VideoPrivacy } from '@peertube/peertube-models'
import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { AttributesOnly } from '@peertube/peertube-typescript-utils'
import { getBytes } from '@root-helpers/bytes' import { getBytes } from '@root-helpers/bytes'
import merge from 'lodash-es/merge' import merge from 'lodash-es/merge'
@ -16,6 +16,7 @@ export class UsageType {
live: EnabledDisabled live: EnabledDisabled
globalSearch: EnabledDisabled globalSearch: EnabledDisabled
defaultPrivacy: typeof VideoPrivacy.INTERNAL | typeof VideoPrivacy.PUBLIC defaultPrivacy: typeof VideoPrivacy.INTERNAL | typeof VideoPrivacy.PUBLIC
defaultCommentPolicy: typeof VideoCommentPolicy.REQUIRES_APPROVAL
p2p: EnabledDisabled p2p: EnabledDisabled
federation: EnabledDisabled federation: EnabledDisabled
keepOriginalVideo: EnabledDisabled keepOriginalVideo: EnabledDisabled
@ -46,7 +47,7 @@ export class UsageType {
usageType.keepOriginalVideo = 'disabled' usageType.keepOriginalVideo = 'disabled'
usageType.allowReplaceFile = 'disabled' usageType.allowReplaceFile = 'disabled'
// Use current config for: authType, preferDisplayName and transcription // Use current config for: defaultCommentPolicy, authType, preferDisplayName and transcription
usageType.compute() usageType.compute()
@ -69,7 +70,7 @@ export class UsageType {
usageType.allowReplaceFile = 'enabled' usageType.allowReplaceFile = 'enabled'
usageType.preferDisplayName = 'enabled' usageType.preferDisplayName = 'enabled'
// Use current config for: authType and transcription // Use current config for: defaultCommentPolicy, authType and transcription
usageType.compute() usageType.compute()
@ -94,6 +95,8 @@ export class UsageType {
usageType.authType = 'local' usageType.authType = 'local'
usageType.transcription = 'enabled' usageType.transcription = 'enabled'
usageType.defaultCommentPolicy = VideoCommentPolicy.REQUIRES_APPROVAL
// Use current config for: federation // Use current config for: federation
usageType.compute() usageType.compute()
@ -107,7 +110,7 @@ export class UsageType {
this.config = {} this.config = {}
this.computeRegistration() this.computeRegistration()
this.computeVideoPrivacy() this.computeDefaultVideoPrivacy()
this.computeVideoQuota() this.computeVideoQuota()
this.computeKeepOriginalVideo() this.computeKeepOriginalVideo()
this.computeReplaceVideoFile() this.computeReplaceVideoFile()
@ -115,6 +118,7 @@ export class UsageType {
this.computeStreamLives() this.computeStreamLives()
this.computeP2P() this.computeP2P()
this.computeGlobalSearch() this.computeGlobalSearch()
this.computeDefaultVideoCommentPolicy()
this.computeFederation() this.computeFederation()
this.computeMiniatureSettings() this.computeMiniatureSettings()
this.computeTranscription() this.computeTranscription()
@ -256,7 +260,7 @@ export class UsageType {
} }
} }
private computeVideoPrivacy () { private computeDefaultVideoPrivacy () {
if (!exists(this.defaultPrivacy)) return if (!exists(this.defaultPrivacy)) return
this.addConfig({ this.addConfig({
@ -274,6 +278,20 @@ export class UsageType {
} }
} }
private computeDefaultVideoCommentPolicy () {
if (!exists(this.defaultCommentPolicy)) return
this.addConfig({
defaults: {
publish: {
commentsPolicy: this.defaultCommentPolicy
}
}
})
this.addExplanation($localize`<strong>Require approval</strong> by default of new video comment`)
}
private computeP2P () { private computeP2P () {
if (!exists(this.p2p)) return if (!exists(this.p2p)) return

View file

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import merge from 'lodash-es/merge.js'
import { omit } from '@peertube/peertube-core-utils' import { omit } from '@peertube/peertube-core-utils'
import { ActorImageType, CustomConfig, HttpStatusCode } from '@peertube/peertube-models' import { ActorImageType, CustomConfig, HttpStatusCode } from '@peertube/peertube-models'
import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils'
import { import {
cleanupTests, cleanupTests,
createSingleServer, createSingleServer,
@ -12,7 +12,7 @@ import {
PeerTubeServer, PeerTubeServer,
setAccessTokensToServers setAccessTokensToServers
} from '@peertube/peertube-server-commands' } from '@peertube/peertube-server-commands'
import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils' import merge from 'lodash-es/merge.js'
describe('Test config API validators', function () { describe('Test config API validators', function () {
const path = '/api/v1/config/custom' const path = '/api/v1/config/custom'
@ -91,13 +91,29 @@ describe('Test config API validators', function () {
}) })
it('Should fail with a bad default NSFW policy', async function () { it('Should fail with a bad default NSFW policy', async function () {
const newUpdateParams = { const newUpdateParams = merge({}, updateParams, {
...updateParams,
instance: { instance: {
defaultNSFWPolicy: 'hello' defaultNSFWPolicy: 'hello'
} }
} })
await makePutBodyRequest({
url: server.url,
path,
fields: newUpdateParams,
token: server.accessToken,
expectedStatus: HttpStatusCode.BAD_REQUEST_400
})
})
it('Should fail with a bad default comment policy', async function () {
const newUpdateParams = merge({}, updateParams, {
defaults: {
publish: {
commentsPolicy: 11
}
}
})
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
@ -110,16 +126,14 @@ describe('Test config API validators', function () {
it('Should fail if email disabled and signup requires email verification', async function () { it('Should fail if email disabled and signup requires email verification', async function () {
// opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts // opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts
const newUpdateParams = { const newUpdateParams = merge({}, updateParams, {
...updateParams,
signup: { signup: {
enabled: true, enabled: true,
limit: 5, limit: 5,
requiresApproval: true, requiresApproval: true,
requiresEmailVerification: true requiresEmailVerification: true
} }
} })
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
@ -131,18 +145,17 @@ describe('Test config API validators', function () {
}) })
it('Should fail with a disabled web videos & hls transcoding', async function () { it('Should fail with a disabled web videos & hls transcoding', async function () {
const newUpdateParams = { const newUpdateParams = merge({}, updateParams, {
...updateParams,
transcoding: { transcoding: {
enabled: true,
hls: { hls: {
enabled: false enabled: false
}, },
web_videos: { webVideos: {
enabled: false enabled: false
} }
} }
} })
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
@ -154,7 +167,7 @@ describe('Test config API validators', function () {
}) })
it('Should fail with a disabled http upload & enabled sync', async function () { it('Should fail with a disabled http upload & enabled sync', async function () {
const newUpdateParams: CustomConfig = merge({}, updateParams, { const newUpdateParams: CustomConfig = merge({}, {}, updateParams, {
import: { import: {
videos: { videos: {
http: { enabled: false } http: { enabled: false }
@ -184,7 +197,6 @@ describe('Test config API validators', function () {
}) })
describe('When deleting the configuration', function () { describe('When deleting the configuration', function () {
it('Should fail without token', async function () { it('Should fail without token', async function () {
await makeDeleteRequest({ await makeDeleteRequest({
url: server.url, url: server.url,

View file

@ -146,6 +146,14 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
expect(data.export.users.enabled).to.be.true expect(data.export.users.enabled).to.be.true
expect(data.export.users.exportExpiration).to.equal(1000 * 3600 * 48) expect(data.export.users.exportExpiration).to.equal(1000 * 3600 * 48)
expect(data.export.users.maxUserVideoQuota).to.equal(10737418240) expect(data.export.users.maxUserVideoQuota).to.equal(10737418240)
expect(data.defaults.publish.commentsPolicy).to.equal(VideoCommentPolicy.ENABLED)
expect(data.defaults.publish.downloadEnabled).to.be.true
expect(data.defaults.publish.licence).to.be.null
expect(data.defaults.publish.privacy).to.equal(VideoPrivacy.PUBLIC)
expect(data.defaults.p2p.embed.enabled).to.be.true
expect(data.defaults.p2p.webapp.enabled).to.be.true
expect(data.defaults.player.autoPlay).to.be.true
} }
function buildNewCustomConfig (server: PeerTubeServer): CustomConfig { function buildNewCustomConfig (server: PeerTubeServer): CustomConfig {

View file

@ -537,12 +537,13 @@ function convertCustomConfigBody (body: CustomConfig) {
// Transcoding resolutions exception // Transcoding resolutions exception
if (/^\d{3,4}p$/.exec(k)) return k if (/^\d{3,4}p$/.exec(k)) return k
if (k === '0p') return k if (k === '0p') return k
if (k === 'p2p') return k
return snakeCase(k) return snakeCase(k)
} }
function valueConverter (v: any) { function valueConverter (v: any) {
if (validator.default.isNumeric(v + '')) return parseInt('' + v, 10) if (validator.isNumeric(v + '')) return parseInt('' + v, 10)
return v return v
} }

View file

@ -138,17 +138,29 @@ const CONFIG = {
DEFAULTS: { DEFAULTS: {
PUBLISH: { PUBLISH: {
DOWNLOAD_ENABLED: config.get<boolean>('defaults.publish.download_enabled'), get DOWNLOAD_ENABLED () {
COMMENTS_POLICY: config.get<VideoCommentPolicyType>('defaults.publish.comments_policy'), return config.get<boolean>('defaults.publish.download_enabled')
PRIVACY: config.get<VideoPrivacyType>('defaults.publish.privacy'), },
LICENCE: config.get<number>('defaults.publish.licence') get COMMENTS_POLICY () {
return config.get<VideoCommentPolicyType>('defaults.publish.comments_policy')
},
get PRIVACY () {
return config.get<VideoPrivacyType>('defaults.publish.privacy')
},
get LICENCE () {
return config.get<number>('defaults.publish.licence')
}
}, },
P2P: { P2P: {
WEBAPP: { WEBAPP: {
ENABLED: config.get<boolean>('defaults.p2p.webapp.enabled') get ENABLED () {
return config.get<boolean>('defaults.p2p.webapp.enabled')
}
}, },
EMBED: { EMBED: {
ENABLED: config.get<boolean>('defaults.p2p.embed.enabled') get ENABLED () {
return config.get<boolean>('defaults.p2p.embed.enabled')
}
} }
}, },
PLAYER: { PLAYER: {

View file

@ -8,6 +8,7 @@ import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaVali
import { isThemeRegistered } from '../../lib/plugins/theme-utils.js' import { isThemeRegistered } from '../../lib/plugins/theme-utils.js'
import { areValidationErrors } from './shared/index.js' import { areValidationErrors } from './shared/index.js'
import { isNumberArray, isStringArray } from '@server/helpers/custom-validators/search.js' import { isNumberArray, isStringArray } from '@server/helpers/custom-validators/search.js'
import { isVideoCommentsPolicyValid, isVideoLicenceValid, isVideoPrivacyValid } from '@server/helpers/custom-validators/videos.js'
const customConfigUpdateValidator = [ const customConfigUpdateValidator = [
body('instance.name').exists(), body('instance.name').exists(),
@ -135,6 +136,13 @@ const customConfigUpdateValidator = [
body('search.searchIndex.disableLocalSearch').isBoolean(), body('search.searchIndex.disableLocalSearch').isBoolean(),
body('search.searchIndex.isDefaultSearch').isBoolean(), body('search.searchIndex.isDefaultSearch').isBoolean(),
body('defaults.publish.commentsPolicy').custom(isVideoCommentsPolicyValid),
body('defaults.publish.privacy').custom(isVideoPrivacyValid),
body('defaults.publish.licence').custom(isVideoLicenceValid),
body('defaults.p2p.webapp.enabled').isBoolean(),
body('defaults.p2p.embed.enabled').isBoolean(),
body('defaults.player.autoPlay').isBoolean(),
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return