feat: instance-level streaming limits with admin settings panel
Add a server-side instance_settings table (single-row, CHECK(id=1)) that stores admin-configurable streaming bounds: bitrate min/max/step, allowed resolutions, and allowed framerates. Backend: - New instance_settings schema + migrations (is_admin on users, default settings row, first-registered-user promoted to admin) - GET/PATCH /api/settings/streaming endpoints with admin-only writes and full input validation including cross-field checks Frontend: - settingsStore fetches limits on WebSocket ready, exposes isAdmin flag - ScreenShareSettingsPopover reads bounds from store instead of hardcoded constants, auto-clamps stale localStorage values - buildScreenShareOptions() clamps bitrate to server limits at build time as enforcement backstop - ServerSettings modal gains a "Streaming" tab (admin-only) with bitrate range sliders, resolution/framerate toggles, and save/reset
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import { create } from 'zustand';
|
||||
import type { InstanceStreamingLimits } from '@opencord/shared';
|
||||
import { api } from '../api/client';
|
||||
|
||||
interface SettingsState {
|
||||
streamingLimits: InstanceStreamingLimits | null;
|
||||
isAdmin: boolean;
|
||||
fetchStreamingLimits: () => Promise<void>;
|
||||
updateStreamingLimits: (limits: Partial<InstanceStreamingLimits>) => Promise<void>;
|
||||
setIsAdmin: (isAdmin: boolean) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_LIMITS: InstanceStreamingLimits = {
|
||||
maxBitrateKbps: 20000,
|
||||
minBitrateKbps: 500,
|
||||
bitrateStepKbps: 500,
|
||||
allowedResolutions: [540, 720, 1080],
|
||||
allowedFramerates: [30, 45, 60],
|
||||
maxResolution: 1080,
|
||||
maxFramerate: 60,
|
||||
};
|
||||
|
||||
export function getStreamingLimits(): InstanceStreamingLimits {
|
||||
return useSettingsStore.getState().streamingLimits ?? DEFAULT_LIMITS;
|
||||
}
|
||||
|
||||
export const useSettingsStore = create<SettingsState>((set) => ({
|
||||
streamingLimits: null,
|
||||
isAdmin: false,
|
||||
|
||||
fetchStreamingLimits: async () => {
|
||||
try {
|
||||
const limits = await api.settings.getStreaming();
|
||||
set({ streamingLimits: limits });
|
||||
} catch (err) {
|
||||
console.warn('[Settings] Failed to fetch streaming limits, using defaults:', err);
|
||||
set({ streamingLimits: DEFAULT_LIMITS });
|
||||
}
|
||||
},
|
||||
|
||||
updateStreamingLimits: async (limits: Partial<InstanceStreamingLimits>) => {
|
||||
const updated = await api.settings.updateStreaming(limits);
|
||||
set({ streamingLimits: updated });
|
||||
},
|
||||
|
||||
setIsAdmin: (isAdmin: boolean) => set({ isAdmin }),
|
||||
}));
|
||||
Reference in New Issue
Block a user