Files
amcs/ui/src/App.svelte
Hein db7b152852
Some checks failed
CI / build-and-test (push) Failing after -31m25s
refactor(store): replace project and skill models with generated models
* Update project creation and retrieval to use generated models
* Modify skill addition and listing to utilize generated models
* Refactor thought handling to incorporate generated models
* Adjust tool annotations to align with new model structure
* Update API calls in the UI to use new ResolveSpec-based endpoints
* Enhance stats retrieval logic to aggregate thought metadata
2026-04-26 12:56:32 +02:00

166 lines
4.5 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import LoginPage from './components/auth/LoginPage.svelte';
import AdminShell from './components/shell/AdminShell.svelte';
import type { ShellPage, StatusResponse } from './types';
import { fromStore } from 'svelte/store';
import {
ensureApiURL,
exchangeOAuthCode,
GlobalStateStore,
isLoggedInStore,
loginWithCredentials,
setCurrentPath
} from './shellState';
let authMessage = $state('');
let authError = $state('');
let authBusy = $state(false);
let callbackBusy = $state(false);
let data = $state<StatusResponse | null>(null);
let loading = $state(false);
let error = $state('');
let currentPage = $state<ShellPage>('dashboard');
ensureApiURL(import.meta.env.VITE_API_URL);
GlobalStateStore.setState({
onFetchSession: async (state) => {
const token = state.session?.authToken;
if (!token) return {};
const res = await fetch('/api/rs/public/projects', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
operation: 'read',
options: { limit: 1 }
})
});
if (!res.ok) return { session: { loggedIn: false } };
return { session: { loggedIn: true, authToken: token } };
}
});
const isLoggedIn = fromStore(isLoggedInStore);
const currentPath = $derived(typeof window !== 'undefined' ? window.location.pathname : '/');
const isOAuthCallback = $derived(currentPath === '/oauth/callback');
async function handleCredentialLogin(username: string, password: string): Promise<void> {
authBusy = true;
authError = '';
authMessage = '';
try {
const token = await loginWithCredentials(username, password);
await GlobalStateStore.getState().login(token, { username });
authMessage = 'Login successful.';
await loadStatus();
} catch (err) {
authError = err instanceof Error ? err.message : 'Login failed.';
} finally {
authBusy = false;
}
}
async function finishOAuthLogin(): Promise<void> {
callbackBusy = true;
authError = '';
authMessage = '';
try {
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const oauthError = params.get('error');
if (oauthError) {
throw new Error(`OAuth login failed: ${oauthError}`);
}
if (!code || !returnedState) {
throw new Error('OAuth callback is missing code or state.');
}
const token = await exchangeOAuthCode(code, returnedState);
await GlobalStateStore.getState().login(token, { username: 'OAuth operator' });
authMessage = 'OAuth login complete. Welcome back.';
window.history.replaceState({}, '', '/');
await loadStatus();
} catch (err) {
authError = err instanceof Error ? err.message : 'OAuth callback failed.';
} finally {
callbackBusy = false;
}
}
async function logout(): Promise<void> {
await GlobalStateStore.getState().logout();
authMessage = 'Logged out.';
authError = '';
}
async function loadStatus(): Promise<void> {
loading = true;
error = '';
try {
const response = await fetch('/api/status');
if (!response.ok) {
throw new Error(`Status request failed with ${response.status}`);
}
data = (await response.json()) as StatusResponse;
} catch (err) {
error = err instanceof Error ? err.message : 'Failed to load status';
} finally {
loading = false;
}
}
onMount(async () => {
if (typeof window !== 'undefined') {
setCurrentPath(window.location.pathname);
}
if (isOAuthCallback) {
await finishOAuthLogin();
return;
}
await GlobalStateStore.getState().fetchData();
if (GlobalStateStore.getState().isLoggedIn()) {
await loadStatus();
}
});
</script>
<svelte:head>
<title>AMCS Admin</title>
</svelte:head>
<div class="min-h-screen bg-slate-950 text-slate-100">
{#if !isLoggedIn.current}
<LoginPage
{isOAuthCallback}
{callbackBusy}
{authBusy}
{authError}
{authMessage}
onlogin={handleCredentialLogin}
/>
{:else}
<AdminShell
{currentPage}
{data}
{loading}
{error}
onlogout={logout}
onnavigate={(page) => { currentPage = page; }}
onrefresh={loadStatus}
/>
{/if}
</div>