feat: dedicated Instance Settings modal with admin controls

Move instance-level administration out of Space Settings into its own
modal. Adds admin UI for instance name, registration toggle, and
discovery toggle. Streaming limits panel relocated from SpaceSettings.

- Add InstanceAdminSettings type and GET/PATCH /api/settings/instance
- Add registration_open DB column (nullable, env var fallback)
- Auth registration and instance info now check DB override
- New InstanceSettings modal with General and Streaming tabs
- Admin shield button in UserAreaPanel (visible to admins only)
- Remove Streaming tab from SpaceSettings
This commit is contained in:
Jannis Braun
2026-03-09 03:52:01 +01:00
parent 096251e5b0
commit 450df432b2
15 changed files with 575 additions and 239 deletions
+27 -1
View File
@@ -1,12 +1,15 @@
import { create } from 'zustand';
import type { InstanceStreamingLimits } from '@backspace/shared';
import type { InstanceStreamingLimits, InstanceAdminSettings } from '@backspace/shared';
import { api } from '../api/client';
interface SettingsState {
streamingLimits: InstanceStreamingLimits | null;
instanceSettings: InstanceAdminSettings | null;
isAdmin: boolean;
fetchStreamingLimits: () => Promise<void>;
updateStreamingLimits: (limits: Partial<InstanceStreamingLimits>) => Promise<void>;
fetchInstanceSettings: () => Promise<void>;
updateInstanceSettings: (data: Partial<InstanceAdminSettings>) => Promise<void>;
setIsAdmin: (isAdmin: boolean) => void;
}
@@ -27,6 +30,7 @@ export function getStreamingLimits(): InstanceStreamingLimits {
export const useSettingsStore = create<SettingsState>((set) => ({
streamingLimits: null,
instanceSettings: null,
isAdmin: false,
fetchStreamingLimits: async () => {
@@ -44,5 +48,27 @@ export const useSettingsStore = create<SettingsState>((set) => ({
set({ streamingLimits: updated });
},
fetchInstanceSettings: async () => {
try {
const settings = await api.settings.getInstance();
set({ instanceSettings: settings });
} catch (err) {
console.warn('[Settings] Failed to fetch instance settings:', err);
}
},
updateInstanceSettings: async (data: Partial<InstanceAdminSettings>) => {
const updated = await api.settings.updateInstance(data);
set({ instanceSettings: updated });
// If discoveryEnabled changed, also update it in streamingLimits for the DiscoveryPanel warning banner
if (data.discoveryEnabled !== undefined) {
set((state) => ({
streamingLimits: state.streamingLimits
? { ...state.streamingLimits, discoveryEnabled: updated.discoveryEnabled }
: state.streamingLimits,
}));
}
},
setIsAdmin: (isAdmin: boolean) => set({ isAdmin }),
}));
+1
View File
@@ -10,6 +10,7 @@ type ModalType =
| 'userSettings'
| 'spaceSettings'
| 'channelSettings'
| 'instanceSettings'
| 'imagePreview'
| 'newDm'
| 'addDmMember'