Publish curated Unraid-AppOps-Gateway source from 68878c3167dacf2b424c16780e60e0d60815e477

This commit is contained in:
Public Source Publisher committed 2026-10-08 18:33:13 +00:00
1 parent 837c263cd8
commit 551de41e9f
18 files changed
+1183 -58

No files matched your search

+5 -1
View File
@@ -4,7 +4,7 @@ import { AISummary, AIRecommendation, AIChatMessage, AIAutopilotRule, AITool } f
import { toIsoString } from '@/lib/backendDateTime';
import type { AiSecurityFinding, AiSecurityRecommendation, AiSecurityReviewResult, AiSecurityScope, AiSecuritySummary, NotificationChannel, NotificationEvaluationResult, NotificationEvent, NotificationRule, NotificationSummary, RemediationApprovalProposal, RemediationDryRunResult, RemediationPlan, RemediationPlanCollection, SafetyDashboard, SafetyFinding, SafetyRecommendation, SafetyScoreBreakdown, SafetySection, SafetySummary, UnraidDisksResponse, UnraidDockerResponse, UnraidSharesResponse, UnraidVmCapabilitiesResponse, UnraidVmsResponse } from '../types/appops';
import type { GiteaActionsStatus, IntegrationStatus, GatewayUser, IdentityProvider, PermissionDefinition, UserAccessUpdate, UserAuthSession, RagQueryResult, RagStatus, RunnerFleetStatus } from '../types/appops';
import type { DeliveryStatus, DeploymentJob, Enrollment, EnrollmentCapabilities, EnrollmentStorageRootSelection, EnrollmentTrustStatus, ForgeRouteStageResult, WorkMeshPromotion } from '../types/appops';
import type { DeliveryStatus, DeploymentJob, Enrollment, EnrollmentCapabilities, EnrollmentStorageRootSelection, EnrollmentTrustStatus, EnrollmentOidcProfile, EnrollmentOidcProfileStatus, ForgeRouteStageResult, WorkMeshPromotion } from '../types/appops';
import type { ExposurePreferences, ManagedAiConfig, ManagedAiConfigUpdate, ManagedAiStatus, ManagedAiInference } from '../types/appops';
type HealthStatus = {
@@ -386,6 +386,10 @@ export const createLiveClient = () => ({
},
async getEnrollmentCapabilities(): Promise<EnrollmentCapabilities> { return get<EnrollmentCapabilities>('/api/enrollments/capabilities', defaultConfig); },
async getEnrollmentTrustStatus(): Promise<EnrollmentTrustStatus> { return get<EnrollmentTrustStatus>('/api/enrollments/trust', defaultConfig); },
async getEnrollmentOidcProfile(): Promise<EnrollmentOidcProfileStatus> { return get<EnrollmentOidcProfileStatus>('/api/enrollments/trust/oidc-profile', defaultConfig); },
async updateEnrollmentOidcProfile(payload: EnrollmentOidcProfile): Promise<EnrollmentOidcProfileStatus> {
return patch<EnrollmentOidcProfileStatus>('/api/enrollments/trust/oidc-profile', payload, defaultConfig);
},
async updateEnrollmentStorageRoot(payload: EnrollmentStorageRootSelection): Promise<EnrollmentTrustStatus> {
return patch<EnrollmentTrustStatus>('/api/enrollments/trust/storage-root', payload, defaultConfig);
},
+82
View File
@@ -0,0 +1,82 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { KeyRound, LoaderCircle, RefreshCw } from 'lucide-react';
import { appopsClient } from '@/api/appopsClient';
import { useUserSession } from '@/hooks/useUserSession';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { formatOperatorError } from '@/lib/operatorError';
import type { EnrollmentOidcProfile } from '@/types/appops';
const emptyProfile: EnrollmentOidcProfile = {
issuer: '', authorization_flow: '', invalidation_flow: '', openid_mapping: '',
email_mapping: '', profile_mapping: '', roles_mapping: '', signing_key: '',
administrator_group: 'Administrator', support_agent_group: 'SupportAgent', read_only_group: 'ReadOnly',
};
const fields = [
['issuer', 'Authentik HTTPS-origin'], ['authorization_flow', 'Authorization flow ID'],
['invalidation_flow', 'Invalidation flow ID'], ['openid_mapping', 'OpenID mapping ID'],
['email_mapping', 'Email mapping ID'], ['profile_mapping', 'Identity-only profile mapping ID'],
['roles_mapping', 'Begrensde roles mapping ID'], ['signing_key', 'RSA signing key ID'],
] as const;
export const EnrollmentOidcSettings = () => {
const session = useUserSession();
const canManage = session.ready && session.authenticated && session.user?.role === 'admin' && session.permissions.includes('settings.manage');
const [draft, setDraft] = useState<EnrollmentOidcProfile>(emptyProfile);
const [configured, setConfigured] = useState(false);
const [loaded, setLoaded] = useState(false);
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const [notice, setNotice] = useState('');
const sequence = useRef(0);
const load = useCallback(async () => {
if (!canManage) return;
const request = ++sequence.current;
setBusy(true); setError(''); setLoaded(false); setNotice('');
try {
const status = await appopsClient.getEnrollmentOidcProfile();
if (request !== sequence.current) return;
setDraft(status.profile ?? { ...emptyProfile, issuer: status.issuerOrigin ?? '' }); setConfigured(status.configured); setLoaded(true);
} catch (failure) {
if (request === sequence.current) setError(formatOperatorError(failure, 'OIDC-rootprofiel laden mislukt.'));
} finally { if (request === sequence.current) setBusy(false); }
}, [canManage]);
const invalidate = useCallback(() => { sequence.current++; }, []);
useEffect(() => { void load(); return invalidate; }, [load, invalidate]);
const save = async () => {
if (!canManage || !loaded || busy) return;
const request = ++sequence.current;
setBusy(true); setError(''); setNotice('');
try {
const status = await appopsClient.updateEnrollmentOidcProfile(draft);
if (request !== sequence.current) return;
setDraft(status.profile ?? emptyProfile); setConfigured(status.configured);
setNotice('Bestaande Authentik-templates geverifieerd en het rootprofiel opgeslagen.');
} catch (failure) {
if (request === sequence.current) setError(formatOperatorError(failure, 'OIDC-rootprofiel opslaan mislukt.'));
} finally { if (request === sequence.current) setBusy(false); }
};
if (!canManage) return null;
return <Card className="mb-5 border-slate-700 bg-slate-900/50">
<CardHeader><CardTitle className="flex items-center gap-2 text-sm"><KeyRound className="h-4 w-4" /> Authenticatie voor nieuwe deploymentdoelen</CardTitle></CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-slate-400">Koppel bestaande Authentik-templates voor native OIDC. Rollen: Administrator, SupportAgent en ReadOnly. Nieuwe applicaties krijgen bij enrollment een eigen client en secret.</p>
<p className="text-sm">Rootprofiel: {configured ? 'gekoppeld' : 'niet gekoppeld'}. De actuele deployment-readiness controleert de tenant opnieuw.</p>
{error && <p role="alert" className="text-sm text-red-300">{error}</p>}
{notice && <p role="status" className="text-sm text-emerald-300">{notice}</p>}
<div className="grid gap-4 md:grid-cols-2">{fields.map(([field, label]) => <div key={field} className="space-y-2">
<Label htmlFor={`enrollment-oidc-${field}`}>{label}</Label>
<Input id={`enrollment-oidc-${field}`} value={draft[field]} disabled={busy || !loaded}
onChange={event => setDraft(current => ({ ...current, [field]: event.target.value.trim() }))} />
</div>)}</div>
<div className="flex gap-2">
<Button type="button" variant="outline" disabled={busy} onClick={() => void load()}><RefreshCw className="mr-2 h-4 w-4" />Vernieuwen</Button>
<Button type="button" disabled={busy || !loaded || fields.some(([field]) => !draft[field])} onClick={() => void save()}>
{busy && <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />}Verifiëren en opslaan
</Button>
</div>
</CardContent>
</Card>;
};
+2
View File
@@ -16,6 +16,7 @@ import { UserAccessSettings } from "@/components/UserAccessSettings";
import { ExposureSettings } from "@/components/ExposureSettings";
import { ManagedAiSettings } from "@/components/ManagedAiSettings";
import { ManagedStorageSettings } from "@/components/ManagedStorageSettings";
import { EnrollmentOidcSettings } from "@/components/EnrollmentOidcSettings";
const settingsTabs = ["general", "security", "ai", "notifications", "audit", "voice", "maintenance", "backups"] as const;
type SettingsTab = typeof settingsTabs[number];
@@ -196,6 +197,7 @@ const Settings = () => {
<TabsContent value="general">
<ManagedStorageSettings />
<EnrollmentOidcSettings />
<Card className="signal-rail border-slate-700 bg-slate-900/50">
<CardHeader>
<CardTitle className="text-sm font-bold uppercase tracking-wider text-slate-400">Runtime Profiel</CardTitle>
+19
View File
@@ -164,6 +164,25 @@ export interface EnrollmentStorageRootSelection {
managed_appdata_backup_acknowledged: boolean;
}
export interface EnrollmentOidcProfile {
issuer: string;
authorization_flow: string;
invalidation_flow: string;
openid_mapping: string;
email_mapping: string;
profile_mapping: string;
roles_mapping: string;
signing_key: string;
administrator_group: 'Administrator';
support_agent_group: 'SupportAgent';
read_only_group: 'ReadOnly';
}
export interface EnrollmentOidcProfileStatus {
configured: boolean;
issuerOrigin: string | null;
profile: EnrollmentOidcProfile | null;
}
export interface Enrollment {
id: string;
applicationId: string;