diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a6de166906b833b7d4faba38693782ebccca03..bd324b8112c681826e0c6664f32249aba5ae624f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add Report show page and link Moderation log references to the respective reports - Add Unconfimed filter for Users table - Filter users by actor type: Person, Bot or Application -- Add ability to configure Media Preview Proxy, User Backup and Websocket based federation settings +- Add ability to configure Media Preview Proxy, User Backup, Websocket based federation and Pleroma.Web.Endpoint.MetricsExporter settings - Mobile and Tablet UI for Single Report show page ### Changed @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Move `:restrict_unauthenticated` settings from Authentication tab to Instance tab - Replace regular inputs with textareas for setting welcome messages in the Settings section - Update rendering Moderation Log Messages so that all usernames are links to the pages of the corresponding users in Admin-FE +- Remove Websocket based federation settings ### Fixed diff --git a/src/store/modules/normalizers.js b/src/store/modules/normalizers.js index e647a4a78dc4a0d08900551554aa35b5f1d3dd71..150865d00e1ccb97d8111b4fb3203fc51faebb83 100644 --- a/src/store/modules/normalizers.js +++ b/src/store/modules/normalizers.js @@ -57,10 +57,18 @@ export const parseNonTuples = (key, value) => { // REFACTOR export const parseTuples = (tuples, key) => { return tuples.reduce((accum, item) => { - if (key === ':rate_limit') { - accum[item.tuple[0]] = Array.isArray(item.tuple[1]) - ? item.tuple[1].map(el => el.tuple) - : item.tuple[1].tuple + if (key === ':rate_limit' || + (key === 'Pleroma.Web.Endpoint.MetricsExporter' && item.tuple[0] === ':auth')) { + const getValue = () => { + if (typeof item.tuple[1] === 'boolean') { + return item.tuple[1] + } else if (Array.isArray(item.tuple[1])) { + return item.tuple[1].map(el => el.tuple) + } else { + return item.tuple[1].tuple + } + } + accum[item.tuple[0]] = getValue() } else if (item.tuple[0] === ':mascots') { accum[item.tuple[0]] = item.tuple[1].reduce((acc, mascot) => { return [...acc, { [mascot.tuple[0]]: { ...mascot.tuple[1], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}] @@ -92,6 +100,8 @@ export const parseTuples = (tuples, key) => { accum[item.tuple[0]] = parseStringOrTupleValue(item.tuple[0], item.tuple[1]) } else if (item.tuple[0] === ':args') { accum[item.tuple[0]] = parseNonTuples(item.tuple[0], item.tuple[1]) + } else if (item.tuple[0] === ':ip_whitelist') { + accum[item.tuple[0]] = item.tuple[1].map(ip => typeof ip === 'string' ? ip : ip.tuple.join('.')) } else if (Array.isArray(item.tuple[1]) && (typeof item.tuple[1][0] === 'object' && !Array.isArray(item.tuple[1][0])) && item.tuple[1][0]['tuple']) { accum[item.tuple[0]] = parseTuples(item.tuple[1], item.tuple[0]) @@ -237,8 +247,9 @@ const wrapValues = (settings, currentState) => { return { 'tuple': [setting, wrapValues(value, currentState)] } } else if (prependWithСolon(type, value)) { return { 'tuple': [setting, `:${value}`] } - } else if (type.includes('tuple') && (type.includes('string') || type.includes('atom'))) { - return typeof value === 'string' + } else if (type.includes('tuple') && + (type.includes('string') || type.includes('atom') || type.includes('boolean'))) { + return typeof value === 'string' || typeof value === 'boolean' ? { 'tuple': [setting, value] } : { 'tuple': [setting, { 'tuple': value }] } } else if (type === 'reversed_tuple') { diff --git a/src/views/settings/components/Http.vue b/src/views/settings/components/Http.vue index 7b82537e6490c61b97a094b4219bc3ed36e5f54b..8aa8a23bf9fbc2b1eba409333aed197d35e53e32 100644 --- a/src/views/settings/components/Http.vue +++ b/src/views/settings/components/Http.vue @@ -11,14 +11,10 @@ <el-form :model="httpSecurityData" :label-position="labelPosition" :label-width="labelWidth"> <setting :setting-group="httpSecurity" :data="httpSecurityData"/> </el-form> - <el-divider v-if="httpSecurity" class="divider thick-line"/> + <el-divider v-if="webCacheTtl" class="divider thick-line"/> <el-form :model="webCacheTtlData" :label-position="labelPosition" :label-width="labelWidth"> <setting :setting-group="webCacheTtl" :data="webCacheTtlData"/> </el-form> - <el-divider v-if="fedSockets" class="divider thick-line"/> - <el-form :model="fedSocketsData" :label-position="labelPosition" :label-width="labelWidth"> - <setting :setting-group="fedSockets" :data="fedSocketsData"/> - </el-form> <div class="submit-button-container"> <el-button class="submit-button" type="primary" @click="onSubmit">Submit</el-button> </div> @@ -44,12 +40,6 @@ export default { corsPlugData() { return _.get(this.settings.settings, [':cors_plug']) || {} }, - fedSockets() { - return this.settings.description.find(setting => setting.key === ':fed_sockets') - }, - fedSocketsData() { - return _.get(this.settings.settings, [':pleroma', ':fed_sockets']) || {} - }, http() { return this.settings.description.find(setting => setting.key === ':http') }, diff --git a/src/views/settings/components/Inputs.vue b/src/views/settings/components/Inputs.vue index 85e41ef094a5bd06930da3d27528c4449dc4a264..e052fefd058ca964f63a747bae3ce8c1d9c9f963 100644 --- a/src/views/settings/components/Inputs.vue +++ b/src/views/settings/components/Inputs.vue @@ -113,7 +113,7 @@ <!-- special inputs --> <editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="keywordData" :setting-group="settingGroup" :setting="setting" :parents="settingParent"/> <icons-input v-if="setting.key === ':icons'" :data="iconsData" :setting-group="settingGroup" :setting="setting"/> - <link-formatter-input v-if="booleanCombinedInput" :data="data" :setting-group="settingGroup" :setting="setting"/> + <boolean-combined-input v-if="booleanCombinedInput" :data="data" :setting-group="settingGroup" :setting="setting"/> <mascots-input v-if="setting.key === ':mascots'" :data="keywordData" :setting-group="settingGroup" :setting="setting"/> <proxy-url-input v-if="setting.key === ':proxy_url'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting" :parents="settingParent"/> <prune-input v-if="setting.key === ':prune'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting"/> @@ -141,7 +141,7 @@ import { EditableKeywordInput, IconsInput, ImageUploadInput, - LinkFormatterInput, + BooleanCombinedInput, MascotsInput, ProxyUrlInput, PruneInput, @@ -160,7 +160,7 @@ export default { EditableKeywordInput, IconsInput, ImageUploadInput, - LinkFormatterInput, + BooleanCombinedInput, MascotsInput, ProxyUrlInput, PruneInput, @@ -363,6 +363,7 @@ export default { }, renderMultipleSelect(type) { return !this.reducedSelects && Array.isArray(type) && this.setting.key !== ':backends' && this.setting.key !== ':args' && ( + this.setting.key === ':ip_whitelist' || type.includes('module') || (type.includes('list') && type.includes('string')) || (type.includes('list') && type.includes('atom')) || diff --git a/src/views/settings/components/Other.vue b/src/views/settings/components/Other.vue index 0a69713897fe53a244b5a34773fd6f4927a4c7cb..91a9fe2a60d8df6242ff837bd3fd237606826a9b 100644 --- a/src/views/settings/components/Other.vue +++ b/src/views/settings/components/Other.vue @@ -2,6 +2,10 @@ <div v-if="!loading" :class="isSidebarOpen" class="form-container"> <editor-input v-model="termsOfServicesContent" :name="'terms-of-service'" @input="handleEditorUpdate"/> <el-divider class="divider thick-line"/> + <el-form :model="prometheusMetricsData" :label-position="labelPosition" :label-width="labelWidth"> + <setting :setting-group="prometheusMetrics" :data="prometheusMetricsData"/> + </el-form> + <el-divider v-if="prometheusMetrics" class="divider thick-line"/> <el-form :model="backupData" :label-position="labelPosition" :label-width="labelWidth"> <setting :setting-group="backup" :data="backupData"/> </el-form> @@ -94,6 +98,12 @@ export default { modulesData() { return _.get(this.settings.settings, [':pleroma', ':modules']) || {} }, + prometheusMetrics() { + return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Endpoint.MetricsExporter') + }, + prometheusMetricsData() { + return _.get(this.settings.settings, [':prometheus', 'Pleroma.Web.Endpoint.MetricsExporter']) || {} + }, remoteIp() { return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Plugs.RemoteIp') }, diff --git a/src/views/settings/components/inputComponents/LinkFormatterInput.vue b/src/views/settings/components/inputComponents/BooleanCombinedInput.vue similarity index 52% rename from src/views/settings/components/inputComponents/LinkFormatterInput.vue rename to src/views/settings/components/inputComponents/BooleanCombinedInput.vue index 38ec2e5091d53ec87211b9a29d4a3960f85be3e5..e47ade3de54d12d8d5628e6e5ad19e4fe08f2453 100644 --- a/src/views/settings/components/inputComponents/LinkFormatterInput.vue +++ b/src/views/settings/components/inputComponents/BooleanCombinedInput.vue @@ -1,34 +1,46 @@ <template> <div> <div v-if="setting.type.includes('string')" :data-search="setting.key || setting.group"> - <el-switch :value="autoLinkerBooleanValue" @change="processTwoTypeValue($event, setting.key)"/> + <el-switch :value="booleanValue" @change="processTwoTypeValue($event, setting.key)"/> <el-input - v-if="autoLinkerBooleanValue" - :value="autoLinkerStringValue" + v-if="booleanValue" + :value="stringValue" @input="processTwoTypeValue($event, setting.key)"/> </div> <div v-if="setting.type.includes('integer')" :data-search="setting.key || setting.group"> - <el-switch :value="autoLinkerBooleanValue" @change="processTwoTypeValue($event, setting.key)"/> + <el-switch :value="booleanValue" @change="processTwoTypeValue($event, setting.key)"/> <el-input-number - v-if="autoLinkerBooleanValue" - :value="autoLinkerIntegerValue" + v-if="booleanValue" + :value="integerValue" @input="processTwoTypeValue($event, setting.key)"/> </div> <div v-if="setting.type.includes('atom')" :data-search="setting.key || setting.group"> - <el-switch :value="autoLinkerBooleanValue" @change="processTwoTypeValue($event, setting.key)"/> + <el-switch :value="booleanValue" @change="processTwoTypeValue($event, setting.key)"/> <el-input - v-if="autoLinkerBooleanValue" - :value="autoLinkerAtomValue" + v-if="booleanValue" + :value="atomValue" @input="processTwoTypeValue($event, setting.key)"> <template slot="prepend">:</template> </el-input> </div> + <div v-if="setting.type.includes('tuple')" :data-search="setting.key || setting.group"> + <el-switch :value="booleanValue" @change="processTupleTwoTypeValue($event, setting.key)"/> + <div v-if="booleanValue" class="tuple-input-container"> + <el-input + v-for="(item, index) in tupleValue" + :value="item" + :key="index" + :placeholder="getPlaceholder[index]" + class="tuple-input" + @input="processTupleTwoTypeValue($event, setting.key, index)"/> + </div> + </div> </div> </template> <script> export default { - name: 'LinkFormatterInput', + name: 'BooleanCombinedInput', props: { data: { type: [Object, Array], @@ -50,24 +62,42 @@ export default { } }, computed: { - autoLinkerAtomValue() { + atomValue() { return this.data[this.setting.key] && this.data[this.setting.key][0] === ':' ? this.data[this.setting.key].substr(1) : this.data[this.setting.key] }, - autoLinkerBooleanValue() { + booleanValue() { const value = this.data[this.setting.key] - return typeof value === 'string' || typeof value === 'number' + return typeof value !== 'boolean' + }, + getPlaceholder() { + return { 0: ':basic', 1: 'username', 2: 'password' } }, - autoLinkerIntegerValue() { + integerValue() { const value = this.data[this.setting.key] return value || 0 }, - autoLinkerStringValue() { + stringValue() { const value = this.data[this.setting.key] return value || '' + }, + tupleValue() { + const value = this.data[this.setting.key] + return value || ['', '', ''] } }, methods: { + processTupleTwoTypeValue(value, input, _index) { + if (value === false) { + this.updateSetting(value, this.settingGroup.group, this.settingGroup.key, input, this.setting.type) + } else if (value === true) { + this.updateSetting(['', '', ''], this.settingGroup.group, this.settingGroup.key, input, this.setting.type) + } else { + const data = [...this.tupleValue] + data[_index] = value + this.updateSetting(data, this.settingGroup.group, this.settingGroup.key, input, this.setting.type) + } + }, processTwoTypeValue(value, input) { if (value === true) { const data = input === ':truncate' ? 0 : '' diff --git a/src/views/settings/components/inputComponents/index.js b/src/views/settings/components/inputComponents/index.js index f7801b8e8ae55bafdb39b4a32255ede5b447bed4..be8097d2c4f941f4ddf1e7867b1ee90466dbc6db 100644 --- a/src/views/settings/components/inputComponents/index.js +++ b/src/views/settings/components/inputComponents/index.js @@ -1,8 +1,8 @@ +export { default as BooleanCombinedInput } from './BooleanCombinedInput' export { default as EditableKeywordInput } from './EditableKeywordInput' export { default as EditorInput } from './EditorInput' export { default as IconsInput } from './IconsInput' export { default as ImageUploadInput } from './ImageUploadInput' -export { default as LinkFormatterInput } from './LinkFormatterInput' export { default as MascotsInput } from './MascotsInput' export { default as ProxyUrlInput } from './ProxyUrlInput' export { default as PruneInput } from './PruneInput' diff --git a/src/views/settings/components/tabs.js b/src/views/settings/components/tabs.js index 6871ca9bb67efbe884f354fe58a54c5478dba160..e3a98a3c84f048a347f0825e0f73828f938fc83d 100644 --- a/src/views/settings/components/tabs.js +++ b/src/views/settings/components/tabs.js @@ -26,7 +26,7 @@ export const tabs = description => { }, 'http': { label: 'settings.http', - settings: [':cors_plug', ':http', ':fed_sockets', ':http_security', ':web_cache_ttl'] + settings: [':cors_plug', ':http', ':http_security', ':web_cache_ttl'] }, 'instance': { label: 'settings.instance', @@ -78,7 +78,7 @@ export const tabs = description => { }, 'other': { label: 'settings.other', - settings: [':mime', 'Pleroma.User.Backup', 'Pleroma.Web.Plugs.RemoteIp', ':modules', 'Pleroma.Web.ApiSpec.CastAndValidate', ':terms_of_services'] + settings: [':mime', 'Pleroma.User.Backup', 'Pleroma.Web.Plugs.RemoteIp', 'Pleroma.Web.Endpoint.MetricsExporter', ':modules', 'Pleroma.Web.ApiSpec.CastAndValidate', ':terms_of_services'] } } } diff --git a/src/views/settings/styles/main.scss b/src/views/settings/styles/main.scss index db55ac707aab9e54948b0e4b0aed6e19daa68c59..c55208ad22c33523707066a6970bede0b685e858 100644 --- a/src/views/settings/styles/main.scss +++ b/src/views/settings/styles/main.scss @@ -352,6 +352,15 @@ line-height: 20px; margin-right: 15px } + .tuple-input { + margin-right: 15px; + } + .tuple-input:last-child { + margin-right: 0; + } + .tuple-input-container { + display: flex; + } .upload-container { display: flex; align-items: baseline;