admin-fe/src/store/modules/normalizers.js

325 lines
13 KiB
JavaScript

import _ from 'lodash'
export const getBooleanValue = value => {
if (value === 'true') {
return true
} else if (value === 'false') {
return false
}
return value
}
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 getValueWithoutKey = (key, [type, value]) => {
if (prependWithColon(type, value)) {
return `:${value}`
} else if (key === ':backends') {
const index = value.findIndex(el => el === ':ex_syslogger')
const updatedArray = value.slice()
if (index !== -1) {
updatedArray[index] = { 'tuple': ['ExSyslogger', ':ex_syslogger'] }
}
return updatedArray
} else if (key === ':types') {
return Object.keys(value).reduce((acc, key) => { return { ...acc, [key]: value[key][1] } }, {})
}
return value
}
export const parseNonTuples = (key, value) => {
if (key === ':backends') {
const index = value.findIndex(el => typeof el === 'object' && el.tuple.includes(':ex_syslogger'))
const updated = value.map((el, i) => i === index ? ':ex_syslogger' : el)
return updated
}
if (key === ':args') {
return typeof value === 'string' ? [value] : value
}
return value
}
// REFACTOR
export const parseTuples = (tuples, key) => {
return tuples.reduce((accum, item) => {
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)}` }}]
}, [])
} else if (Array.isArray(item.tuple[1]) &&
(item.tuple[0] === ':groups' ||
item.tuple[0] === ':replace' ||
item.tuple[0] === ':retries' ||
(item.tuple[0] === ':headers' && key === 'Pleroma.Web.MediaProxy.Invalidation.Http') ||
item.tuple[0] === ':crontab' ||
item.tuple[0] === ':transparency_exclusions' ||
item.tuple[0] === ':quarantined_instances' ||
key === ':mrf_simple')) {
if (item.tuple[0] === ':crontab') {
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
return [...acc, { [group.tuple[1]]: { value: group.tuple[0], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
}, [])
} else {
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
/**
* The ':quarantined_instances' and ':mrf_simple' settings have changed to a list of tuples instead of a list of strings.
* This is to have backwards compatibility for instances that still use strings.
*/
if (typeof group === 'string') {
return [...acc, group]
} else {
return [...acc, { [group.tuple[0]]: { value: group.tuple[1], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
}
}, [])
}
} else if (item.tuple[0] === ':icons') {
accum[item.tuple[0]] = item.tuple[1].map(icon => {
return Object.keys(icon).map(name => {
return { key: name, value: icon[name], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }
})
}, [])
} else if (item.tuple[0] === ':prune') {
accum[item.tuple[0]] = item.tuple[1] === ':disabled' ? [item.tuple[1]] : item.tuple[1].tuple
} else if (item.tuple[0] === ':sender') {
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]) && (item.tuple[1][0] !== null &&
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])
} else if (Array.isArray(item.tuple[1])) {
accum[item.tuple[0]] = item.tuple[1]
} else if (item.tuple[0] === ':ip') {
accum[item.tuple[0]] = item.tuple[1].tuple.join('.')
} else if (item.tuple[1] && typeof item.tuple[1] === 'object') {
if (item.tuple[0] === ':params' || item.tuple[0] === ':match_actor') {
accum[item.tuple[0]] = Object.keys(item.tuple[1]).reduce((acc, key) => {
return [...acc, { [key]: { value: item.tuple[1][key], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
}, [])
} else {
accum[item.tuple[0]] = parseObject(item.tuple[1])
}
} else {
accum[item.tuple[0]] = item.tuple[1]
}
return accum
}, {})
}
const parseObject = object => {
return Object.keys(object).reduce((acc, item) => {
acc[item] = object[item]
return acc
}, {})
}
const parseStringOrTupleValue = (key, value) => {
if (key === ':proxy_url') {
if (value && !Array.isArray(value) &&
typeof value === 'object' &&
value.tuple.length === 3 &&
value.tuple[0] === ':socks5') {
const [, host, port] = value.tuple
return { socks5: true, host, port }
} else if (typeof value === 'string') {
const [host, port] = value.split(':')
return { socks5: false, host, port }
}
return { socks5: false, host: null, port: null }
} else if (key === ':sender') {
if (typeof value === 'string') {
return { email: value }
} else if (value &&
typeof value === 'object' &&
value.tuple.length === 2) {
const [nickname, email] = value.tuple
return { nickname, email }
}
}
}
const prependWithColon = (type, value) => {
return (type === 'atom' && value.length > 0) ||
(Array.isArray(type) && type.includes('boolean') && type.includes('atom') && typeof value === 'string')
}
export const processNested = (valueForState, valueForUpdatedSettings, group, parentKey, parents, settings, updatedSettings) => {
const [{ key, type }, ...otherParents] = parents
const path = [group, parentKey, ...parents.reverse().map(parent => parent.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', 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]
? { ...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
}
return otherParents.length === 1
? { valueForState: updatedValueForState, valueForUpdatedSettings: updatedValueForUpdatedSettings, setting: otherParents[0] }
: processNested(updatedValueForState, updatedValueForUpdatedSettings, group, parentKey, otherParents, settings, updatedSettings)
}
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] = keys
return value[1][element] ? secondSegment(value[1][element], rest) : false
}
return secondSegment(firstSegment, restKeys)
}
}
}
export const valueHasTuples = (key, value) => {
const valueIsArrayOfNonObjects = Array.isArray(value) && value.length > 0 && value.every(el => typeof el !== 'object')
return key === ':meta' ||
key === ':types' ||
key === ':backends' ||
key === ':compiled_template_engines' ||
key === ':compiled_format_encoders' ||
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean' ||
value === null ||
valueIsArrayOfNonObjects
}
export const wrapUpdatedSettings = (group, settings, currentState) => {
return Object.keys(settings).map((key) => {
return settings[key]._value
? { group, key, value: getValueWithoutKey(key, settings[key]._value) }
: { group, key, value: wrapValues(settings[key], currentState[group][key]) }
})
}
const wrapValues = (settings, currentState) => {
return Object.keys(settings).map(setting => {
const [type, value] = settings[setting]
if (type === 'keyword' ||
(Array.isArray(type) && (
type.includes('keyword') ||
(type.includes('tuple') && type.includes('list'))
))
) {
return { 'tuple': [setting, wrapValues(value, currentState)] }
} else if (prependWithColon(type, value)) {
return { 'tuple': [setting, `:${value}`] }
} 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') {
return { 'tuple': [value, setting] }
} else if (type === 'map') {
const mapValue = Object.keys(value).reduce((acc, key) => {
acc[key] = value[key][1]
return acc
}, {})
return { 'tuple': [setting, { ...currentState[setting], ...mapValue }] }
} else if (type.includes('map') && !type.includes('list')) {
const mapValue = Object.keys(value).reduce((acc, key) => {
acc[key] = value[key][1]
return acc
}, {})
return { 'tuple': [setting, mapValue] }
} else if (setting === ':ip') {
const ip = value.split('.').map(s => parseInt(s, 10))
return { 'tuple': [setting, { 'tuple': ip }] }
} else {
return { 'tuple': [setting, value] }
}
})
}
export const formSearchObject = description => {
const parseNestedSettings = (description, label, key) => description.reduce((acc, setting) => {
const searchArray = _.compact([setting.key, setting.label, setting.description]).map(el => el.toLowerCase())
const resultObject = { label: setting.label, key: setting.key || setting.group, groupKey: key, groupLabel: label, search: searchArray }
if (setting.children) {
const updatedAcc = [...acc, resultObject]
return [...updatedAcc, ...parseNestedSettings(setting.children, label, key)]
}
return [...acc, resultObject]
}, [])
const processedDescription = description.reduce((acc, setting) => {
const searchArray = _.compact([setting.key, setting.label, setting.description]).map(el => el.toLowerCase())
const resultObject = { label: setting.label, key: setting.key || setting.group, groupKey: setting.key || setting.group, groupLabel: setting.label, search: searchArray }
if (setting.children) {
const updatedAcc = !setting.key && setting.group === ':pleroma' ? acc : [...acc, resultObject]
return [...updatedAcc, ...parseNestedSettings(setting.children, setting.label, setting.key || setting.group)]
}
return !setting.key && setting.group === ':pleroma' ? acc : [...acc, resultObject]
}, [])
const searchDataForEditableDocs = [{
groupKey: ':instance_panel',
groupLabel: 'Instance Panel',
key: ':instance_panel',
label: 'Instance Panel',
search: ['Instance Panel', ':instance_panel']
}, {
groupKey: ':terms_of_services',
groupLabel: 'Terms of Services',
key: ':terms_of_services',
label: 'Terms of Services',
search: ['Terms of Services', ':terms_of_services']
}]
return processedDescription.concat(searchDataForEditableDocs)
}