- ws.ts: auth via query params (token + session), store session_id from assay session_info, mark connected on ready signal - useAgentSocket.ts: handlers for session_info, controls, artifacts, cleared - auth.ts: auto-set dev service token for instant login - Dockerfile + nginx.conf for K3s deployment - .env.production: wss://assay.loop42.de/ws Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
import { ref, type Ref } from 'vue';
|
|
import router from '../router';
|
|
|
|
const SESSION_TOKEN_KEY = 'nyx_session';
|
|
|
|
import { getApiBase } from '../utils/apiBase';
|
|
|
|
// Dev service token — auto-login for development
|
|
const DEV_TOKEN = '7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g';
|
|
|
|
export function useAuth(connectFn: () => void) {
|
|
// Auto-set dev token if no token exists
|
|
if (!localStorage.getItem(SESSION_TOKEN_KEY) && !localStorage.getItem('titan_token')) {
|
|
localStorage.setItem(SESSION_TOKEN_KEY, DEV_TOKEN);
|
|
}
|
|
const isLoggedIn: Ref<boolean> = ref(!!localStorage.getItem(SESSION_TOKEN_KEY));
|
|
const loginToken: Ref<string> = ref('');
|
|
const loginError: Ref<string> = ref('');
|
|
const loggingIn: Ref<boolean> = ref(false);
|
|
|
|
async function doLogin(): Promise<void> {
|
|
const token = loginToken.value.trim();
|
|
if (!token) return;
|
|
loggingIn.value = true;
|
|
loginError.value = '';
|
|
|
|
try {
|
|
const nonceRes = await fetch(`${getApiBase()}/api/auth/nonce`);
|
|
if (!nonceRes.ok) { loginError.value = 'Auth unavailable'; loggingIn.value = false; return; }
|
|
const { nonce } = await nonceRes.json();
|
|
const res = await fetch(`${getApiBase()}/api/auth`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ token, nonce }),
|
|
});
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({ error: 'Login failed' }));
|
|
loginError.value = err.error || 'Invalid token';
|
|
loggingIn.value = false;
|
|
return;
|
|
}
|
|
const { sessionToken } = await res.json();
|
|
localStorage.removeItem('titan_token');
|
|
localStorage.removeItem('nyx_token');
|
|
localStorage.setItem(SESSION_TOKEN_KEY, sessionToken);
|
|
sessionStorage.removeItem('agent');
|
|
isLoggedIn.value = true;
|
|
connectFn();
|
|
router.push('/chat');
|
|
setTimeout(() => { loggingIn.value = false; }, 500);
|
|
} catch {
|
|
loginError.value = 'Network error';
|
|
loggingIn.value = false;
|
|
}
|
|
}
|
|
|
|
async function doLogout(disconnectFn?: () => void): Promise<void> {
|
|
const sessionToken = localStorage.getItem(SESSION_TOKEN_KEY);
|
|
if (sessionToken) {
|
|
// Fire-and-forget revoke
|
|
fetch(`${getApiBase()}/api/auth/logout`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ sessionToken }),
|
|
}).catch(() => {});
|
|
}
|
|
if (disconnectFn) disconnectFn();
|
|
localStorage.removeItem(SESSION_TOKEN_KEY);
|
|
localStorage.removeItem('titan_token');
|
|
localStorage.removeItem('nyx_token');
|
|
sessionStorage.removeItem('agent');
|
|
sessionStorage.removeItem('viewer_auth'); // clear cached fstoken
|
|
isLoggedIn.value = false;
|
|
loginToken.value = '';
|
|
loggingIn.value = false;
|
|
router.push('/');
|
|
}
|
|
|
|
return { isLoggedIn, loginToken, loginError, loggingIn, doLogin, doLogout };
|
|
}
|