import React from "react"; import {useSidebar} from "@/components/ui/sidebar"; import {Separator} from "@/components/ui/separator.tsx"; import {Button} from "@/components/ui/button.tsx"; import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx"; import {Checkbox} from "@/components/ui/checkbox.tsx"; import {Input} from "@/components/ui/input.tsx"; import {Label} from "@/components/ui/label.tsx"; import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs.tsx"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table.tsx"; import {Shield, Trash2, Users} from "lucide-react"; import { getOIDCConfig, getRegistrationAllowed, getUserList, updateRegistrationAllowed, updateOIDCConfig, makeUserAdmin, removeAdminStatus, deleteUser } from "@/ui/main-axios.ts"; function getCookie(name: string) { return document.cookie.split('; ').reduce((r, v) => { const parts = v.split('='); return parts[0] === name ? decodeURIComponent(parts[1]) : r; }, ""); } interface AdminSettingsProps { isTopbarOpen?: boolean; } export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React.ReactElement { const {state: sidebarState} = useSidebar(); const [allowRegistration, setAllowRegistration] = React.useState(true); const [regLoading, setRegLoading] = React.useState(false); const [oidcConfig, setOidcConfig] = React.useState({ client_id: '', client_secret: '', issuer_url: '', authorization_url: '', token_url: '', identifier_path: 'sub', name_path: 'name', scopes: 'openid email profile' }); const [oidcLoading, setOidcLoading] = React.useState(false); const [oidcError, setOidcError] = React.useState(null); const [oidcSuccess, setOidcSuccess] = React.useState(null); const [users, setUsers] = React.useState>([]); const [usersLoading, setUsersLoading] = React.useState(false); const [newAdminUsername, setNewAdminUsername] = React.useState(""); const [makeAdminLoading, setMakeAdminLoading] = React.useState(false); const [makeAdminError, setMakeAdminError] = React.useState(null); const [makeAdminSuccess, setMakeAdminSuccess] = React.useState(null); React.useEffect(() => { const jwt = getCookie("jwt"); if (!jwt) return; getOIDCConfig() .then(res => { if (res) setOidcConfig(res); }) .catch(() => { }); fetchUsers(); }, []); React.useEffect(() => { getRegistrationAllowed() .then(res => { if (typeof res?.allowed === 'boolean') { setAllowRegistration(res.allowed); } }) .catch(() => { }); }, []); const fetchUsers = async () => { const jwt = getCookie("jwt"); if (!jwt) return; setUsersLoading(true); try { const response = await getUserList(); setUsers(response.users); } finally { setUsersLoading(false); } }; const handleToggleRegistration = async (checked: boolean) => { setRegLoading(true); const jwt = getCookie("jwt"); try { await updateRegistrationAllowed(checked); setAllowRegistration(checked); } finally { setRegLoading(false); } }; const handleOIDCConfigSubmit = async (e: React.FormEvent) => { e.preventDefault(); setOidcLoading(true); setOidcError(null); setOidcSuccess(null); const required = ['client_id', 'client_secret', 'issuer_url', 'authorization_url', 'token_url']; const missing = required.filter(f => !oidcConfig[f as keyof typeof oidcConfig]); if (missing.length > 0) { setOidcError(`Missing required fields: ${missing.join(', ')}`); setOidcLoading(false); return; } const jwt = getCookie("jwt"); try { await updateOIDCConfig(oidcConfig); setOidcSuccess("OIDC configuration updated successfully!"); } catch (err: any) { setOidcError(err?.response?.data?.error || "Failed to update OIDC configuration"); } finally { setOidcLoading(false); } }; const handleOIDCConfigChange = (field: string, value: string) => { setOidcConfig(prev => ({...prev, [field]: value})); }; const makeUserAdmin = async (e: React.FormEvent) => { e.preventDefault(); if (!newAdminUsername.trim()) return; setMakeAdminLoading(true); setMakeAdminError(null); setMakeAdminSuccess(null); const jwt = getCookie("jwt"); try { await makeUserAdmin(newAdminUsername.trim()); setMakeAdminSuccess(`User ${newAdminUsername} is now an admin`); setNewAdminUsername(""); fetchUsers(); } catch (err: any) { setMakeAdminError(err?.response?.data?.error || "Failed to make user admin"); } finally { setMakeAdminLoading(false); } }; const removeAdminStatus = async (username: string) => { if (!confirm(`Remove admin status from ${username}?`)) return; const jwt = getCookie("jwt"); try { await removeAdminStatus(username); fetchUsers(); } catch { } }; const deleteUser = async (username: string) => { if (!confirm(`Delete user ${username}? This cannot be undone.`)) return; const jwt = getCookie("jwt"); try { await deleteUser(username); fetchUsers(); } catch { } }; const topMarginPx = isTopbarOpen ? 74 : 26; const leftMarginPx = sidebarState === 'collapsed' ? 26 : 8; const bottomMarginPx = 8; const wrapperStyle: React.CSSProperties = { marginLeft: leftMarginPx, marginRight: 17, marginTop: topMarginPx, marginBottom: bottomMarginPx, height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)` }; return (

Admin Settings

General OIDC Users Admins

User Registration

External Authentication (OIDC)

Configure external identity provider for OIDC/OAuth2 authentication.

{oidcError && ( Error {oidcError} )}
handleOIDCConfigChange('client_id', e.target.value)} placeholder="your-client-id" required/>
handleOIDCConfigChange('client_secret', e.target.value)} placeholder="your-client-secret" required/>
handleOIDCConfigChange('authorization_url', e.target.value)} placeholder="https://your-provider.com/application/o/authorize/" required/>
handleOIDCConfigChange('issuer_url', e.target.value)} placeholder="https://your-provider.com/application/o/termix/" required/>
handleOIDCConfigChange('token_url', e.target.value)} placeholder="https://your-provider.com/application/o/token/" required/>
handleOIDCConfigChange('identifier_path', e.target.value)} placeholder="sub" required/>
handleOIDCConfigChange('name_path', e.target.value)} placeholder="name" required/>
handleOIDCConfigChange('scopes', (e.target as HTMLInputElement).value)} placeholder="openid email profile" required/>
{oidcSuccess && ( Success {oidcSuccess} )}

User Management

{usersLoading ? (
Loading users...
) : (
Username Type Actions {users.map((user) => ( {user.username} {user.is_admin && ( Admin )} {user.is_oidc ? "External" : "Local"} ))}
)}

Admin Management

Make User Admin

setNewAdminUsername(e.target.value)} placeholder="Enter username to make admin" required/>
{makeAdminError && ( Error {makeAdminError} )} {makeAdminSuccess && ( Success {makeAdminSuccess} )}

Current Admins

Username Type Actions {users.filter(u => u.is_admin).map((admin) => ( {admin.username} Admin {admin.is_oidc ? "External" : "Local"} ))}
); } export default AdminSettings;