feat(control-panel): add oidc account switching
This commit is contained in:
@@ -8,9 +8,10 @@ import { ChatModule } from './chat/chat.module.js';
|
||||
import { ProviderAuthModule } from './provider-auth/provider-auth.module.js';
|
||||
import { GoalsModule } from './goals/goals.module.js';
|
||||
import { EvidenceModule } from './evidence/evidence.module.js';
|
||||
import { SessionController } from './session/session.controller.js';
|
||||
|
||||
@Module({
|
||||
imports: [TelemetryModule, SettingsModule, KillSwitchModule, ApprovalsModule, ChatModule, ProviderAuthModule, GoalsModule, EvidenceModule],
|
||||
controllers: [HealthController],
|
||||
controllers: [HealthController, SessionController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Controller, Get, Headers } from '@nestjs/common';
|
||||
import { ok } from '../common/api-response.js';
|
||||
import { actorFromHeaders } from '../common/auth-context.js';
|
||||
|
||||
@Controller('api/v1/session')
|
||||
export class SessionController {
|
||||
@Get()
|
||||
current(@Headers() headers: Record<string, string | string[] | undefined>) {
|
||||
return ok(actorFromHeaders(headers));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { health } from '../../lib/api';
|
||||
import { api, health } from '../../lib/api';
|
||||
|
||||
const PAGE_COPY: Record<string, { title: string; eyebrow: string }> = {
|
||||
'/': { title: 'Operations overview', eyebrow: 'System posture' },
|
||||
@@ -18,6 +18,7 @@ const PAGE_COPY: Record<string, { title: string; eyebrow: string }> = {
|
||||
|
||||
export function Header() {
|
||||
const { data } = useQuery({ queryKey: ['health'], queryFn: health });
|
||||
const { data: session } = useQuery({ queryKey: ['session'], queryFn: api.session, staleTime: 60_000 });
|
||||
const { pathname } = useLocation();
|
||||
const stale = data ? !data.ok : true;
|
||||
const page = PAGE_COPY[pathname] ?? PAGE_COPY['/'];
|
||||
@@ -36,6 +37,16 @@ export function Header() {
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${stale ? 'bg-amber-500' : 'bg-emerald-500'}`} />
|
||||
{stale ? `STALE${data?.metrics_age_s != null ? ` · ${data.metrics_age_s}s` : ''}` : 'LIVE TRUST'}
|
||||
</div>
|
||||
<div className="hidden min-w-0 rounded-xl border border-slate-200 bg-white px-3 py-1.5 sm:block">
|
||||
<div className="max-w-40 truncate font-semibold text-slate-800">{session?.actor ?? 'Loading identity…'}</div>
|
||||
<div className="text-[10px] font-bold uppercase tracking-wide text-indigo-600">{session?.role ?? 'OIDC'}</div>
|
||||
</div>
|
||||
<a
|
||||
href="/oauth2/sign_out?rd=https%3A%2F%2Flocalhost%3A18443%2F"
|
||||
className="rounded-xl border border-slate-300 bg-white px-3 py-2 font-semibold text-slate-700 transition-colors hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700"
|
||||
>
|
||||
Sign out / switch
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -424,6 +424,7 @@ function actorHeaders(actor: SettingsActor): Record<string, string> {
|
||||
}
|
||||
|
||||
export const api = {
|
||||
session: () => get<SettingsActor>('session'),
|
||||
overview: () => get<Overview>('overview'),
|
||||
runs: (limit = 50) => get<Freshness & { count: number; runs: HarnessRunRecord[] }>(`runs?limit=${limit}`),
|
||||
traceGraph: (traceId: string) => get<HarnessTraceGraph>(`runs/${encodeURIComponent(traceId)}/graph`),
|
||||
|
||||
Reference in New Issue
Block a user