diff --git a/packages/web/src/components/modals/instanceSettingsPanels/FederationPanel.tsx b/packages/web/src/components/modals/instanceSettingsPanels/FederationPanel.tsx index 5bab0dd1..52e62518 100644 --- a/packages/web/src/components/modals/instanceSettingsPanels/FederationPanel.tsx +++ b/packages/web/src/components/modals/instanceSettingsPanels/FederationPanel.tsx @@ -1,10 +1,153 @@ +import { useState, useEffect, useCallback } from 'react'; +import { useSettingsStore } from '../../../stores/settingsStore'; +import { useUIStore } from '../../../stores/uiStore'; +import { Toggle } from '../../ui/Toggle'; +import { api } from '../../../api/client'; +import type { InstanceAdminSettings } from '@backspace/shared'; +import type { FederationPeer } from '../../../api/client'; + +// ─── Global Settings ───────────────────────────────────────────────────────── + +function FederationGlobalSettings() { + const instanceSettings = useSettingsStore((s) => s.instanceSettings); + const updateInstanceSettings = useSettingsStore((s) => s.updateInstanceSettings); + const addToast = useUIStore((s) => s.addToast); + + const [draft, setDraft] = useState | null>(null); + const [saving, setSaving] = useState(false); + const [saveError, setSaveError] = useState(''); + + useEffect(() => { + if (instanceSettings) { + setDraft({ + federationRelayEnabled: instanceSettings.federationRelayEnabled, + federationRelayTtlDays: instanceSettings.federationRelayTtlDays, + defaultAutoRotateIntervalDays: instanceSettings.defaultAutoRotateIntervalDays, + }); + } + }, [instanceSettings]); + + if (!draft) return null; + + const hasChanges = instanceSettings + ? draft.federationRelayEnabled !== instanceSettings.federationRelayEnabled || + draft.federationRelayTtlDays !== instanceSettings.federationRelayTtlDays || + draft.defaultAutoRotateIntervalDays !== instanceSettings.defaultAutoRotateIntervalDays + : false; + + const handleSave = async () => { + setSaving(true); + setSaveError(''); + try { + await updateInstanceSettings(draft); + addToast('Settings saved', 'success', 2000); + } catch (err) { + setSaveError(err instanceof Error ? err.message : 'Failed to save'); + } finally { + setSaving(false); + } + }; + + const handleReset = () => { + if (instanceSettings) { + setDraft({ + federationRelayEnabled: instanceSettings.federationRelayEnabled, + federationRelayTtlDays: instanceSettings.federationRelayTtlDays, + defaultAutoRotateIntervalDays: instanceSettings.defaultAutoRotateIntervalDays, + }); + } + setSaveError(''); + }; + + return ( +
+
Relay Settings
+

+ Control DM relay between federated instances. When enabled, DMs with users on peer instances are relayed server-to-server. +

+
+ + +
+
Relay TTL (days)
+
How long relayed messages are retained in the outbox before cleanup
+ { + const val = parseInt(e.target.value, 10); + if (!isNaN(val) && val >= 1 && val <= 365) { + setDraft({ ...draft, federationRelayTtlDays: val }); + } + }} + className="input-standard w-24" + /> +
+ +
+
Default Secret Rotation (days)
+
Auto-rotation interval for new peers. Existing peers keep their current setting.
+ { + const val = parseInt(e.target.value, 10); + if (!isNaN(val) && val >= 1 && val <= 365) { + setDraft({ ...draft, defaultAutoRotateIntervalDays: val }); + } + }} + className="input-standard w-24" + /> +
+
+ + {saveError && ( +
{saveError}
+ )} + + {hasChanges && ( +
+
+
+ + +
+
+
+ )} +
+ ); +} + +// ─── Main Panel ────────────────────────────────────────────────────────────── + export function FederationPanel() { return ( -
+
e.preventDefault()}>

Federation

Configure federation relay, secret rotation, and manage peered instances.
-
+ + + ); }