feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan

- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
  encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
  status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
  disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
  (onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-06 12:23:57 +00:00
parent 0b3c23ff76
commit e55fd3baf0
16 changed files with 2402 additions and 152 deletions

View File

@@ -27,23 +27,28 @@ export const useAppStore = defineStore('app', () => {
const isOffline = computed(() => !isConnected.value || isRestarting.value || isShuttingDown.value)
// Actions
async function login(password: string): Promise<void> {
async function login(password: string): Promise<{ requires_totp?: boolean }> {
isLoading.value = true
error.value = null
try {
await rpcClient.login(password)
const result = await rpcClient.login(password)
if (result && result.requires_totp) {
return { requires_totp: true }
}
isAuthenticated.value = true
sessionValidated = true
localStorage.setItem('neode-auth', 'true')
// Initialize data structure immediately so dashboard can render
await initializeData()
// Connect WebSocket in background - don't block login flow
connectWebSocket().catch((err) => {
console.warn('[Store] WebSocket connection failed after login, will retry:', err)
})
return {}
} catch (err) {
error.value = err instanceof Error ? err.message : 'Login failed'
throw err
@@ -52,6 +57,16 @@ export const useAppStore = defineStore('app', () => {
}
}
async function completeLoginAfterTotp(): Promise<void> {
isAuthenticated.value = true
sessionValidated = true
localStorage.setItem('neode-auth', 'true')
await initializeData()
connectWebSocket().catch((err) => {
console.warn('[Store] WebSocket connection failed after TOTP login, will retry:', err)
})
}
async function logout(): Promise<void> {
try {
await rpcClient.logout()
@@ -285,6 +300,7 @@ export const useAppStore = defineStore('app', () => {
// Actions
login,
completeLoginAfterTotp,
logout,
checkSession,
needsSessionValidation,