pl-fe: fix simple policy mrf management

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-10-15 13:49:27 +02:00
parent 5316395ee9
commit 5bc3ef409e
7 changed files with 61 additions and 36 deletions

View File

@ -188,18 +188,36 @@ const pleromaSchema = coerceObject({
federation: coerceObject({ federation: coerceObject({
enabled: v.fallback(v.boolean(), true), // Assume true unless explicitly false enabled: v.fallback(v.boolean(), true), // Assume true unless explicitly false
mrf_policies: v.fallback(v.optional(v.array(v.string())), undefined), mrf_policies: v.fallback(v.optional(v.array(v.string())), undefined),
mrf_simple: coerceObject({ mrf_simple: coerceObject(v.entriesFromList(
accept: v.fallback(v.array(v.string()), []), [
avatar_removal: v.fallback(v.array(v.string()), []), 'accept',
banner_removal: v.fallback(v.array(v.string()), []), 'avatar_removal',
federated_timeline_removal: v.fallback(v.array(v.string()), []), 'banner_removal',
followers_only: v.fallback(v.array(v.string()), []), 'federated_timeline_removal',
media_nsfw: v.fallback(v.array(v.string()), []), 'followers_only',
media_removal: v.fallback(v.array(v.string()), []), 'media_nsfw',
reject: v.fallback(v.array(v.string()), []), 'media_removal',
reject_deletes: v.fallback(v.array(v.string()), []), 'reject',
report_removal: v.fallback(v.array(v.string()), []), 'reject_deletes',
}), 'report_removal',
],
v.fallback(v.array(v.string()), []),
)),
mrf_simple_info: coerceObject(v.entriesFromList(
[
'accept',
'avatar_removal',
'banner_removal',
'federated_timeline_removal',
'followers_only',
'media_nsfw',
'media_removal',
'reject',
'reject_deletes',
'report_removal',
],
v.fallback(v.array(v.tuple([v.string(), v.string()])), []),
)),
}), }),
fields_limits: coerceObject({ fields_limits: coerceObject({
max_fields: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 4), max_fields: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 4),

View File

@ -1,26 +1,33 @@
import { mrfSimpleSchema, type MRFSimple } from 'pl-fe/schemas/pleroma';
import ConfigDB from 'pl-fe/utils/config-db'; import ConfigDB from 'pl-fe/utils/config-db';
import { fetchConfig, updateConfig } from './admin'; import { fetchConfig, updateConfig } from './admin';
import type { MRFSimple } from 'pl-fe/schemas/pleroma';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
const simplePolicyMerge = (simplePolicy: MRFSimple, host: string, restrictions: Record<string, any>) => { const simplePolicyMerge = (simplePolicy: Partial<MRFSimple>, host: string, restrictions: Record<string, any>): MRFSimple => {
const entries = Object.entries(simplePolicy).map(([key, hosts]) => { const entries = Object.entries(simplePolicy).map(([key, hosts]) => {
const isRestricted = restrictions[key]; const isRestricted = restrictions[key];
const set = new Set(hosts); hosts = [...hosts];
if (isRestricted) { if (isRestricted) {
set.add(host); if (!hosts.some((tuple) => tuple[0] === host)) {
hosts.push([host, '']);
}
} else { } else {
set.delete(host); hosts = hosts.filter((tuple) => tuple[0] !== host);
} }
return [key, [...set]]; return [key, hosts];
}); });
return Object.fromEntries(entries); return Object.fromEntries([
...Object.keys(
(mrfSimpleSchema as any).wrapped.pipe[2].entries as typeof mrfSimpleSchema.entries,
).map((key) => [key, []]),
...entries,
]);
}; };
const updateMrf = (host: string, restrictions: Record<string, any>) => const updateMrf = (host: string, restrictions: Record<string, any>) =>

View File

@ -47,7 +47,7 @@ const FederationRestrictionsPage = () => {
<div className='pt-4'> <div className='pt-4'>
<ScrollableList emptyMessageText={emptyMessage}> <ScrollableList emptyMessageText={emptyMessage}>
{hosts.map((host) => <RestrictedInstance key={host} host={host} />)} {hosts.map(([host]) => <RestrictedInstance key={host} host={host} />)}
</ScrollableList> </ScrollableList>
</div> </div>
</Column> </Column>

View File

@ -32,9 +32,8 @@ const getConfigValue = (instanceConfig: Array<any>, key: string) => {
const importConfigs = (state: State, configs: PleromaConfig['configs']) => { const importConfigs = (state: State, configs: PleromaConfig['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');
const simplePolicy = ConfigDB.toSimplePolicy(configs);
if (!config && !simplePolicy) return state; if (!config) return state;
if (config) { if (config) {
const value = config.value || []; const value = config.value || [];
@ -47,10 +46,6 @@ const importConfigs = (state: State, configs: PleromaConfig['configs']) => {
approval_required: approvalRequired ?? state.registrations.approval_required, approval_required: approvalRequired ?? state.registrations.approval_required,
}; };
} }
if (simplePolicy) {
state.pleroma.metadata.federation.mrf_simple = simplePolicy;
}
}; };
const handleAuthFetch = (state: State) => { const handleAuthFetch = (state: State) => {

View File

@ -2,6 +2,11 @@ import * as v from 'valibot';
import { coerceObject } from './utils'; import { coerceObject } from './utils';
const mrfSimpleInstanceSchema = v.pipe(v.any(), v.transform((value) => {
if (typeof value === 'string') return [value, ''];
return value.tuple;
}), v.tuple([v.string(), v.string()]));
const mrfSimpleSchema = coerceObject(v.entriesFromList( const mrfSimpleSchema = coerceObject(v.entriesFromList(
[ [
'accept', 'accept',
@ -15,7 +20,7 @@ const mrfSimpleSchema = coerceObject(v.entriesFromList(
'reject_deletes', 'reject_deletes',
'report_removal', 'report_removal',
], ],
v.fallback(v.array(v.string()), []), v.fallback(v.array(mrfSimpleInstanceSchema), []),
)); ));
type MRFSimple = v.InferOutput<typeof mrfSimpleSchema>; type MRFSimple = v.InferOutput<typeof mrfSimpleSchema>;

View File

@ -1,6 +1,5 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
// import { getLocale } from 'pl-fe/actions/settings';
import { Entities } from 'pl-fe/entity-store/entities'; import { Entities } from 'pl-fe/entity-store/entities';
import { useSettingsStore } from 'pl-fe/stores/settings'; import { useSettingsStore } from 'pl-fe/stores/settings';
import { getDomain } from 'pl-fe/utils/accounts'; import { getDomain } from 'pl-fe/utils/accounts';
@ -245,8 +244,8 @@ const makeGetOtherAccounts = () => createSelector([
const getSimplePolicy = createSelector([ const getSimplePolicy = createSelector([
(state: RootState) => state.admin.configs, (state: RootState) => state.admin.configs,
(state: RootState) => state.instance.pleroma.metadata.federation.mrf_simple, (state: RootState) => state.instance.pleroma.metadata.federation.mrf_simple_info,
], (configs, instancePolicy) => ({ ], (configs, instancePolicy): MRFSimple => ({
...instancePolicy, ...instancePolicy,
...ConfigDB.toSimplePolicy(configs), ...ConfigDB.toSimplePolicy(configs),
})); }));
@ -265,7 +264,7 @@ const getRemoteInstanceFederation = (state: RootState, host: string): HostFedera
const simplePolicy = getSimplePolicy(state); const simplePolicy = getSimplePolicy(state);
return Object.fromEntries( return Object.fromEntries(
Object.entries(simplePolicy).map(([key, hosts]) => [key, hosts.includes(host)]), Object.entries(simplePolicy).map(([key, hosts]) => [key, hosts.some(entry => entry[0] === host)]),
) as HostFederation; ) as HostFederation;
}; };

View File

@ -1,7 +1,7 @@
import trimStart from 'lodash/trimStart'; import trimStart from 'lodash/trimStart';
import * as v from 'valibot'; import * as v from 'valibot';
import { mrfSimpleSchema } from 'pl-fe/schemas/pleroma'; import { mrfSimpleSchema, type MRFSimple } from 'pl-fe/schemas/pleroma';
import type { PleromaConfig } from 'pl-api'; import type { PleromaConfig } from 'pl-api';
@ -16,13 +16,14 @@ const find = (
config.group === group && config.key === key, config.group === group && config.key === key,
); );
const toSimplePolicy = (configs: PleromaConfig['configs']) => { const toSimplePolicy = (configs: PleromaConfig['configs']): Partial<MRFSimple> => {
const config = find(configs, ':pleroma', ':mrf_simple'); const config = find(configs, ':pleroma', ':mrf_simple');
const reducer = (acc: Record<string, any>, curr: Record<string, any>) => { const reducer = (acc: Record<string, any>, curr: Record<string, any>) => {
const key = curr.tuple?.[0] as string; const key = curr.tuple?.[0] as string;
const hosts = curr.tuple?.[1] as Array<string>; const hosts = curr.tuple?.[1] as Array<string>;
return acc[trimStart(key, ':')] = hosts; acc[trimStart(key, ':')] = hosts;
return acc;
}; };
if (config) { if (config) {
@ -30,12 +31,12 @@ const toSimplePolicy = (configs: PleromaConfig['configs']) => {
const result = value.reduce(reducer, {}); const result = value.reduce(reducer, {});
return v.parse(mrfSimpleSchema, result); return v.parse(mrfSimpleSchema, result);
} else { } else {
return v.parse(mrfSimpleSchema, {}); return {};
} }
}; };
const fromSimplePolicy = (simplePolicy: Policy) => { const fromSimplePolicy = (simplePolicy: Policy) => {
const mapper = ([key, hosts]: [key: string, hosts: Array<string>]) => ({ tuple: [`:${key}`, hosts] }); const mapper = ([key, hosts]: [key: string, hosts: Array<[string, string]>]) => ({ tuple: [`:${key}`, hosts.map(host => ({ tuple: host }))] });
const value = Object.entries(simplePolicy).map(mapper); const value = Object.entries(simplePolicy).map(mapper);