- Add Home/Profile links to nav bar alongside Vault/Settings/Logout - Profile page: change email, display name, password - TOTP 2FA: setup with QR code, verify, disable with password - Login flow: two-step TOTP challenge when 2FA is enabled - Backend: new endpoints PUT /profile, POST /totp/setup|verify|disable - Migration: add totp_secret and totp_enabled columns to users Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
54 lines
2.0 KiB
Vue
54 lines
2.0 KiB
Vue
<script setup lang="ts">
|
|
const auth = useAuthStore()
|
|
|
|
// Redirect to login if not authenticated
|
|
if (!auth.isAuthenticated) {
|
|
navigateTo('/login')
|
|
}
|
|
|
|
// Apply persisted theme on layout mount
|
|
onMounted(async () => {
|
|
if (!auth.isAuthenticated) return
|
|
try {
|
|
const settings = await $fetch('/api/settings', {
|
|
headers: { Authorization: `Bearer ${auth.token}` },
|
|
}) as Record<string, string>
|
|
const theme = settings.theme || 'dark'
|
|
if (theme === 'light') {
|
|
document.documentElement.classList.remove('dark')
|
|
document.documentElement.classList.add('light')
|
|
} else {
|
|
document.documentElement.classList.remove('light')
|
|
document.documentElement.classList.add('dark')
|
|
}
|
|
} catch {
|
|
// Default to dark theme if settings unavailable
|
|
document.documentElement.classList.add('dark')
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="h-screen flex flex-col bg-gray-950 text-gray-100">
|
|
<!-- Top bar -->
|
|
<header class="h-12 flex items-center justify-between px-4 bg-gray-900 border-b border-gray-800 shrink-0">
|
|
<div class="flex items-center gap-3">
|
|
<h1 class="text-lg font-bold tracking-wider text-wraith-400">WRAITH</h1>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<NuxtLink to="/" class="text-sm text-gray-400 hover:text-white">Home</NuxtLink>
|
|
<NuxtLink to="/vault" class="text-sm text-gray-400 hover:text-white">Vault</NuxtLink>
|
|
<NuxtLink to="/profile" class="text-sm text-gray-400 hover:text-white">Profile</NuxtLink>
|
|
<NuxtLink to="/settings" class="text-sm text-gray-400 hover:text-white">Settings</NuxtLink>
|
|
<button @click="auth.logout()" class="text-sm text-gray-500 hover:text-red-400">Logout</button>
|
|
</div>
|
|
</header>
|
|
<!-- Main content area — relative for SessionContainer absolute positioning -->
|
|
<div class="flex-1 flex overflow-hidden relative">
|
|
<slot />
|
|
<!-- Session container persists across page navigation, overlays content when sessions are active -->
|
|
<SessionContainer />
|
|
</div>
|
|
</div>
|
|
</template>
|