diff options
| author | Henry Jameson <me@hjkos.com> | 2023-06-05 21:53:14 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2023-06-05 21:53:14 +0300 |
| commit | 5e656cc0b40a26435556fff79636c0b2e9c8af4f (patch) | |
| tree | 8efc5369ac64d471b4d61f731bd99653f7e1e19d /src/modules/adminSettings.js | |
| parent | 00b47e16736f8b472f20dab8def30fb22d54c8be (diff) | |
| parent | ae5181d21eefecc0167e2a076e6c8ad44f3ca859 (diff) | |
Merge remote-tracking branch 'origin/develop' into harden-parser
Diffstat (limited to 'src/modules/adminSettings.js')
| -rw-r--r-- | src/modules/adminSettings.js | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/modules/adminSettings.js b/src/modules/adminSettings.js new file mode 100644 index 00000000..cad9c0ca --- /dev/null +++ b/src/modules/adminSettings.js @@ -0,0 +1,230 @@ +import { set, get, cloneDeep, differenceWith, isEqual, flatten } from 'lodash' + +export const defaultState = { + frontends: [], + loaded: false, + needsReboot: null, + config: null, + modifiedPaths: null, + descriptions: null, + draft: null, + dbConfigEnabled: null +} + +export const newUserFlags = { + ...defaultState.flagStorage +} + +const adminSettingsStorage = { + state: { + ...cloneDeep(defaultState) + }, + mutations: { + setInstanceAdminNoDbConfig (state) { + state.loaded = false + state.dbConfigEnabled = false + }, + setAvailableFrontends (state, { frontends }) { + state.frontends = frontends.map(f => { + if (f.name === 'pleroma-fe') { + f.refs = ['master', 'develop'] + } else { + f.refs = [f.ref] + } + return f + }) + }, + updateAdminSettings (state, { config, modifiedPaths }) { + state.loaded = true + state.dbConfigEnabled = true + state.config = config + state.modifiedPaths = modifiedPaths + }, + updateAdminDescriptions (state, { descriptions }) { + state.descriptions = descriptions + }, + updateAdminDraft (state, { path, value }) { + const [group, key, subkey] = path + const parent = [group, key, subkey] + + set(state.draft, path, value) + + // force-updating grouped draft to trigger refresh of group settings + if (path.length > parent.length) { + set(state.draft, parent, cloneDeep(get(state.draft, parent))) + } + }, + resetAdminDraft (state) { + state.draft = cloneDeep(state.config) + } + }, + actions: { + loadFrontendsStuff ({ state, rootState, dispatch, commit }) { + rootState.api.backendInteractor.fetchAvailableFrontends() + .then(frontends => commit('setAvailableFrontends', { frontends })) + }, + loadAdminStuff ({ state, rootState, dispatch, commit }) { + rootState.api.backendInteractor.fetchInstanceDBConfig() + .then(backendDbConfig => { + if (backendDbConfig.error) { + if (backendDbConfig.error.status === 400) { + backendDbConfig.error.json().then(errorJson => { + if (/configurable_from_database/.test(errorJson.error)) { + commit('setInstanceAdminNoDbConfig') + } + }) + } + } else { + dispatch('setInstanceAdminSettings', { backendDbConfig }) + } + }) + if (state.descriptions === null) { + rootState.api.backendInteractor.fetchInstanceConfigDescriptions() + .then(backendDescriptions => dispatch('setInstanceAdminDescriptions', { backendDescriptions })) + } + }, + setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) { + const config = state.config || {} + const modifiedPaths = new Set() + backendDbConfig.configs.forEach(c => { + const path = [c.group, c.key] + if (c.db) { + // Path elements can contain dot, therefore we use ' -> ' as a separator instead + // Using strings for modified paths for easier searching + c.db.forEach(x => modifiedPaths.add([...path, x].join(' -> '))) + } + const convert = (value) => { + if (Array.isArray(value) && value.length > 0 && value[0].tuple) { + return value.reduce((acc, c) => { + return { ...acc, [c.tuple[0]]: convert(c.tuple[1]) } + }, {}) + } else { + return value + } + } + set(config, path, convert(c.value)) + }) + console.log(config[':pleroma']) + commit('updateAdminSettings', { config, modifiedPaths }) + commit('resetAdminDraft') + }, + setInstanceAdminDescriptions ({ state, commit, dispatch }, { backendDescriptions }) { + const convert = ({ children, description, label, key = '<ROOT>', group, suggestions }, path, acc) => { + const newPath = group ? [group, key] : [key] + const obj = { description, label, suggestions } + if (Array.isArray(children)) { + children.forEach(c => { + convert(c, newPath, obj) + }) + } + set(acc, newPath, obj) + } + + const descriptions = {} + backendDescriptions.forEach(d => convert(d, '', descriptions)) + console.log(descriptions[':pleroma']['Pleroma.Captcha']) + commit('updateAdminDescriptions', { descriptions }) + }, + + // This action takes draft state, diffs it with live config state and then pushes + // only differences between the two. Difference detection only work up to subkey (third) level. + pushAdminDraft ({ rootState, state, commit, dispatch }) { + // TODO cleanup paths in modifiedPaths + const convert = (value) => { + if (typeof value !== 'object') { + return value + } else if (Array.isArray(value)) { + return value.map(convert) + } else { + return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] })) + } + } + + // Getting all group-keys used in config + const allGroupKeys = flatten( + Object + .entries(state.config) + .map( + ([group, lv1data]) => Object + .keys(lv1data) + .map((key) => ({ group, key })) + ) + ) + + // Only using group-keys where there are changes detected + const changedGroupKeys = allGroupKeys.filter(({ group, key }) => { + return !isEqual(state.config[group][key], state.draft[group][key]) + }) + + // Here we take all changed group-keys and get all changed subkeys + const changed = changedGroupKeys.map(({ group, key }) => { + const config = state.config[group][key] + const draft = state.draft[group][key] + + // We convert group-key value into entries arrays + const eConfig = Object.entries(config) + const eDraft = Object.entries(draft) + + // Then those entries array we diff so only changed subkey entries remain + // We use the diffed array to reconstruct the object and then shove it into convert() + return ({ group, key, value: convert(Object.fromEntries(differenceWith(eDraft, eConfig, isEqual))) }) + }) + + rootState.api.backendInteractor.pushInstanceDBConfig({ + payload: { + configs: changed + } + }) + .then(() => rootState.api.backendInteractor.fetchInstanceDBConfig()) + .then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig })) + }, + pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) { + const [group, key, ...rest] = Array.isArray(path) ? path : path.split(/\./g) + const clone = {} // not actually cloning the entire thing to avoid excessive writes + set(clone, rest, value) + + // TODO cleanup paths in modifiedPaths + const convert = (value) => { + if (typeof value !== 'object') { + return value + } else if (Array.isArray(value)) { + return value.map(convert) + } else { + return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] })) + } + } + + rootState.api.backendInteractor.pushInstanceDBConfig({ + payload: { + configs: [{ + group, + key, + value: convert(clone) + }] + } + }) + .then(() => rootState.api.backendInteractor.fetchInstanceDBConfig()) + .then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig })) + }, + resetAdminSetting ({ rootState, state, commit, dispatch }, { path }) { + const [group, key, subkey] = path.split(/\./g) + + state.modifiedPaths.delete(path) + + return rootState.api.backendInteractor.pushInstanceDBConfig({ + payload: { + configs: [{ + group, + key, + delete: true, + subkeys: [subkey] + }] + } + }) + .then(() => rootState.api.backendInteractor.fetchInstanceDBConfig()) + .then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig })) + } + } +} + +export default adminSettingsStorage |
