diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
index 3ceea02ca..6ae7b1b79 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
@@ -63,7 +63,7 @@
-
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
index 5951d0aaa..0458d257f 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
@@ -33,6 +33,11 @@ input[type=number] {
top: 5px;
right: 2.5rem;
}
+
+ input[disabled] {
+ background-color: #f9f9f9;
+ pointer-events: none;
+ }
}
input[type=checkbox] {
@@ -93,6 +98,11 @@ textarea {
}
}
+input[disabled] {
+ opacity: 0.5;
+}
+
+
.form-group-right {
padding-top: 2px;
}
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index f13fe4bf9..04b0175a7 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -258,6 +258,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
this.loadConfigAndUpdateForm()
this.loadCategoriesAndLanguages()
+ if (!this.serverConfig.allowEdits) {
+ this.form.disable()
+ }
}
formValidated () {
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.html b/client/src/app/shared/shared-forms/markdown-textarea.component.html
index 6e70e2f37..a460cb9b7 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.html
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.html
@@ -2,6 +2,7 @@
@@ -25,11 +26,11 @@
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.ts b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
index 80ca6690f..dcb5d20da 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
@@ -45,6 +45,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
previewHTML: SafeHtml | string = ''
isMaximized = false
+ disabled = false
maximizeInText = $localize`Maximize editor`
maximizeOutText = $localize`Exit maximized editor`
@@ -108,6 +109,10 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
}
}
+ setDisabledState (isDisabled: boolean) {
+ this.disabled = isDisabled
+ }
+
private lockBodyScroll () {
this.scrollPosition = this.viewportScroller.getScrollPosition()
document.getElementById('content').classList.add('lock-scroll')
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.html b/client/src/app/shared/shared-forms/select/select-checkbox.component.html
index 7b49a0c01..03db2875b 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox.component.html
+++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.html
@@ -7,6 +7,7 @@
[multiple]="true"
[searchable]="true"
[closeOnSelect]="false"
+ [disabled]="disabled"
bindValue="id"
bindLabel="label"
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.ts b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
index 12f697628..c9a500324 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
@@ -23,6 +23,8 @@ export class SelectCheckboxComponent implements OnInit, ControlValueAccessor {
@Input() selectableGroupAsModel: boolean
@Input() placeholder: string
+ disabled = false
+
ngOnInit () {
if (!this.placeholder) this.placeholder = $localize`Add a new option`
}
@@ -59,6 +61,10 @@ export class SelectCheckboxComponent implements OnInit, ControlValueAccessor {
this.propagateChange(this.selectedItems)
}
+ setDisabledState (isDisabled: boolean) {
+ this.disabled = isDisabled
+ }
+
compareFn (item: SelectOptionsItem, selected: ItemSelectCheckboxValue) {
if (typeof selected === 'string' || typeof selected === 'number') {
return item.id === selected
diff --git a/client/src/app/shared/shared-forms/select/select-custom-value.component.html b/client/src/app/shared/shared-forms/select/select-custom-value.component.html
index 9dc8c2ec2..69fdedc10 100644
--- a/client/src/app/shared/shared-forms/select/select-custom-value.component.html
+++ b/client/src/app/shared/shared-forms/select/select-custom-value.component.html
@@ -5,6 +5,7 @@
[searchable]="searchable"
[groupBy]="groupBy"
[labelForId]="labelForId"
+ [disabled]="disabled"
[(ngModel)]="selectedId"
(ngModelChange)="onModelChange()"
diff --git a/client/src/app/shared/shared-forms/select/select-custom-value.component.ts b/client/src/app/shared/shared-forms/select/select-custom-value.component.ts
index bc6b863c7..636bd6101 100644
--- a/client/src/app/shared/shared-forms/select/select-custom-value.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-custom-value.component.ts
@@ -25,6 +25,7 @@ export class SelectCustomValueComponent implements ControlValueAccessor, OnChang
customValue: number | string = ''
selectedId: number | string
+ disabled = false
itemsWithCustom: SelectOptionsItem[] = []
@@ -75,4 +76,8 @@ export class SelectCustomValueComponent implements ControlValueAccessor, OnChang
isCustomValue () {
return this.selectedId === 'other'
}
+
+ setDisabledState (isDisabled: boolean) {
+ this.disabled = isDisabled
+ }
}
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.html b/client/src/app/shared/shared-forms/select/select-options.component.html
index 3b1761255..83c7de9f5 100644
--- a/client/src/app/shared/shared-forms/select/select-options.component.html
+++ b/client/src/app/shared/shared-forms/select/select-options.component.html
@@ -7,6 +7,7 @@
[labelForId]="labelForId"
[searchable]="searchable"
[searchFn]="searchFn"
+ [disabled]="disabled"
bindLabel="label"
bindValue="id"
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.ts b/client/src/app/shared/shared-forms/select/select-options.component.ts
index 8482b9dea..820a82c24 100644
--- a/client/src/app/shared/shared-forms/select/select-options.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-options.component.ts
@@ -23,6 +23,7 @@ export class SelectOptionsComponent implements ControlValueAccessor {
@Input() searchFn: any
selectedId: number | string
+ disabled = false
propagateChange = (_: any) => { /* empty */ }
@@ -48,4 +49,8 @@ export class SelectOptionsComponent implements ControlValueAccessor {
onModelChange () {
this.propagateChange(this.selectedId)
}
+
+ setDisabledState (isDisabled: boolean) {
+ this.disabled = isDisabled
+ }
}
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 9f6d69131..679c235a6 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -364,6 +364,9 @@
cursor: default;
}
}
+ select[disabled] {
+ background-color: #f9f9f9;
+ }
@media screen and (max-width: $width) {
width: 100%;
diff --git a/config/default.yaml b/config/default.yaml
index 3865ab5cf..eb96b6bbb 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -243,6 +243,11 @@ peertube:
# You can use a custom URL if your want, that respect the format behind https://joinpeertube.org/api/v1/versions.json
url: 'https://joinpeertube.org/api/v1/versions.json'
+webadmin:
+ configuration:
+ edit:
+ allowed: true
+
cache:
previews:
size: 500 # Max number of previews you want to cache
diff --git a/config/production.yaml.example b/config/production.yaml.example
index 94238fad0..082c75e53 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -241,6 +241,11 @@ peertube:
# You can use a custom URL if your want, that respect the format behind https://joinpeertube.org/api/v1/versions.json
url: 'https://joinpeertube.org/api/v1/versions.json'
+webadmin:
+ configuration:
+ # Set to false if you want the config to be readonly
+ allow_edits: true
+
###############################################################################
#
# From this point, all the following keys can be overridden by the web interface
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index d542f62aa..5ea1f67c9 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -11,7 +11,7 @@ import { objectConverter } from '../../helpers/core-utils'
import { CONFIG, reloadConfig } from '../../initializers/config'
import { ClientHtml } from '../../lib/client-html'
import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
-import { customConfigUpdateValidator } from '../../middlewares/validators/config'
+import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
const configRouter = express.Router()
@@ -38,6 +38,7 @@ configRouter.put('/custom',
openapiOperationDoc({ operationId: 'putCustomConfig' }),
authenticate,
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
+ ensureConfigIsEditable,
customConfigUpdateValidator,
asyncMiddleware(updateCustomConfig)
)
@@ -46,6 +47,7 @@ configRouter.delete('/custom',
openapiOperationDoc({ operationId: 'delCustomConfig' }),
authenticate,
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
+ ensureConfigIsEditable,
asyncMiddleware(deleteCustomConfig)
)
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index be9fc61f0..b2a8e9e19 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -195,6 +195,13 @@ const CONFIG = {
URL: config.get('peertube.check_latest_version.url')
}
},
+ WEBADMIN: {
+ CONFIGURATION: {
+ EDITS: {
+ ALLOWED: config.get('webadmin.configuration.edit.allowed')
+ }
+ }
+ },
ADMIN: {
get EMAIL () { return config.get('admin.email') }
},
@@ -411,14 +418,22 @@ export {
// ---------------------------------------------------------------------------
function getLocalConfigFilePath () {
- const configSources = config.util.getConfigSources()
- if (configSources.length === 0) throw new Error('Invalid config source.')
+ const localConfigDir = getLocalConfigDir()
let filename = 'local'
if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
- return join(dirname(configSources[0].name), filename + '.json')
+ return join(localConfigDir, filename + '.json')
+}
+
+function getLocalConfigDir () {
+ if (process.env.PEERTUBE_LOCAL_CONFIG) return process.env.PEERTUBE_LOCAL_CONFIG
+
+ const configSources = config.util.getConfigSources()
+ if (configSources.length === 0) throw new Error('Invalid config source.')
+
+ return dirname(configSources[0].name)
}
function buildVideosRedundancy (objs: any[]): VideosRedundancyStrategy[] {
@@ -437,19 +452,19 @@ function buildVideosRedundancy (objs: any[]): VideosRedundancyStrategy[] {
export function reloadConfig () {
- function getConfigDirectory () {
+ function getConfigDirectories () {
if (process.env.NODE_CONFIG_DIR) {
- return process.env.NODE_CONFIG_DIR
+ return process.env.NODE_CONFIG_DIR.split(":")
}
- return join(root(), 'config')
+ return [ join(root(), 'config') ]
}
function purge () {
- const directory = getConfigDirectory()
+ const directories = getConfigDirectories()
for (const fileName in require.cache) {
- if (fileName.includes(directory) === false) {
+ if (directories.some((dir) => fileName.includes(dir)) === false) {
continue
}
diff --git a/server/lib/server-config-manager.ts b/server/lib/server-config-manager.ts
index 80d87a9d3..358f47133 100644
--- a/server/lib/server-config-manager.ts
+++ b/server/lib/server-config-manager.ts
@@ -42,6 +42,7 @@ class ServerConfigManager {
const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
return {
+ allowEdits: CONFIG.WEBADMIN.CONFIGURATION.EDITS.ALLOWED,
instance: {
name: CONFIG.INSTANCE.NAME,
shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index 16a840667..5f1ac89bc 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -1,13 +1,14 @@
import express from 'express'
import { body } from 'express-validator'
import { isIntOrNull } from '@server/helpers/custom-validators/misc'
-import { isEmailEnabled } from '@server/initializers/config'
+import { CONFIG, isEmailEnabled } from '@server/initializers/config'
import { CustomConfig } from '../../../shared/models/server/custom-config.model'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
import { logger } from '../../helpers/logger'
import { isThemeRegistered } from '../../lib/plugins/theme-utils'
import { areValidationErrors } from './shared'
+import { HttpStatusCode } from '@shared/models/http/http-error-codes'
const customConfigUpdateValidator = [
body('instance.name').exists().withMessage('Should have a valid instance name'),
@@ -104,10 +105,21 @@ const customConfigUpdateValidator = [
}
]
+function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
+ if (!CONFIG.WEBADMIN.CONFIGURATION.EDITS.ALLOWED) {
+ return res.fail({
+ status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
+ message: 'Server configuration is static and cannot be edited'
+ })
+ }
+ return next()
+}
+
// ---------------------------------------------------------------------------
export {
- customConfigUpdateValidator
+ customConfigUpdateValidator,
+ ensureConfigIsEditable
}
function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index c4dd882b8..e057ec1a2 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -201,6 +201,199 @@ function checkUpdatedConfig (data: CustomConfig) {
expect(data.broadcastMessage.dismissable).to.be.true
}
+const newCustomConfig: CustomConfig = {
+ instance: {
+ name: 'PeerTube updated',
+ shortDescription: 'my short description',
+ description: 'my super description',
+ terms: 'my super terms',
+ codeOfConduct: 'my super coc',
+
+ creationReason: 'my super creation reason',
+ moderationInformation: 'my super moderation information',
+ administrator: 'Kuja',
+ maintenanceLifetime: 'forever',
+ businessModel: 'my super business model',
+ hardwareInformation: '2vCore 3GB RAM',
+
+ languages: [ 'en', 'es' ],
+ categories: [ 1, 2 ],
+
+ isNSFW: true,
+ defaultNSFWPolicy: 'blur' as 'blur',
+
+ defaultClientRoute: '/videos/recently-added',
+
+ customizations: {
+ javascript: 'alert("coucou")',
+ css: 'body { background-color: red; }'
+ }
+ },
+ theme: {
+ default: 'default'
+ },
+ services: {
+ twitter: {
+ username: '@Kuja',
+ whitelisted: true
+ }
+ },
+ cache: {
+ previews: {
+ size: 2
+ },
+ captions: {
+ size: 3
+ },
+ torrents: {
+ size: 4
+ }
+ },
+ signup: {
+ enabled: false,
+ limit: 5,
+ requiresEmailVerification: false,
+ minimumAge: 10
+ },
+ admin: {
+ email: 'superadmin1@example.com'
+ },
+ contactForm: {
+ enabled: false
+ },
+ user: {
+ videoQuota: 5242881,
+ videoQuotaDaily: 318742
+ },
+ transcoding: {
+ enabled: true,
+ allowAdditionalExtensions: true,
+ allowAudioFiles: true,
+ threads: 1,
+ concurrency: 3,
+ profile: 'vod_profile',
+ resolutions: {
+ '0p': false,
+ '240p': false,
+ '360p': true,
+ '480p': true,
+ '720p': false,
+ '1080p': false,
+ '1440p': false,
+ '2160p': false
+ },
+ webtorrent: {
+ enabled: true
+ },
+ hls: {
+ enabled: false
+ }
+ },
+ live: {
+ enabled: true,
+ allowReplay: true,
+ maxDuration: 5000,
+ maxInstanceLives: -1,
+ maxUserLives: 10,
+ transcoding: {
+ enabled: true,
+ threads: 4,
+ profile: 'live_profile',
+ resolutions: {
+ '240p': true,
+ '360p': true,
+ '480p': true,
+ '720p': true,
+ '1080p': true,
+ '1440p': true,
+ '2160p': true
+ }
+ }
+ },
+ import: {
+ videos: {
+ concurrency: 4,
+ http: {
+ enabled: false
+ },
+ torrent: {
+ enabled: false
+ }
+ }
+ },
+ trending: {
+ videos: {
+ algorithms: {
+ enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
+ default: 'hot'
+ }
+ }
+ },
+ autoBlacklist: {
+ videos: {
+ ofUsers: {
+ enabled: true
+ }
+ }
+ },
+ followers: {
+ instance: {
+ enabled: false,
+ manualApproval: true
+ }
+ },
+ followings: {
+ instance: {
+ autoFollowBack: {
+ enabled: true
+ },
+ autoFollowIndex: {
+ enabled: true,
+ indexUrl: 'https://updated.example.com'
+ }
+ }
+ },
+ broadcastMessage: {
+ enabled: true,
+ level: 'error',
+ message: 'super bad message',
+ dismissable: true
+ },
+ search: {
+ remoteUri: {
+ anonymous: true,
+ users: true
+ },
+ searchIndex: {
+ enabled: true,
+ url: 'https://search.joinpeertube.org',
+ disableLocalSearch: true,
+ isDefaultSearch: true
+ }
+ }
+}
+
+describe('Test static config', function () {
+ let server: PeerTubeServer = null
+
+ before(async function () {
+ this.timeout(30000)
+
+ server = await createSingleServer(1, { webadmin: { configuration: { edit: { allowed: false } } } })
+ await setAccessTokensToServers([ server ])
+ })
+
+ it('Should tell the client that edits are not allowed', async function () {
+ const data = await server.config.getConfig()
+
+ expect(data.allowEdits).to.be.false
+ })
+
+ it('Should error when client tries to update', async function () {
+ await server.config.updateCustomConfig({ newCustomConfig, expectedStatus: 405 })
+ })
+})
+
describe('Test config', function () {
let server: PeerTubeServer = null
@@ -252,177 +445,6 @@ describe('Test config', function () {
})
it('Should update the customized configuration', async function () {
- const newCustomConfig: CustomConfig = {
- instance: {
- name: 'PeerTube updated',
- shortDescription: 'my short description',
- description: 'my super description',
- terms: 'my super terms',
- codeOfConduct: 'my super coc',
-
- creationReason: 'my super creation reason',
- moderationInformation: 'my super moderation information',
- administrator: 'Kuja',
- maintenanceLifetime: 'forever',
- businessModel: 'my super business model',
- hardwareInformation: '2vCore 3GB RAM',
-
- languages: [ 'en', 'es' ],
- categories: [ 1, 2 ],
-
- isNSFW: true,
- defaultNSFWPolicy: 'blur' as 'blur',
-
- defaultClientRoute: '/videos/recently-added',
-
- customizations: {
- javascript: 'alert("coucou")',
- css: 'body { background-color: red; }'
- }
- },
- theme: {
- default: 'default'
- },
- services: {
- twitter: {
- username: '@Kuja',
- whitelisted: true
- }
- },
- cache: {
- previews: {
- size: 2
- },
- captions: {
- size: 3
- },
- torrents: {
- size: 4
- }
- },
- signup: {
- enabled: false,
- limit: 5,
- requiresEmailVerification: false,
- minimumAge: 10
- },
- admin: {
- email: 'superadmin1@example.com'
- },
- contactForm: {
- enabled: false
- },
- user: {
- videoQuota: 5242881,
- videoQuotaDaily: 318742
- },
- transcoding: {
- enabled: true,
- allowAdditionalExtensions: true,
- allowAudioFiles: true,
- threads: 1,
- concurrency: 3,
- profile: 'vod_profile',
- resolutions: {
- '0p': false,
- '240p': false,
- '360p': true,
- '480p': true,
- '720p': false,
- '1080p': false,
- '1440p': false,
- '2160p': false
- },
- webtorrent: {
- enabled: true
- },
- hls: {
- enabled: false
- }
- },
- live: {
- enabled: true,
- allowReplay: true,
- maxDuration: 5000,
- maxInstanceLives: -1,
- maxUserLives: 10,
- transcoding: {
- enabled: true,
- threads: 4,
- profile: 'live_profile',
- resolutions: {
- '240p': true,
- '360p': true,
- '480p': true,
- '720p': true,
- '1080p': true,
- '1440p': true,
- '2160p': true
- }
- }
- },
- import: {
- videos: {
- concurrency: 4,
- http: {
- enabled: false
- },
- torrent: {
- enabled: false
- }
- }
- },
- trending: {
- videos: {
- algorithms: {
- enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
- default: 'hot'
- }
- }
- },
- autoBlacklist: {
- videos: {
- ofUsers: {
- enabled: true
- }
- }
- },
- followers: {
- instance: {
- enabled: false,
- manualApproval: true
- }
- },
- followings: {
- instance: {
- autoFollowBack: {
- enabled: true
- },
- autoFollowIndex: {
- enabled: true,
- indexUrl: 'https://updated.example.com'
- }
- }
- },
- broadcastMessage: {
- enabled: true,
- level: 'error',
- message: 'super bad message',
- dismissable: true
- },
- search: {
- remoteUri: {
- anonymous: true,
- users: true
- },
- searchIndex: {
- enabled: true,
- url: 'https://search.joinpeertube.org',
- disableLocalSearch: true,
- isDefaultSearch: true
- }
- }
- }
await server.config.updateCustomConfig({ newCustomConfig })
const data = await server.config.getCustomConfig()
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts
index 585e99aca..3b026e3a5 100644
--- a/shared/models/server/server-config.model.ts
+++ b/shared/models/server/server-config.model.ts
@@ -30,6 +30,7 @@ export interface RegisteredIdAndPassAuthConfig {
}
export interface ServerConfig {
+ allowEdits: boolean
serverVersion: string
serverCommit?: string
diff --git a/support/docker/production/Dockerfile.buster b/support/docker/production/Dockerfile.buster
index 2ff0591f9..163c514f5 100644
--- a/support/docker/production/Dockerfile.buster
+++ b/support/docker/production/Dockerfile.buster
@@ -33,7 +33,8 @@ RUN mkdir /data /config
RUN chown -R peertube:peertube /data /config
ENV NODE_ENV production
-ENV NODE_CONFIG_DIR /config
+ENV NODE_CONFIG_DIR /app/config:/app/support/docker/production/config:/config
+ENV PEERTUBE_LOCAL_CONFIG /config
VOLUME /data
VOLUME /config
diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml
index 1b474582a..7c430a995 100644
--- a/support/docker/production/config/custom-environment-variables.yaml
+++ b/support/docker/production/config/custom-environment-variables.yaml
@@ -68,6 +68,13 @@ object_storage:
prefix: "PEERTUBE_OBJECT_STORAGE_VIDEOS_PREFIX"
base_url: "PEERTUBE_OBJECT_STORAGE_VIDEOS_BASE_URL"
+webadmin:
+ configuration:
+ edit:
+ allowed:
+ __name: "PEERTUBE_ALLOW_WEBADMIN_CONFIG"
+ __format: "json"
+
log:
level: "PEERTUBE_LOG_LEVEL"
log_ping_requests:
diff --git a/support/docker/production/entrypoint.sh b/support/docker/production/entrypoint.sh
index 7dd626b9f..261055e84 100755
--- a/support/docker/production/entrypoint.sh
+++ b/support/docker/production/entrypoint.sh
@@ -1,15 +1,8 @@
#!/bin/sh
set -e
-# Populate config directory
-if [ -z "$(ls -A /config)" ]; then
- cp /app/support/docker/production/config/* /config
-fi
-# Always copy default and custom env configuration file, in cases where new keys were added
-cp /app/config/default.yaml /config
-cp /app/support/docker/production/config/custom-environment-variables.yaml /config
-find /config ! -user peertube -exec chown peertube:peertube {} \;
+find /config ! -user peertube -exec chown peertube:peertube {} \; || true
# first arg is `-f` or `--some-option`
# or first arg is `something.conf`