Some checks failed
CI / build-and-test (push) Failing after -31m25s
* 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
166 lines
4.5 KiB
Svelte
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>
|