Ability to remove settings from db

This commit is contained in:
Angelina Filippova 2020-01-21 18:16:56 +00:00
parent 9206538f61
commit 2d86e26aea
39 changed files with 635 additions and 489 deletions

View file

@ -65,7 +65,6 @@
"vue-i18n": "^8.9.0",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.2",
"vue2-ace-editor": "^0.0.13",
"vuedraggable": "^2.16.0",
"vuex": "3.0.1",
"xlsx": "^0.11.16"

View file

@ -40,16 +40,4 @@ export async function removeSettings(configs, authHost, token) {
})
}
export async function uploadMedia(file, authHost, token) {
const formData = new FormData()
formData.append('file', file)
return await request({
baseURL: baseName(authHost),
url: `/api/v1/media`,
method: 'post',
headers: authHeaders(token),
data: formData
})
}
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}

View file

@ -339,12 +339,10 @@ export default {
mediaProxy: 'Media Proxy',
metadata: 'Metadata',
gopher: 'Gopher',
endpoint: 'Endpoint',
jobQueue: 'Job queue',
webPush: 'Web push encryption',
esshd: 'BBS / SSH access',
rateLimiters: 'Rate limiters',
database: 'Database',
other: 'Other',
relays: 'Relays',
follow: 'Follow',
@ -383,6 +381,7 @@ export default {
file: 'File',
update: 'Update',
remove: 'Remove',
removeFromDB: 'Remove setting from the DB',
selectLocalPack: 'Select the local pack to copy to',
localPack: 'Local pack',
specifyShortcode: 'Specify a custom shortcode',
@ -405,7 +404,8 @@ export default {
nowNewPacksToImport: 'No new packs to import',
successfullyUpdated: 'Successfully updated',
metadatLowerCase: 'metadata',
files: 'files'
files: 'files',
successfullyRemoved: 'Setting removed successfully!'
},
invites: {
inviteTokens: 'Invite tokens',

View file

@ -1,3 +1,5 @@
import _ from 'lodash'
export const checkPartialUpdate = (settings, updatedSettings, description) => {
return Object.keys(updatedSettings).reduce((acc, group) => {
acc[group] = Object.keys(updatedSettings[group]).reduce((acc, key) => {
@ -20,12 +22,22 @@ export const checkPartialUpdate = (settings, updatedSettings, description) => {
}, {})
}
const getCurrentValue = (object, keys) => {
if (keys.length === 0) {
return object
const getCurrentValue = (type, value, path) => {
if (type === 'state') {
return _.get(value, path)
} else {
const [firstSettingName, ...restKeys] = path
const firstSegment = value[firstSettingName]
if (restKeys.length === 0 || !firstSegment) {
return firstSegment || false
} else {
const secondSegment = (value, keys) => {
const [element, ...rest] = keys
return keys.length === 0 ? value : secondSegment(value[1][element], rest)
}
return secondSegment(firstSegment, restKeys)
}
}
const [currentKey, ...restKeys] = keys
return getCurrentValue(object[currentKey], restKeys)
}
const getValueWithoutKey = (key, [type, value]) => {
@ -136,24 +148,26 @@ export const processNested = (valueForState, valueForUpdatedSettings, group, par
const [{ key, type }, ...otherParents] = parents
const path = [group, parentKey, ...parents.reverse().map(parent => parent.key).slice(0, -1)]
let updatedValueForState = valueExists(settings, path)
? { ...getCurrentValue(settings[group][parentKey], parents.map(el => el.key).slice(0, -1)),
let updatedValueForState = valueExists('state', settings, path)
? { ...getCurrentValue('state', settings[group][parentKey], parents.map(el => el.key).slice(0, -1)),
...{ [key]: valueForState }}
: { [key]: valueForState }
let updatedValueForUpdatedSettings = valueExists(updatedSettings, path)
? { ...getCurrentValue(updatedSettings[group][parentKey], parents.map(el => el.key).slice(0, -1))[1],
let updatedValueForUpdatedSettings = valueExists('updatedSettings', updatedSettings, path)
? { ...getCurrentValue('updatedSettings', updatedSettings[group][parentKey], parents.map(el => el.key).slice(0, -1))[1],
...{ [key]: [type, valueForUpdatedSettings] }}
: { [key]: [type, valueForUpdatedSettings] }
if (group === ':mime' && parents[0].key === ':types') {
updatedValueForState = { ...settings[group][parents[0].key].value, ...updatedValueForState }
updatedValueForUpdatedSettings = {
...Object.keys(settings[group][parents[0].key].value)
updatedValueForState = settings[group][parents[0].key]
? { ...settings[group][parents[0].key].value, ...updatedValueForState }
: updatedValueForState
updatedValueForUpdatedSettings = settings[group][parents[0].key]
? { ...Object.keys(settings[group][parents[0].key].value)
.reduce((acc, el) => {
return { ...acc, [el]: [type, settings[group][parents[0].key].value[el]] }
}, {}),
...updatedValueForUpdatedSettings
}
...updatedValueForUpdatedSettings }
: updatedValueForUpdatedSettings
}
return otherParents.length === 1
@ -161,12 +175,25 @@ export const processNested = (valueForState, valueForUpdatedSettings, group, par
: processNested(updatedValueForState, updatedValueForUpdatedSettings, group, parentKey, otherParents, settings, updatedSettings)
}
const valueExists = (value, path) => {
if (path.length === 0) {
const valueExists = (type, value, path) => {
if (type === 'state') {
return _.get(value, path)
} else {
const [group, key, firstSettingName, ...restKeys] = path
const firstSegment = _.get(value, [group, key, firstSettingName])
if (restKeys.length === 0 || !firstSegment) {
return firstSegment || false
} else {
const secondSegment = (value, keys) => {
if (keys.length === 0) {
return true
}
const [element, ...rest] = path
return value[element] ? valueExists(value[element], rest) : false
const [element, ...rest] = keys
return value[1][element] ? secondSegment(value[1][element], rest) : false
}
return secondSegment(firstSegment, restKeys)
}
}
}
export const valueHasTuples = (key, value) => {
@ -218,8 +245,6 @@ const wrapValues = (settings, currentState) => {
} else if (setting === ':ip') {
const ip = value.split('.').map(s => parseInt(s, 10))
return { 'tuple': [setting, { 'tuple': ip }] }
} else if (setting === ':ssl_options') {
return { 'tuple': [setting, wrapValues(value, currentState)] }
} else if (setting === ':args') {
const index = value.findIndex(el => el === 'implode')
const updatedArray = value.slice()

View file

@ -1,32 +1,25 @@
import { fetchDescription, fetchSettings, removeSettings, updateSettings, uploadMedia } from '@/api/settings'
import { fetchDescription, fetchSettings, removeSettings, updateSettings } from '@/api/settings'
import { checkPartialUpdate, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
import _ from 'lodash'
const settings = {
state: {
description: [],
settings: {
':auto_linker': {},
':cors_plug': {},
':esshd': {},
':http_signatures': {},
':logger': {},
':mime': {},
':phoenix': {},
':pleroma': {},
':prometheus': {},
':quack': {},
':tesla': {},
':ueberauth': {},
':web_push_encryption': {}
},
settings: {},
updatedSettings: {},
ignoredIfNotEnabled: ['enabled', 'handler', 'password_authenticator', 'port', 'priv_dir'],
db: {},
loading: true
},
mutations: {
CLEAR_UPDATED_SETTINGS: (state) => {
state.updatedSettings = {}
},
REMOVE_SETTING_FROM_UPDATED: (state, { group, key, subkeys }) => {
if (_.get(state.updatedSettings, [group, key, subkeys[0]])) {
const { [subkeys[0]]: value, ...updatedSettings } = state.updatedSettings[group][key]
state.updatedSettings = updatedSettings
}
},
SET_DESCRIPTION: (state, data) => {
state.description = data
},
@ -38,10 +31,19 @@ const settings = {
const parsedValue = valueHasTuples(key, value)
? { value: parseNonTuples(key, value) }
: parseTuples(value, key)
acc[group][key] = { ...acc[group][key], ...parsedValue }
acc[group] = acc[group] ? { ...acc[group], [key]: parsedValue } : { [key]: parsedValue }
return acc
}, state.settings)
}, {})
const newDbSettings = data.reduce((acc, { group, key, db }) => {
if (db) {
acc[group] = acc[group] ? { ...acc[group], [key]: db } : { [key]: db }
}
return acc
}, {})
state.settings = newSettings
state.db = newDbSettings
},
UPDATE_SETTINGS: (state, { group, key, input, value, type }) => {
const updatedSetting = !state.updatedSettings[group] || (key === 'Pleroma.Emails.Mailer' && input === ':adapter')
@ -66,8 +68,12 @@ const settings = {
commit('SET_SETTINGS', response.data.configs)
commit('SET_LOADING', false)
},
async RemoveSetting({ getters }, configs) {
async RemoveSetting({ commit, getters }, configs) {
await removeSettings(configs, getters.authHost, getters.token)
const response = await fetchSettings(getters.authHost, getters.token)
const { group, key, subkeys } = configs[0]
commit('SET_SETTINGS', response.data.configs)
commit('REMOVE_SETTING_FROM_UPDATED', { group, key, subkeys: subkeys || [] })
},
async SubmitChanges({ getters, commit, state }) {
const updatedData = checkPartialUpdate(state.settings, state.updatedSettings, state.description)
@ -75,7 +81,8 @@ const settings = {
return [...acc, ...wrapUpdatedSettings(group, updatedData[group], state.settings)]
}, [])
const response = await updateSettings(configs, getters.authHost, getters.token)
await updateSettings(configs, getters.authHost, getters.token)
const response = await fetchSettings(getters.authHost, getters.token)
commit('SET_SETTINGS', response.data.configs)
commit('CLEAR_UPDATED_SETTINGS')
},
@ -84,24 +91,14 @@ const settings = {
? commit('UPDATE_SETTINGS', { group, key, input, value, type })
: commit('UPDATE_SETTINGS', { group, key: input, input: '_value', value, type })
},
UpdateState({ commit, dispatch, state }, { group, key, input, value }) {
async UpdateState({ commit, getters, state }, { group, key, input, value }) {
if (key === 'Pleroma.Emails.Mailer' && input === ':adapter') {
const subkeys = Object.keys(state.settings[group][key]).filter(el => el !== ':adapter')
const emailsValue = subkeys.map(el => {
return { 'tuple': [el, state.settings[group][key][el]] }
})
dispatch('RemoveSetting', [{ group, key, value: emailsValue, delete: true, subkeys }])
await removeSettings([{ group, key, delete: true, subkeys }], getters.authHost, getters.token)
}
key
? commit('UPDATE_STATE', { group, key, input, value })
: commit('UPDATE_STATE', { group, key: input, input: 'value', value })
},
async UploadMedia({ dispatch, getters, state }, { file, tab, inputName, childName }) {
const response = await uploadMedia(file, getters.authHost, getters.token)
const updatedValue = childName
? { ...state.settings[tab][inputName], ...{ [childName]: response.data.url }}
: response.data.url
dispatch('UpdateSettings', { tab, data: { [inputName]: updatedValue }})
}
}
}

View file

@ -17,6 +17,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'ActivityPub',
@ -29,13 +30,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':activitypub')
},
activitypubData() {
return this.settings.settings[':pleroma'][':activitypub']
return _.get(this.settings.settings, [':pleroma', ':activitypub']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.$store.state.settings.loading
@ -44,7 +45,7 @@ export default {
return this.settings.description.find(setting => setting.key === ':user')
},
userData() {
return this.settings.settings[':pleroma'][':user']
return _.get(this.settings.settings, [':pleroma', ':user']) || {}
}
},
methods: {

View file

@ -25,6 +25,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Authentication',
@ -37,19 +38,19 @@ export default {
return this.settings.description.find(setting => setting.key === ':auth')
},
authData() {
return this.settings.settings[':pleroma'][':auth']
return _.get(this.settings.settings, [':pleroma', ':auth']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
ldap() {
return this.settings.description.find(setting => setting.key === ':ldap')
},
ldapData() {
return this.settings.settings[':pleroma'][':ldap']
return _.get(this.settings.settings, [':pleroma', ':ldap']) || {}
},
loading() {
return this.settings.loading
@ -58,13 +59,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':oauth2')
},
oauth2Data() {
return this.settings.settings[':pleroma'][':oauth2']
return _.get(this.settings.settings, [':pleroma', ':oauth2']) || {}
},
pleromaAuthenticator() {
return this.settings.description.find(setting => setting.description === 'Authenticator')
},
pleromaAuthenticatorData() {
return this.settings.settings[':pleroma']['Pleroma.Web.Auth.Authenticator']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Auth.Authenticator']) || {}
}
},
methods: {

View file

@ -11,6 +11,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'AutoLinker',
@ -23,13 +24,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':opts')
},
autoLinkerData() {
return this.settings.settings[':auto_linker'][':opts']
return _.get(this.settings.settings, [':auto_linker', ':opts']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading

View file

@ -17,6 +17,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Captcha',
@ -29,7 +30,7 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.Captcha')
},
captchaData() {
return this.settings.settings[':pleroma']['Pleroma.Captcha']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Captcha']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
@ -38,10 +39,10 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.Captcha.Kocaptcha')
},
kocaptchaData() {
return this.settings.settings[':pleroma']['Pleroma.Captcha.Kocaptcha']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Captcha.Kocaptcha']) || {}
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading

View file

@ -1,59 +0,0 @@
<template>
<div v-if="!loading">
<el-form ref="databaseData" :model="databaseData" :label-width="labelWidth">
<setting :setting-group="database" :data="databaseData"/>
<el-form-item>
<el-button type="primary" @click="onSubmit">Submit</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
export default {
name: 'Database',
components: { Setting },
computed: {
...mapGetters([
'settings'
]),
database() {
return this.settings.description.find(setting => setting.key === ':database')
},
databaseData() {
return this.settings.settings[':pleroma'][':database']
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
},
loading() {
return this.settings.loading
}
},
methods: {
async onSubmit() {
try {
await this.$store.dispatch('SubmitChanges')
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.success')
})
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@include settings
</style>

View file

@ -1,81 +0,0 @@
<template>
<div v-if="!loading">
<el-form ref="endpointData" :model="endpointData" :label-width="labelWidth">
<setting :setting-group="endpoint" :data="endpointData"/>
</el-form>
<div class="line"/>
<el-form ref="endpointMetricsExporter" :model="endpointMetricsExporterData" :label-width="labelWidth">
<setting :setting-group="endpointMetricsExporter" :data="endpointMetricsExporterData"/>
</el-form>
<div class="line"/>
<el-form ref="remoteIp" :model="remoteIpData" :label-width="labelWidth">
<setting :setting-group="remoteIp" :data="remoteIpData"/>
<el-form-item>
<el-button type="primary" @click="onSubmit">Submit</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
export default {
name: 'Endpoint',
components: {
Setting
},
computed: {
...mapGetters([
'settings'
]),
endpoint() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Endpoint')
},
endpointData() {
return this.settings.settings[':pleroma']['Pleroma.Web.Endpoint']
},
endpointMetricsExporter() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Endpoint.MetricsExporter')
},
endpointMetricsExporterData() {
return this.settings.settings[':prometheus']['Pleroma.Web.Endpoint.MetricsExporter']
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
},
loading() {
return this.settings.loading
},
remoteIp() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Plugs.RemoteIp')
},
remoteIpData() {
return this.settings.settings[':pleroma']['Pleroma.Plugs.RemoteIp']
}
},
methods: {
async onSubmit() {
try {
await this.$store.dispatch('SubmitChanges')
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.success')
})
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@include settings
</style>

View file

@ -20,6 +20,7 @@
import i18n from '@/lang'
import { mapGetters } from 'vuex'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Esshd',
@ -32,13 +33,13 @@ export default {
return this.settings.description.find(setting => setting.group === ':esshd')
},
esshdData() {
return this.settings.settings[':esshd']
return _.get(this.settings.settings, [':esshd']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading

View file

@ -1,11 +1,6 @@
<template>
<div v-if="!loading">
<el-form ref="frontendData" :model="frontendData" :label-width="labelWidth">
<el-form-item>
<p class="expl">This form can be used to configure a keyword list that keeps the configuration data for any kind of frontend.
By default, settings for <span class="code">pleroma_fe</span> and <span class="code">masto_fe</span> are configured.
If you want to add your own configuration your settings need to be complete as they will override the defaults.</p>
</el-form-item>
<setting :setting-group="frontend" :data="frontendData"/>
</el-form>
<el-form ref="assetsData" :model="assetsData" :label-width="labelWidth">
@ -36,6 +31,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Frontend',
@ -48,37 +44,37 @@ export default {
return this.settings.description.find(setting => setting.key === ':assets')
},
assetsData() {
return this.settings.settings[':pleroma'][':assets']
return _.get(this.settings.settings, [':pleroma', ':assets']) || {}
},
chat() {
return this.settings.description.find(setting => setting.key === ':chat')
},
chatData() {
return this.settings.settings[':pleroma'][':chat']
return _.get(this.settings.settings, [':pleroma', ':chat']) || {}
},
emoji() {
return this.settings.description.find(setting => setting.key === ':emoji')
},
emojiData() {
return this.settings.settings[':pleroma'][':emoji']
return _.get(this.settings.settings, [':pleroma', ':emoji']) || {}
},
frontend() {
return this.settings.description.find(setting => setting.key === ':frontend_configurations')
},
frontendData() {
return this.settings.settings[':pleroma'][':frontend_configurations']
return _.get(this.settings.settings, [':pleroma', ':frontend_configurations']) || {}
},
markup() {
return this.settings.description.find(setting => setting.key === ':markup')
},
markupData() {
return this.settings.settings[':pleroma'][':markup']
return _.get(this.settings.settings, [':pleroma', ':markup']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading

View file

@ -11,6 +11,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Gopher',
@ -23,13 +24,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':gopher')
},
gopherData() {
return this.settings.settings[':pleroma'][':gopher']
return _.get(this.settings.settings, [':pleroma', ':gopher']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading

View file

@ -29,6 +29,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'HTTP',
@ -41,31 +42,31 @@ export default {
return this.settings.description.find(setting => setting.group === ':cors_plug')
},
corsPlugData() {
return this.settings.settings[':cors_plug']
return _.get(this.settings.settings, [':cors_plug']) || {}
},
http() {
return this.settings.description.find(setting => setting.key === ':http')
},
httpData() {
return this.settings.settings[':pleroma'][':http']
return _.get(this.settings.settings, [':pleroma', ':http']) || {}
},
httpSecurity() {
return this.settings.description.find(setting => setting.key === ':http_security')
},
httpSecurityData() {
return this.settings.settings[':pleroma'][':http_security']
return _.get(this.settings.settings, [':pleroma', ':http_security']) || {}
},
httpSignatures() {
return this.settings.description.find(setting => setting.group === ':http_signatures')
},
httpSignaturesData() {
return this.settings.settings[':http_signatures']
return _.get(this.settings.settings, [':http_signatures']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -74,7 +75,7 @@ export default {
return this.settings.description.find(setting => setting.key === ':web_cache_ttl')
},
webCacheTtlData() {
return this.settings.settings[':pleroma'][':web_cache_ttl']
return _.get(this.settings.settings, [':pleroma', ':web_cache_ttl']) || {}
}
},
methods: {

View file

@ -1,7 +1,13 @@
<template>
<el-form-item :label="setting.label" :label-width="customLabelWidth" :class="labelClass">
<el-form-item :label-width="customLabelWidth" :class="labelClass">
<span slot="label">
{{ setting.label }}
<el-tooltip v-if="canBeDeleted" :content="$t('settings.removeFromDB')" placement="bottom-end">
<el-button icon="el-icon-delete" circle size="mini" style="margin-left:5px" @click="removeSetting"/>
</el-tooltip>
</span>
<el-input
v-if="setting.type === 'string'"
v-if="setting.type === 'string' || (setting.type.includes('string') && setting.type.includes('atom'))"
:value="inputValue"
:placeholder="setting.suggestions ? setting.suggestions[0] : null"
@input="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
@ -36,13 +42,6 @@
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)">
<el-option v-for="(option, index) in setting.suggestions" :key="index" :value="option"/>
</el-select>
<editor
v-if="setting.key === ':dispatch'"
v-model="editorContent"
height="150"
width="100%"
lang="elixir"
theme="chrome"/>
<el-input
v-if="setting.key === ':ip'"
:value="inputValue"
@ -62,7 +61,7 @@
:setting-parent="[...settingParent, subSetting]"
:setting="subSetting"
:data="data[setting.key]"
:custom-label-width="'100px'"
:custom-label-width="'140px'"
:label-class="'center-label'"
:input-class="'keyword-inner-input'"
:nested="true"/>
@ -70,11 +69,10 @@
</div>
<!-- special inputs -->
<auto-linker-input v-if="settingGroup.group === ':auto_linker'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<mascots-input v-if="setting.key === ':mascots'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="data" :setting-group="settingGroup" :setting="setting"/>
<icons-input v-if="setting.key === ':icons'" :data="data[':icons']" :setting-group="settingGroup" :setting="setting"/>
<mascots-input v-if="setting.key === ':mascots'" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
<editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
<icons-input v-if="setting.key === ':icons'" :data="iconsData" :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"/>
<!-- <ssl-options-input v-if="setting.key === ':ssl_options'" :setting-group="settingGroup" :setting-parent="settingParent" :setting="setting" :data="data" :nested="true" :custom-label-width="'100px'"/> -->
<multiple-select v-if="setting.key === ':backends' || setting.key === ':args'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<prune-input v-if="setting.key === ':prune'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting"/>
<rate-limit-input v-if="settingGroup.key === ':rate_limit'" :data="data" :setting-group="settingGroup" :setting="setting"/>
@ -84,16 +82,14 @@
</template>
<script>
import AceEditor from 'vue2-ace-editor'
import 'brace/mode/elixir'
import 'default-passive-events'
import { AutoLinkerInput, EditableKeywordInput, IconsInput, MascotsInput, MultipleSelect, ProxyUrlInput, PruneInput, RateLimitInput, SslOptionsInput } from './inputComponents'
import i18n from '@/lang'
import { AutoLinkerInput, EditableKeywordInput, IconsInput, MascotsInput, MultipleSelect, ProxyUrlInput, PruneInput, RateLimitInput } from './inputComponents'
import { processNested } from '@/store/modules/normalizers'
import _ from 'lodash'
export default {
name: 'Inputs',
components: {
editor: AceEditor,
AutoLinkerInput,
EditableKeywordInput,
IconsInput,
@ -101,8 +97,7 @@ export default {
MultipleSelect,
ProxyUrlInput,
PruneInput,
RateLimitInput,
SslOptionsInput
RateLimitInput
},
props: {
customLabelWidth: {
@ -159,13 +154,13 @@ export default {
}
},
computed: {
editorContent: {
get: function() {
return this.data[this.setting.key] ? this.data[this.setting.key][0] : ''
canBeDeleted() {
const { group, key } = this.settingGroup
return _.get(this.$store.state.settings.db, [group, key]) &&
this.$store.state.settings.db[group][key].includes(this.setting.key)
},
set: function(value) {
this.processNestedData([value], this.settingGroup.group, this.settingGroup.key, this.settingParent)
}
iconsData() {
return Array.isArray(this.data[':icons']) ? this.data[':icons'] : []
},
inputValue() {
if ([':esshd', ':cors_plug', ':quack', ':http_signatures', ':tesla'].includes(this.settingGroup.group) &&
@ -178,7 +173,7 @@ export default {
this.setting.key === ':admin_token') {
return this.data.value
} else if (this.settingGroup.group === ':mime' && this.settingParent[0].key === ':types') {
return this.data.value[this.setting.key]
return this.data.value ? this.data.value[this.setting.key] : []
} else if (this.setting.type === 'atom') {
return this.data[this.setting.key] && this.data[this.setting.key][0] === ':' ? this.data[this.setting.key].substr(1) : this.data[this.setting.key]
} else {
@ -186,7 +181,10 @@ export default {
}
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
keywordData() {
return Array.isArray(this.data) ? this.data : []
},
rewritePolicyValue() {
return typeof this.data[this.setting.key] === 'string' ? [this.data[this.setting.key]] : this.data[this.setting.key]
@ -215,6 +213,20 @@ export default {
this.$store.dispatch('UpdateState',
{ group, key: parentKey, input: setting.key, value: valueForState })
},
async removeSetting() {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [this.setting.key] }]
: [{ group: this.settingGroup.group, key: this.setting.key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
})
},
renderMultipleSelect(type) {
return Array.isArray(type) && this.setting.key !== ':backends' && this.setting.key !== ':args' && (
type.includes('module') ||

View file

@ -42,6 +42,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Instance',
@ -56,25 +57,25 @@ export default {
return this.settings.description.find(setting => setting.description === `Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter`)
},
adminTokenData() {
return this.settings.settings[':pleroma'][':admin_token']
return _.get(this.settings.settings, [':pleroma', ':admin_token']) || {}
},
fetchInitialPosts() {
return this.settings.description.find(setting => setting.key === ':fetch_initial_posts')
},
fetchInitialPostsData() {
return this.settings.settings[':pleroma'][':fetch_initial_posts']
return _.get(this.settings.settings, [':pleroma', ':fetch_initial_posts']) || {}
},
instance() {
return this.settings.description.find(setting => setting.key === ':instance')
},
instanceData() {
return this.settings.settings[':pleroma'][':instance']
return _.get(this.settings.settings, [':pleroma', ':instance']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -83,31 +84,31 @@ export default {
return this.settings.description.find(setting => setting.key === ':manifest')
},
manifestData() {
return this.settings.settings[':pleroma'][':manifest']
return _.get(this.settings.settings, [':pleroma', ':manifest']) || {}
},
pleromaUser() {
return this.settings.description.find(setting => setting.key === 'Pleroma.User')
},
pleromaUserData() {
return this.settings.settings[':pleroma']['Pleroma.User']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.User']) || {}
},
scheduledActivity() {
return this.$store.state.settings.description.find(setting => setting.key === 'Pleroma.ScheduledActivity')
},
scheduledActivityData() {
return this.settings.settings[':pleroma']['Pleroma.ScheduledActivity']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.ScheduledActivity']) || {}
},
suggestions() {
return this.$store.state.settings.description.find(setting => setting.key === ':suggestions')
},
suggestionsData() {
return this.settings.settings[':pleroma'][':suggestions']
return _.get(this.settings.settings, [':pleroma', ':suggestions']) || {}
},
uriSchemes() {
return this.$store.state.settings.description.find(setting => setting.key === ':uri_schemes')
},
uriSchemesData() {
return this.settings.settings[':pleroma'][':uri_schemes']
return _.get(this.settings.settings, [':pleroma', ':uri_schemes']) || {}
}
},
methods: {

View file

@ -19,6 +19,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'JobQueue',
@ -31,13 +32,13 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.ActivityExpiration')
},
activityExpirationData() {
return this.settings.settings[':pleroma']['Pleroma.ActivityExpiration']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.ActivityExpiration']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -46,13 +47,13 @@ export default {
return this.settings.description.find(setting => setting.key === 'Oban')
},
obanQueuesData() {
return this.settings.settings[':pleroma']['Oban']
return _.get(this.settings.settings, [':pleroma', 'Oban']) || {}
},
workers() {
return this.settings.description.find(setting => setting.key === ':workers')
},
workersData() {
return this.settings.settings[':pleroma'][':workers']
return _.get(this.settings.settings, [':pleroma', ':workers']) || {}
}
},
methods: {

View file

@ -26,6 +26,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Logger',
@ -38,19 +39,19 @@ export default {
return this.settings.description.find(setting => setting.key === ':console')
},
consoleData() {
return this.settings.settings[':logger'][':console']
return _.get(this.settings.settings, [':logger', ':console']) || {}
},
exsyslogger() {
return this.settings.description.find(setting => setting.key === ':ex_syslogger')
},
exsysloggerData() {
return this.settings.settings[':logger'][':ex_syslogger']
return _.get(this.settings.settings, [':logger', ':ex_syslogger']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -59,13 +60,13 @@ export default {
return this.settings.description.find(setting => setting.group === ':logger')
},
loggerData() {
return this.settings.settings[':logger'][':backends']
return _.get(this.settings.settings, [':logger', ':backends']) || {}
},
quack() {
return this.settings.description.find(setting => setting.group === ':quack')
},
quackData() {
return this.settings.settings[':quack']
return _.get(this.settings.settings, [':quack']) || {}
}
},
methods: {

View file

@ -39,6 +39,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'MRF',
@ -51,7 +52,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -60,49 +61,49 @@ export default {
return this.settings.description.find(setting => setting.key === ':mrf_simple')
},
mrfSimpleData() {
return this.settings.settings[':pleroma'][':mrf_simple']
return _.get(this.settings.settings, [':pleroma', ':mrf_simple']) || {}
},
mrfRejectnonpublic() {
return this.settings.description.find(setting => setting.key === ':mrf_rejectnonpublic')
},
mrfRejectnonpublicData() {
return this.settings.settings[':pleroma'][':mrf_rejectnonpublic']
return _.get(this.settings.settings, [':pleroma', ':mrf_rejectnonpublic']) || {}
},
mrfHellthread() {
return this.settings.description.find(setting => setting.key === ':mrf_hellthread')
},
mrfHellthreadData() {
return this.settings.settings[':pleroma'][':mrf_hellthread']
return _.get(this.settings.settings, [':pleroma', ':mrf_hellthread']) || {}
},
mrfKeyword() {
return this.settings.description.find(setting => setting.key === ':mrf_keyword')
},
mrfKeywordData() {
return this.settings.settings[':pleroma'][':mrf_keyword']
return _.get(this.settings.settings, [':pleroma', ':mrf_keyword']) || {}
},
mrfSubchain() {
return this.settings.description.find(setting => setting.key === ':mrf_subchain')
},
mrfSubchainData() {
return this.settings.settings[':pleroma'][':mrf_subchain']
return _.get(this.settings.settings, [':pleroma', ':mrf_subchain']) || {}
},
mrfMention() {
return this.settings.description.find(setting => setting.key === ':mrf_mention')
},
mrfMentionData() {
return this.settings.settings[':pleroma'][':mrf_mention']
return _.get(this.settings.settings, [':pleroma', ':mrf_mention']) || {}
},
mrfNormalizeMarkup() {
return this.settings.description.find(setting => setting.key === ':mrf_normalize_markup')
},
mrfNormalizeMarkupData() {
return this.settings.settings[':pleroma'][':mrf_normalize_markup']
return _.get(this.settings.settings, [':pleroma', ':mrf_normalize_markup']) || {}
},
mrfVocabulary() {
return this.settings.description.find(setting => setting.key === ':mrf_vocabulary')
},
mrfVocabularyData() {
return this.settings.settings[':pleroma'][':mrf_vocabulary']
return _.get(this.settings.settings, [':pleroma', ':mrf_vocabulary']) || {}
}
},
methods: {

View file

@ -20,6 +20,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Mailer',
@ -34,13 +35,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':email_notifications')
},
emailNotificationsData() {
return this.settings.settings[':pleroma'][':email_notifications']
return _.get(this.settings.settings, [':pleroma', ':email_notifications']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.$store.state.settings.loading
@ -49,13 +50,13 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.Emails.Mailer')
},
mailerData() {
return this.settings.settings[':pleroma']['Pleroma.Emails.Mailer']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.Mailer']) || {}
},
userEmail() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Emails.UserEmail')
},
userEmailData() {
return this.settings.settings[':pleroma']['Pleroma.Emails.UserEmail']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.UserEmail']) || {}
}
},
methods: {

View file

@ -11,6 +11,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'MediaProxy',
@ -23,7 +24,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -32,7 +33,7 @@ export default {
return this.settings.description.find(setting => setting.key === ':media_proxy')
},
mediaProxyData() {
return this.settings.settings[':pleroma'][':media_proxy']
return _.get(this.settings.settings, [':pleroma', ':media_proxy']) || {}
}
},
methods: {

View file

@ -17,6 +17,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Metadata',
@ -29,7 +30,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -38,13 +39,13 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Metadata')
},
metadataData() {
return this.settings.settings[':pleroma']['Pleroma.Web.Metadata']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Metadata']) || {}
},
richMedia() {
return this.settings.description.find(setting => setting.key === ':rich_media')
},
richMediaData() {
return this.settings.settings[':pleroma'][':rich_media']
return _.get(this.settings.settings, [':pleroma', ':rich_media']) || {}
}
},
methods: {

View file

@ -6,6 +6,9 @@
<div class="line"/>
<el-form ref="mimeTypes" :model="mimeTypesData" :label-width="labelWidth">
<setting :setting-group="mimeTypes" :data="mimeTypesData"/>
</el-form>
<el-form ref="remoteIp" :model="remoteIpData" :label-width="labelWidth">
<setting :setting-group="remoteIp" :data="remoteIpData"/>
<el-form-item>
<el-button type="primary" @click="onSubmit">Submit</el-button>
</el-form-item>
@ -17,6 +20,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Other',
@ -29,7 +33,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -38,13 +42,19 @@ export default {
return this.settings.description.find(setting => setting.group === ':mime')
},
mimeTypesData() {
return this.settings.settings[':mime']
return _.get(this.settings.settings, [':mime']) || {}
},
remoteIp() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Plugs.RemoteIp')
},
remoteIpData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Plugs.RemoteIp']) || {}
},
teslaAdapter() {
return this.settings.description.find(setting => setting.group === ':tesla')
},
teslaAdapterData() {
return this.settings.settings[':tesla']
return _.get(this.settings.settings, [':tesla']) || {}
}
},
methods: {

View file

@ -11,6 +11,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'RateLimiters',
@ -23,13 +24,13 @@ export default {
return this.settings.description.find(setting => setting.key === ':rate_limit')
},
rateLimitersData() {
return this.settings.settings[':pleroma'][':rate_limit']
return _.get(this.settings.settings, [':pleroma', ':rate_limit']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.$store.state.settings.loading

View file

@ -29,7 +29,22 @@
:nested="false"/>
</div>
<div v-if="compound(setting)">
<el-form-item :label="`${setting.label}:`"/>
<div v-if="!setting.children">
<inputs
:setting-group="settingGroup"
:setting="setting"
:data="data[setting.key]"
:nested="true"/>
</div>
<div v-else>
<el-form-item>
<span slot="label">
{{ setting.label }}:
<el-tooltip v-if="canBeDeleted(setting.key)" :content="$t('settings.removeFromDB')" placement="bottom-end">
<el-button icon="el-icon-delete" circle size="mini" style="margin-left:5px" @click="removeSetting(setting.key)"/>
</el-tooltip>
</span>
</el-form-item>
<div v-for="subSetting in setting.children" :key="subSetting.key">
<inputs
:setting-group="settingGroup"
@ -38,8 +53,6 @@
:data="data[setting.key]"
:nested="true"/>
</div>
<div v-if="!setting.children">
<inputs :setting-group="settingGroup" :setting="setting" :data="data[setting.key]" :nested="true"/>
</div>
<div class="line"/>
</div>
@ -49,15 +62,13 @@
</template>
<script>
import AceEditor from 'vue2-ace-editor'
import Inputs from './Inputs'
import 'brace/mode/elixir'
import 'default-passive-events'
import i18n from '@/lang'
import _ from 'lodash'
export default {
name: 'Setting',
components: {
editor: AceEditor,
Inputs
},
props: {
@ -84,12 +95,32 @@ export default {
}
},
methods: {
canBeDeleted(settingKey) {
const { group, key } = this.settingGroup
const existingKey = key || settingKey
return _.get(this.$store.state.settings.db, [group, existingKey]) &&
this.$store.state.settings.db[group][existingKey].includes(settingKey)
},
compound({ type, key, children }) {
return type === 'keyword' ||
type === 'map' ||
type.includes('keyword') ||
key === ':replace'
},
async removeSetting(key) {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [key] }]
: [{ group: this.settingGroup.group, key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
})
},
updateSetting(value, tab, input) {
this.$store.dispatch('UpdateSettings', { tab, data: { [input]: value }})
}

View file

@ -11,10 +11,6 @@
<setting :setting-group="uploadersS3" :data="uploadersS3Data"/>
</el-form>
<div class="line"/>
<el-form ref="uploadersMDII" :model="uploadersMDIIData" :label-width="labelWidth">
<setting :setting-group="uploadersMDII" :data="uploadersMDIIData"/>
</el-form>
<div class="line"/>
<el-form ref="uploadFilterMogrify" :model="uploadFilterMogrifyData" :label-width="labelWidth">
<setting :setting-group="uploadFilterMogrify" :data="uploadFilterMogrifyData"/>
</el-form>
@ -32,6 +28,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'Upload',
@ -44,7 +41,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -53,37 +50,31 @@ export default {
return this.settings.description.find(setting => setting.key === 'Pleroma.Upload')
},
uploadData() {
return this.settings.settings[':pleroma']['Pleroma.Upload']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload']) || {}
},
uploadersLocal() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.Local')
},
uploadersLocalData() {
return this.settings.settings[':pleroma']['Pleroma.Uploaders.Local']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Uploaders.Local']) || {}
},
uploadersS3() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.S3')
},
uploadersS3Data() {
return this.settings.settings[':pleroma']['Pleroma.Uploaders.S3']
},
uploadersMDII() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.MDII')
},
uploadersMDIIData() {
return this.settings.settings[':pleroma']['Pleroma.Uploaders.MDII']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Uploaders.S3']) || {}
},
uploadFilterMogrify() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Upload.Filter.Mogrify')
},
uploadFilterMogrifyData() {
return this.settings.settings[':pleroma']['Pleroma.Upload.Filter.Mogrify']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload.Filter.Mogrify']) || {}
},
uploadAnonymizeFilename() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Upload.Filter.AnonymizeFilename')
},
uploadAnonymizeFilenameData() {
return this.settings.settings[':pleroma']['Pleroma.Upload.Filter.AnonymizeFilename']
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload.Filter.AnonymizeFilename']) || {}
}
},
methods: {

View file

@ -11,6 +11,7 @@
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import Setting from './Setting'
import _ from 'lodash'
export default {
name: 'WebPush',
@ -23,7 +24,7 @@ export default {
return this.$store.state.app.device === 'mobile'
},
labelWidth() {
return this.isMobile ? '100px' : '240px'
return this.isMobile ? '100px' : '280px'
},
loading() {
return this.settings.loading
@ -32,7 +33,7 @@ export default {
return this.settings.description.find(setting => setting.key === ':vapid_details')
},
vapidDetailsData() {
return this.settings.settings[':web_push_encryption'][':vapid_details']
return _.get(this.settings.settings, [':web_push_encryption', ':vapid_details']) || {}
}
},
methods: {

View file

@ -2,8 +2,6 @@ export { default as ActivityPub } from './ActivityPub'
export { default as Authentication } from './Authentication'
export { default as AutoLinker } from './AutoLinker'
export { default as Captcha } from './Captcha'
export { default as Database } from './Database'
export { default as Endpoint } from './Endpoint'
export { default as Esshd } from './Esshd'
export { default as Frontend } from './Frontend'
export { default as Gopher } from './Gopher'

View file

@ -32,7 +32,7 @@ export default {
name: 'EditableKeywordInput',
props: {
data: {
type: [Object, Array],
type: Array,
default: function() {
return {}
}
@ -95,7 +95,7 @@ export default {
updateSetting(value, group, key, input, type) {
const updatedSettings = type !== 'map'
? value.reduce((acc, element) => {
return { ...acc, [Object.keys(element)[0]]: [['list'], Object.values(element)[0].value] }
return { ...acc, [Object.keys(element)[0]]: ['list', Object.values(element)[0].value] }
}, {})
: value.reduce((acc, element) => {
return { ...acc, [Object.keys(element)[0]]: Object.values(element)[0].value }

View file

@ -28,7 +28,7 @@ export default {
name: 'EditableKeywordInput',
props: {
data: {
type: [Object, Array],
type: Array,
default: function() {
return {}
}
@ -45,14 +45,11 @@ export default {
return {}
}
}
},
computed: {
},
methods: {
addIconToIcons() {
const updatedValue = [...this.data, [{ key: '', value: '', id: this.generateID() }]]
this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key)
this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
},
addValueToIcons(index) {
const updatedValue = this.data.map((icon, i) => {
@ -61,11 +58,11 @@ export default {
}
return icon
})
this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key)
this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
},
deleteIcondRow(index) {
const filteredValues = this.data.filter((icon, i) => i !== index)
this.updateSetting(filteredValues, this.settingGroup.group, this.settingGroup.key, this.setting.key)
this.updateSetting(filteredValues, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
},
generateID() {
return `f${(~~(Math.random() * 1e8)).toString(16)}`

View file

@ -23,7 +23,7 @@ export default {
name: 'MascotsInput',
props: {
data: {
type: [Object, Array],
type: Array,
default: function() {
return {}
}
@ -88,7 +88,7 @@ export default {
updateSetting(value, group, key, input, type) {
const mascotsWithoutIDs = value.reduce((acc, mascot) => {
const { id, ...mascotValue } = Object.values(mascot)[0]
return { ...acc, [Object.keys(mascot)[0]]: mascotValue }
return { ...acc, [Object.keys(mascot)[0]]: ['', mascotValue] }
}, {})
this.$store.dispatch('UpdateSettings', { group, key, input, value: mascotsWithoutIDs, type })
this.$store.dispatch('UpdateState', { group, key, input, value })

View file

@ -5,7 +5,8 @@
:value="rateLimitAllUsers[0]"
placeholder="scale"
class="scale-input"
@input="parseRateLimiter($event, setting.key, 'scale', 'oneLimit', rateLimitAllUsers)"/> :
@input="parseRateLimiter($event, setting.key, 'scale', 'oneLimit', rateLimitAllUsers)"/>
<span>:</span>
<el-input
:value="rateLimitAllUsers[1]"
placeholder="limit"
@ -17,24 +18,26 @@
</div>
</div>
<div v-if="rateLimitAuthUsers">
<el-form-item label="Unauthenticated users:">
<el-form-item label="Unauthenticated users:" label-width="180px" class="rate-limit">
<el-input
:value="rateLimitUnauthUsers[0]"
placeholder="scale"
class="scale-input"
@input="parseRateLimiter($event, setting.key, 'scale', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/> :
@input="parseRateLimiter($event, setting.key, 'scale', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
<span>:</span>
<el-input
:value="rateLimitUnauthUsers[1]"
placeholder="limit"
class="limit-input"
@input="parseRateLimiter($event, setting.key, 'limit', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
</el-form-item>
<el-form-item label="Authenticated users:">
<el-form-item label="Authenticated users:" label-width="180px" class="rate-limit">
<el-input
:value="rateLimitAuthUsers[0]"
placeholder="scale"
class="scale-input"
@input="parseRateLimiter($event, setting.key, 'scale', 'authUserslimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/> :
@input="parseRateLimiter($event, setting.key, 'scale', 'authUserslimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
<span>:</span>
<el-input
:value="rateLimitAuthUsers[1]"
placeholder="limit"

View file

@ -1,93 +0,0 @@
<template>
<div>
<div v-for="subSetting in setting.children" :key="subSetting.key">
<el-form-item :label="subSetting.label" :label-width="customLabelWidth">
<el-select
v-if="subSetting.type.includes('list') && subSetting.type.includes('atom')"
:value="subSettingValue(subSetting)"
multiple
filterable
allow-create
@change="update($event, subSetting.key)">
<el-option v-for="(option, index) in subSetting.suggestions" :key="index" :value="option"/>
</el-select>
<p class="expl">{{ subSetting.description }}</p>
</el-form-item>
</div>
</div>
</template>
<script>
export default {
name: 'SslOptionsInput',
props: {
customLabelWidth: {
type: String,
default: function() {
return this.labelWidth
},
required: false
},
data: {
type: [Object, Array],
default: function() {
return {}
}
},
nested: {
type: Boolean,
default: function() {
return false
}
},
setting: {
type: Object,
default: function() {
return {}
}
},
settingGroup: {
type: Object,
default: function() {
return {}
}
},
settingParent: {
type: Object,
default: function() {
return {}
},
required: false
}
},
methods: {
inputValue(key) {
return this.data[this.setting.key][key]
},
subSettingValue(subSetting) {
return this.data && this.data[this.setting.key] ? this.data[this.setting.key][subSetting.key] : []
},
update(value, childKey) {
const [group, key, parentKey, input] = [this.settingGroup.group, this.settingGroup.key, this.setting.key, this.settingParent.key]
const { updatedSettings, description } = this.$store.state.settings
const type = description
.find(element => element.group === group && element.key === key).children
.find(child => child.key === ':adapter').children.find(child => child.key === ':ssl_options').children
.find(child => child.key === childKey).type
const updatedState = { ...this.data, [parentKey]: { ...this.data[parentKey], [childKey]: value }}
const updatedSetting = !updatedSettings[group] || !updatedSettings[group][key]
? { [parentKey]: ['keyword', { [childKey]: [type, value] }] }
: { ...updatedSettings[group][key][parentKey], [parentKey]: { ...updatedSettings[group][key][parentKey], [childKey]: [type, value] }}
this.$store.dispatch('UpdateSettings', { group, key, input, value: updatedSetting, type: this.settingParent.type })
this.$store.dispatch('UpdateState', { group, key, input, value: updatedState })
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@include settings
</style>

View file

@ -6,4 +6,3 @@ export { default as MultipleSelect } from './MultipleSelect'
export { default as ProxyUrlInput } from './ProxyUrlInput'
export { default as PruneInput } from './PruneInput'
export { default as RateLimitInput } from './RateLimitInput'
export { default as SslOptionsInput } from './SslOptionsInput'

View file

@ -17,15 +17,9 @@
<el-tab-pane :label="$t('settings.captcha')" lazy>
<captcha/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.database')" lazy>
<database/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.emojiPacks')" lazy>
<emoji-packs/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.endpoint')" lazy>
<endpoint/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.frontend')" lazy>
<frontend/>
</el-tab-pane>
@ -76,11 +70,11 @@
</template>
<script>
import { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush } from './components'
import { ActivityPub, Authentication, AutoLinker, Captcha, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush } from './components'
import EmojiPacks from '../emojiPacks/index'
export default {
components: { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush },
components: { ActivityPub, Authentication, AutoLinker, Captcha, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush },
data() {
return {
activeTab: 'instance'

View file

@ -23,6 +23,9 @@
.el-form-item {
margin-right: 30px;
}
.el-form-item .rate-limit {
margin-right: 0;
}
.center-label label {
text-align: center;
}
@ -95,8 +98,8 @@
margin-left: 10px;
}
.limit-input {
width: 48%;
margin: 0 0 5px 8px
width: 47%;
margin: 0 0 5px 1%
}
.line {
width: 100%;
@ -130,6 +133,15 @@
margin-left: 8px;
margin-right: 10px
}
.replacement-input {
width: 80%;
margin-left: 8px;
margin-right: 10px
}
.scale-input {
width: 47%;
margin: 0 1% 5px 0
}
.setting-input {
display: flex;
margin-bottom: 10px;
@ -137,22 +149,13 @@
.single-input {
margin-right: 10px
}
.scale-input {
width: 48%;
margin: 0 8px 5px 0
}
.replacement-input {
width: 80%;
margin-left: 8px;
margin-right: 10px
.ssl-tls-opts {
margin: 36px 0 0 0;
}
.text {
line-height: 20px;
margin-right: 15px
}
.ssl-tls-opts {
margin: 36px 0 0 0;
}
.upload-container {
display: flex;
align-items: baseline;

View file

@ -31,7 +31,8 @@ describe('Wrap settings', () => {
const result = wrapUpdatedSettings(':mime', settings, {})
const expectedResult = [{
group: ':mime',
key: ':types', value: {
key: ':types',
value: {
'application/ld+json': ['activity+json'],
'application/xml': ['xml'],
'application/xrd+xml': ['xrd+xml']
@ -39,4 +40,335 @@ describe('Wrap settings', () => {
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps :mascots setting in group :assets', () => {
const settings = { ':assets': { ':mascots': [['keyword', 'map'], {
':pleroma_fox_tan_shy': ['', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-shy.png' }],
':pleroma_fox_tan': ['', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-smol.png' }],
}]}}
const state = { ':pleroma': { ':assets': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: ':assets',
value: [{ tuple: [':mascots', [
{ tuple: [':pleroma_fox_tan_shy', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-shy.png'}] },
{ tuple: [':pleroma_fox_tan', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-smol.png'}] }
]]}]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps settings with type keyword', () => {
const settings1 = { 'Pleroma.Upload': { ':proxy_opts':
[ 'keyword', {
':redirect_on_failure': ['boolean', true],
':http': ['keyword', { ':proxy_url': [['string', 'tuple'], 'localhost:3090' ]}]
}]
}}
const state1 = { ':pleroma': { 'Pleroma.Upload': {}}}
const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
const expectedResult1 = [{
group: ':pleroma',
key: 'Pleroma.Upload',
value: [{ tuple: [':proxy_opts', [
{ tuple: [':redirect_on_failure', true] },
{ tuple: [':http', [{ tuple: [':proxy_url', 'localhost:3090'] }]] }
]]}]
}]
const settings2 = { ':media_proxy': { ':proxy_opts':
['keyword', {
':max_body_length': ['integer', 26210000],
':http': ['keyword', {
':proxy_url': [['string', 'tuple'], [':socks5', '127.0.0.1', '9020']],
':adapter': ['keyword', {
':ssl_options': ['keyword', {
':versions': [['list', 'atom'], [':tlsv1', ':tlsv1.1']]
}]
}]
}]
}]
}}
const state2 = { ':pleroma': { ':media_proxy': {}}}
const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
const expectedResult2 = [{
group: ':pleroma',
key: ':media_proxy',
value: [{ tuple: [':proxy_opts', [
{ tuple: [':max_body_length', 26210000] },
{ tuple: [':http',
[{ tuple: [ ':proxy_url', { tuple: [ ':socks5', '127.0.0.1', '9020' ] }]},
{ tuple: [':adapter',
[{ tuple: [':ssl_options', [{ tuple: [':versions', [':tlsv1', ':tlsv1.1']]}]]}]
]}]
]}
]]}]
}]
expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
})
it('wraps settings that includes keyword in type', () => {
const settings1 = { 'Oban': { ':queues': [
['keyword', 'integer'],
{ ':activity_expiration': ['integer', 15],
':background': ['integer', 10],
':federator_incoming': ['integer', 30]}
]}}
const state1 = { ':pleroma': { 'Oban': {}}}
const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
const expectedResult1 = [{
group: ':pleroma',
key: 'Oban',
value: [{ tuple: [':queues', [
{ tuple: [':activity_expiration', 15] },
{ tuple: [':background', 10] },
{ tuple: [':federator_incoming', 30]}
]]}]
}]
const settings2 = { ':emoji': { ':groups': [
['keyword', 'string', ['list', 'string']],
{ ':custom': [['list'], ['/emoji/*.png', '/emoji/**/*.png']],
':another_group': ['list', ['/custom_emoji/*.png']]}
]}}
const state2 = { ':pleroma': { ':emoji': {}}}
const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
const expectedResult2 = [{
group: ':pleroma',
key: ':emoji',
value: [{ tuple: [':groups', [
{ tuple: [':custom', ['/emoji/*.png', '/emoji/**/*.png']]},
{ tuple: [':another_group', ['/custom_emoji/*.png']]}
]]}]
}]
expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
})
it('wraps :replace setting', () => {
const settings = { ':mrf_keyword': { ':replace': [
[['tuple', 'string', 'string'], ['tuple', 'regex', 'string']],
{ 'pattern': ['list', 'replacement'],
'/\w+/': ['list', 'test_replacement']}
]}}
const state = { ':pleroma': { ':mrf_keyword': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: ':mrf_keyword',
value: [{ tuple: [':replace', [
{ tuple: ['pattern', 'replacement'] },
{ tuple: ['/\w+/', 'test_replacement'] }
]]}]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps settings with type atom', () => {
const settings = {
':ldap': { ':sslopts': [ 'keyword', { ':verify': ['atom', 'verify_peer']}]},
':assets': { ':default_mascot': ['atom', 'pleroma_fox_tan_test']}
}
const state = { ':pleroma': { ':sslopts': {}, ':assets': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: ':ldap',
value: [{ tuple: [':sslopts', [{tuple: [':verify', ':verify_peer']}]]}]
}, {
group: ':pleroma',
key: ':assets',
value: [{ tuple: [':default_mascot', ':pleroma_fox_tan_test']}]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps settings with type string and tuple', () => {
const settings1 = { ':media_proxy': { ':proxy_opts':
['keyword', {
':http': ['keyword', { ':proxy_url': [['string', 'tuple'], 'localhost:9020']}]
}]
}}
const state1 = { ':pleroma': { ':media_proxy': {}}}
const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
const expectedResult1 = [{
group: ':pleroma',
key: ':media_proxy',
value: [{ tuple: [':proxy_opts', [
{ tuple: [':http',
[{ tuple: [':proxy_url', 'localhost:9020'] }]
]}
]]}]
}]
const settings2 = { ':media_proxy': { ':proxy_opts':
['keyword', {
':http': ['keyword', { ':proxy_url': [['string', 'tuple'], [':socks5', '127.0.0.1', '9020']]}]
}]
}}
const state2 = { ':pleroma': { ':media_proxy': {}}}
const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
const expectedResult2 = [{
group: ':pleroma',
key: ':media_proxy',
value: [{ tuple: [':proxy_opts', [
{ tuple: [':http',
[{ tuple: [ ':proxy_url', { tuple: [':socks5', '127.0.0.1', '9020'] }]}]
]}
]]}]
}]
expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
})
it('wraps settings with type atom and tuple', () => {
const settings1 = { 'Oban': { ':prune': [['atom', 'tuple'], ':disabled']}}
const state1 = { ':pleroma': { 'Oban': {}}}
const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
const expectedResult1 = [{
group: ':pleroma',
key: 'Oban',
value: [{ tuple: [':prune', ':disabled']}]
}]
const settings2 = { 'Oban': { ':prune':
[['atom', 'tuple'], [':maxlen', 1500]]
}}
const state2 = { ':pleroma': { 'Oban': {}}}
const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
const expectedResult2 = [{
group: ':pleroma',
key: 'Oban',
value: [{ tuple: [':prune', {tuple: [':maxlen', 1500]}]}]
}]
expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
})
it('wraps settings with type map', () => {
const settings1 = { ':instance': { ':poll_limits': ['map', { ':min_expiration': ['integer', 100] }]}}
const state1 = { ':pleroma': { ':instance': { ':poll_limits': {
':max_expiration': 31536000,
':max_option_chars': 200,
':max_options': 20,
':min_expiration': 100
}}}}
const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
const expectedResult1 = [{
group: ':pleroma',
key: ':instance',
value: [{ tuple: [':poll_limits', {
':max_expiration': 31536000,
':max_option_chars': 200,
':max_options': 20,
':min_expiration': 100
}]}]
}]
const settings2 = { ':email_notifications': { ':digest': ['map', {
':active': ['boolean', true],
':schedule': ['string', '0 0 0'],
':inactivity_threshold': ['integer', 10]
}]}}
const state2 = { ':pleroma': { ':email_notifications': { ':digest': {
':active': true,
':inactivity_threshold': 10,
':interval': 7,
':schedule': '0 0 0'
}}}}
const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
const expectedResult2 = [{
group: ':pleroma',
key: ':email_notifications',
value: [{ tuple: [':digest', {
':active': true,
':inactivity_threshold': 10,
':interval': 7,
':schedule': '0 0 0'
}]}]
}]
const settings3 = { ':mrf_subchain': { ':match_actor': ['map', {
'~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'],
'~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy']
}]}}
const state3 = { ':pleroma': { ':mrf_subchain': { ':match_actor': [
{ '~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'] },
{ '~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy'] }
]
}}}
const result3 = wrapUpdatedSettings(':pleroma', settings3, state3)
const expectedResult3 = [{
group: ':pleroma',
key: ':mrf_subchain',
value: [{ tuple: [':match_actor', {
'~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'],
'~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy']
}]}]
}]
expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
expect(_.isEqual(result3, expectedResult3)).toBeTruthy()
})
it('wraps IP setting', () => {
const settings = { ':gopher': { ':ip': ['tuple', '127.0.0.1']}}
const state = { ':pleroma': { ':gopher': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: ':gopher',
value: [{ tuple: [':ip', { tuple: [127, 0, 0, 1] }]}]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps args setting in Pleroma.Upload.Filter.Mogrify group', () => {
const settings = { 'Pleroma.Upload.Filter.Mogrify': { ':args': [
['string', ['list', 'string'], ['list', 'tuple']],
['strip', 'implode']
]}}
const state = { ':pleroma': { 'Pleroma.Upload.Filter.Mogrify': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: 'Pleroma.Upload.Filter.Mogrify',
value: [{tuple: [':args', ['strip', {tuple: ['implode', '1']}]]}]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
it('wraps regular settings', () => {
const settings = { ':http_security': {
':report_uri': ["string", "https://test.com"],
':ct_max_age': ["integer", 150000],
':sts': ["boolean", true],
':methods': [["list", "string"], ["POST", "PUT", "PATCH"]]
}}
const state = { ':pleroma': { ':http_security': {}}}
const result = wrapUpdatedSettings(':pleroma', settings, state)
const expectedResult = [{
group: ':pleroma',
key: ':http_security',
value: [
{ tuple: [":report_uri", "https://test.com"] },
{ tuple: [":ct_max_age", 150000] },
{ tuple: [":sts", true] },
{ tuple: [":methods", ["POST", "PUT", "PATCH"]] }
]
}]
expect(_.isEqual(result, expectedResult)).toBeTruthy()
})
})

View file

@ -1868,11 +1868,6 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58"
integrity sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=
braces@^2.2.2, braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@ -10207,13 +10202,6 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue2-ace-editor@^0.0.13:
version "0.0.13"
resolved "https://registry.yarnpkg.com/vue2-ace-editor/-/vue2-ace-editor-0.0.13.tgz#5528998ce2c13e8ed3a294f714298199fd107dc2"
integrity sha512-uQICyvJzYNix16xeYjNAINuNUQhPbqMR7UQsJeI+ycpEd2otsiNNU73jcZqHkpjuz0uaHDHnrpzQuI/RApsKXA==
dependencies:
brace "^0.11.0"
vue@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.8.tgz#f21cbc536bfc14f7d1d792a137bb12f69e60ea91"