Merge branch 'mrf-config-updates' into 'develop'

EditFederationModal: use ConfigDB directly instead of /api/v1/instance

See merge request soapbox-pub/soapbox-fe!649
This commit is contained in:
Alex Gleason 2021-07-28 22:37:09 +00:00
commit 67d68b7efb
9 changed files with 118 additions and 55 deletions

View File

@ -82,10 +82,10 @@ export function updateConfig(configs) {
dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs }); dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs });
return api(getState) return api(getState)
.post('/api/pleroma/admin/config', { configs }) .post('/api/pleroma/admin/config', { configs })
.then(({ data: { configs } }) => { .then(({ data }) => {
dispatch({ type: ADMIN_CONFIG_UPDATE_SUCCESS, configs }); dispatch({ type: ADMIN_CONFIG_UPDATE_SUCCESS, configs: data.configs, needsReboot: data.need_reboot });
}).catch(error => { }).catch(error => {
dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error }); dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error, configs });
}); });
}; };
} }

View File

@ -1,6 +1,6 @@
import { fetchInstance } from './instance'; import { fetchConfig, updateConfig } from './admin';
import { updateConfig } from './admin';
import { Set as ImmutableSet } from 'immutable'; import { Set as ImmutableSet } from 'immutable';
import ConfigDB from 'soapbox/utils/config_db';
const simplePolicyMerge = (simplePolicy, host, restrictions) => { const simplePolicyMerge = (simplePolicy, host, restrictions) => {
return simplePolicy.map((hosts, key) => { return simplePolicy.map((hosts, key) => {
@ -14,31 +14,15 @@ const simplePolicyMerge = (simplePolicy, host, restrictions) => {
}); });
}; };
const simplePolicyToConfig = simplePolicy => {
const value = simplePolicy.map((hosts, key) => (
{ tuple: [`:${key}`, hosts.toJS()] }
)).toList();
return [{
group: ':pleroma',
key: ':mrf_simple',
value,
}];
};
export function updateMrf(host, restrictions) { export function updateMrf(host, restrictions) {
return (dispatch, getState) => { return (dispatch, getState) => {
return dispatch(fetchInstance()) return dispatch(fetchConfig())
.then(() => { .then(() => {
const simplePolicy = getState().getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple']); const configs = getState().getIn(['admin', 'configs']);
const simplePolicy = ConfigDB.toSimplePolicy(configs);
const merged = simplePolicyMerge(simplePolicy, host, restrictions); const merged = simplePolicyMerge(simplePolicy, host, restrictions);
const config = simplePolicyToConfig(merged); const config = ConfigDB.fromSimplePolicy(merged);
dispatch(updateConfig(config)); dispatch(updateConfig(config));
// TODO: Make this less insane
setTimeout(() => {
dispatch(fetchInstance());
}, 1000);
}); });
}; };
} }

View File

@ -4,21 +4,11 @@ import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import { createSelector } from 'reselect';
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import RestrictedInstance from './components/restricted_instance'; import RestrictedInstance from './components/restricted_instance';
import Accordion from 'soapbox/features/ui/components/accordion'; import Accordion from 'soapbox/features/ui/components/accordion';
import ScrollableList from 'soapbox/components/scrollable_list'; import ScrollableList from 'soapbox/components/scrollable_list';
import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; import { federationRestrictionsDisclosed } from 'soapbox/utils/state';
import { makeGetHosts } from 'soapbox/selectors';
const getHosts = createSelector([
state => state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()),
], (simplePolicy) => {
return simplePolicy
.deleteAll(['accept', 'reject_deletes', 'report_removal'])
.reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet())
.sort();
});
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation Restrictions' }, heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation Restrictions' },
@ -28,6 +18,8 @@ const messages = defineMessages({
notDisclosed: { id: 'federation_restrictions.not_disclosed_message', defaultMessage: '{siteTitle} does not disclose federation restrictions through the API.' }, notDisclosed: { id: 'federation_restrictions.not_disclosed_message', defaultMessage: '{siteTitle} does not disclose federation restrictions through the API.' },
}); });
const getHosts = makeGetHosts();
const mapStateToProps = state => ({ const mapStateToProps = state => ({
siteTitle: state.getIn(['instance', 'title']), siteTitle: state.getIn(['instance', 'title']),
hosts: getHosts(state), hosts: getHosts(state),

View File

@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import { SimpleForm, Checkbox } from 'soapbox/features/forms'; import { SimpleForm, Checkbox } from 'soapbox/features/forms';
import { makeGetRemoteInstance } from 'soapbox/selectors'; import { makeGetRemoteInstance } from 'soapbox/selectors';
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap, is } from 'immutable';
import { updateMrf } from 'soapbox/actions/mrf'; import { updateMrf } from 'soapbox/actions/mrf';
import snackbar from 'soapbox/actions/snackbar'; import snackbar from 'soapbox/actions/snackbar';
@ -41,11 +41,23 @@ class EditFederationModal extends ImmutablePureComponent {
data: ImmutableMap(), data: ImmutableMap(),
} }
componentDidMount() { hydrateState = () => {
const { remoteInstance } = this.props; const { remoteInstance } = this.props;
this.setState({ data: remoteInstance.get('federation') }); this.setState({ data: remoteInstance.get('federation') });
} }
componentDidMount() {
this.hydrateState();
}
componentDidUpdate(prevProps) {
const { remoteInstance } = this.props;
if (!is(prevProps.remoteInstance, remoteInstance)) {
this.hydrateState();
}
}
handleDataChange = key => { handleDataChange = key => {
return ({ target }) => { return ({ target }) => {
const { data } = this.state; const { data } = this.state;

View File

@ -9,9 +9,11 @@ import LinkFooter from 'soapbox/features/ui/components/link_footer';
import { getFeatures } from 'soapbox/utils/features'; import { getFeatures } from 'soapbox/utils/features';
import InstanceInfoPanel from 'soapbox/features/ui/components/instance_info_panel'; import InstanceInfoPanel from 'soapbox/features/ui/components/instance_info_panel';
import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; import { federationRestrictionsDisclosed } from 'soapbox/utils/state';
import { isAdmin } from 'soapbox/utils/accounts';
const mapStateToProps = state => { const mapStateToProps = state => {
const me = state.get('me'); const me = state.get('me');
const account = state.getIn(['accounts', me]);
const features = getFeatures(state.get('instance')); const features = getFeatures(state.get('instance'));
return { return {
@ -19,6 +21,7 @@ const mapStateToProps = state => {
showTrendsPanel: features.trends, showTrendsPanel: features.trends,
showWhoToFollowPanel: features.suggestions, showWhoToFollowPanel: features.suggestions,
disclosed: federationRestrictionsDisclosed(state), disclosed: federationRestrictionsDisclosed(state),
isAdmin: isAdmin(account),
}; };
}; };
@ -26,7 +29,7 @@ export default @connect(mapStateToProps)
class RemoteInstancePage extends ImmutablePureComponent { class RemoteInstancePage extends ImmutablePureComponent {
render() { render() {
const { me, children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host }, disclosed } = this.props; const { me, children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host }, disclosed, isAdmin } = this.props;
return ( return (
<div className='page'> <div className='page'>
@ -35,7 +38,7 @@ class RemoteInstancePage extends ImmutablePureComponent {
<div className='columns-area__panels__pane columns-area__panels__pane--left'> <div className='columns-area__panels__pane columns-area__panels__pane--left'>
<div className='columns-area__panels__pane__inner'> <div className='columns-area__panels__pane__inner'>
{disclosed && <InstanceInfoPanel host={host} />} {(disclosed || isAdmin) && <InstanceInfoPanel host={host} />}
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
import { import {
ADMIN_CONFIG_FETCH_SUCCESS, ADMIN_CONFIG_FETCH_SUCCESS,
ADMIN_CONFIG_UPDATE_SUCCESS,
ADMIN_REPORTS_FETCH_SUCCESS, ADMIN_REPORTS_FETCH_SUCCESS,
ADMIN_REPORTS_PATCH_REQUEST, ADMIN_REPORTS_PATCH_REQUEST,
ADMIN_REPORTS_PATCH_SUCCESS, ADMIN_REPORTS_PATCH_SUCCESS,
@ -127,6 +128,7 @@ function handleReportDiffs(state, reports) {
export default function admin(state = initialState, action) { export default function admin(state = initialState, action) {
switch(action.type) { switch(action.type) {
case ADMIN_CONFIG_FETCH_SUCCESS: case ADMIN_CONFIG_FETCH_SUCCESS:
case ADMIN_CONFIG_UPDATE_SUCCESS:
return state.set('configs', fromJS(action.configs)); return state.set('configs', fromJS(action.configs));
case ADMIN_REPORTS_FETCH_SUCCESS: case ADMIN_REPORTS_FETCH_SUCCESS:
return importReports(state, action.reports); return importReports(state, action.reports);

View File

@ -51,15 +51,23 @@ const getConfigValue = (instanceConfig, key) => {
const importConfigs = (state, configs) => { const importConfigs = (state, configs) => {
// FIXME: This is pretty hacked together. Need to make a cleaner map. // FIXME: This is pretty hacked together. Need to make a cleaner map.
const config = ConfigDB.find(configs, ':pleroma', ':instance'); const config = ConfigDB.find(configs, ':pleroma', ':instance');
if (!config) return state; const simplePolicy = ConfigDB.toSimplePolicy(configs);
const value = config.get('value', ImmutableList());
if (!config && !simplePolicy) return state;
return state.withMutations(state => { return state.withMutations(state => {
const registrationsOpen = getConfigValue(value, ':registrations_open'); if (config) {
const approvalRequired = getConfigValue(value, ':account_approval_required'); const value = config.get('value', ImmutableList());
const registrationsOpen = getConfigValue(value, ':registrations_open');
const approvalRequired = getConfigValue(value, ':account_approval_required');
state.update('registrations', c => typeof registrationsOpen === 'boolean' ? registrationsOpen : c); state.update('registrations', c => typeof registrationsOpen === 'boolean' ? registrationsOpen : c);
state.update('approval_required', c => typeof approvalRequired === 'boolean' ? approvalRequired : c); state.update('approval_required', c => typeof approvalRequired === 'boolean' ? approvalRequired : c);
}
if (simplePolicy) {
state.setIn(['pleroma', 'metadata', 'federation', 'mrf_simple'], simplePolicy);
}
}); });
}; };

View File

@ -1,6 +1,11 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import {
Map as ImmutableMap,
List as ImmutableList,
OrderedSet as ImmutableOrderedSet,
} from 'immutable';
import { getDomain } from 'soapbox/utils/accounts'; import { getDomain } from 'soapbox/utils/accounts';
import ConfigDB from 'soapbox/utils/config_db';
const getAccountBase = (state, id) => state.getIn(['accounts', id], null); const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null); const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
@ -217,22 +222,38 @@ export const makeGetOtherAccounts = () => {
}); });
}; };
const getSimplePolicy = createSelector([
state => state.getIn(['admin', 'configs'], ImmutableMap()),
state => state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()),
], (configs, instancePolicy) => {
return instancePolicy.merge(ConfigDB.toSimplePolicy(configs));
});
const getRemoteInstanceFavicon = (state, host) => ( const getRemoteInstanceFavicon = (state, host) => (
state.get('accounts') state.get('accounts')
.find(account => getDomain(account) === host, null, ImmutableMap()) .find(account => getDomain(account) === host, null, ImmutableMap())
.getIn(['pleroma', 'favicon']) .getIn(['pleroma', 'favicon'])
); );
const getSimplePolicy = (state, host) => ( const getRemoteInstanceFederation = (state, host) => (
state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()) getSimplePolicy(state)
.map(hosts => hosts.includes(host)) .map(hosts => hosts.includes(host))
); );
export const makeGetHosts = () => {
return createSelector([getSimplePolicy], (simplePolicy) => {
return simplePolicy
.deleteAll(['accept', 'reject_deletes', 'report_removal'])
.reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet())
.sort();
});
};
export const makeGetRemoteInstance = () => { export const makeGetRemoteInstance = () => {
return createSelector([ return createSelector([
(state, host) => host, (state, host) => host,
getRemoteInstanceFavicon, getRemoteInstanceFavicon,
getSimplePolicy, getRemoteInstanceFederation,
], (host, favicon, federation) => { ], (host, favicon, federation) => {
return ImmutableMap({ return ImmutableMap({
host, host,

View File

@ -1,9 +1,50 @@
import {
Map as ImmutableMap,
List as ImmutableList,
Set as ImmutableSet,
fromJS,
} from 'immutable';
import { trimStart } from 'lodash';
const find = (configs, group, key) => {
return configs.find(config =>
config.isSuperset({ group, key }),
);
};
const toSimplePolicy = configs => {
const config = find(configs, ':pleroma', ':mrf_simple');
const reducer = (acc, curr) => {
const { tuple: [key, hosts] } = curr.toJS();
return acc.set(trimStart(key, ':'), ImmutableSet(hosts));
};
if (config && config.get) {
const value = config.get('value', ImmutableList());
return value.reduce(reducer, ImmutableMap());
} else {
return ImmutableMap();
}
};
const fromSimplePolicy = simplePolicy => {
const mapper = (hosts, key) => fromJS({ tuple: [`:${key}`, hosts.toJS()] });
const value = simplePolicy.map(mapper).toList();
return ImmutableList([
ImmutableMap({
group: ':pleroma',
key: ':mrf_simple',
value,
}),
]);
};
export const ConfigDB = { export const ConfigDB = {
find: (configs, group, key) => { find,
return configs.find(config => toSimplePolicy,
config.isSuperset({ group, key }), fromSimplePolicy,
);
},
}; };
export default ConfigDB; export default ConfigDB;