feat: update session cookie options and enhance authentication flow

- Changed SameSite attribute for session cookies from 'strict' to 'lax' to allow cross-origin fetches, improving compatibility with various client requests.
- Updated cookie clearing logic in the authentication route to use `res.cookie()` for better reliability in cross-origin environments.
- Refactored the login view to implement a state machine for managing authentication phases, enhancing clarity and maintainability.
- Introduced a new logged-out view to inform users of session expiration and provide options to log in or retry.
- Added account and security sections to the settings view, allowing users to manage their account and security preferences more effectively.
This commit is contained in:
webdevcody
2026-01-07 12:55:23 -05:00
parent 927451013c
commit 70c04b5a3f
20 changed files with 895 additions and 304 deletions

View File

@@ -20,8 +20,8 @@
import { useEffect, useState, useRef } from 'react';
import { createLogger } from '@automaker/utils/logger';
import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
import { getItem, removeItem } from '@/lib/storage';
import { useAppStore } from '@/store/app-store';
import { getItem, removeItem, setItem } from '@/lib/storage';
import { useAppStore, THEME_STORAGE_KEY } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import type { GlobalSettings } from '@automaker/types';
@@ -69,7 +69,12 @@ let migrationCompleteResolve: (() => void) | null = null;
let migrationCompletePromise: Promise<void> | null = null;
let migrationCompleted = false;
function signalMigrationComplete(): void {
/**
* Signal that migration/hydration is complete.
* Call this after hydrating the store from server settings.
* This unblocks useSettingsSync so it can start syncing changes.
*/
export function signalMigrationComplete(): void {
migrationCompleted = true;
if (migrationCompleteResolve) {
migrationCompleteResolve();
@@ -436,7 +441,7 @@ export function useSettingsMigration(): MigrationState {
/**
* Hydrate the Zustand store from settings object
*/
function hydrateStoreFromSettings(settings: GlobalSettings): void {
export function hydrateStoreFromSettings(settings: GlobalSettings): void {
const current = useAppStore.getState();
// Convert ProjectRef[] to Project[] (minimal data, features will be loaded separately)
@@ -458,6 +463,11 @@ function hydrateStoreFromSettings(settings: GlobalSettings): void {
}
}
// Save theme to localStorage for fallback when server settings aren't available
if (settings.theme) {
setItem(THEME_STORAGE_KEY, settings.theme);
}
useAppStore.setState({
theme: settings.theme as unknown as import('@/store/app-store').ThemeMode,
sidebarOpen: settings.sidebarOpen ?? true,

View File

@@ -14,7 +14,8 @@
import { useEffect, useRef, useCallback, useState } from 'react';
import { createLogger } from '@automaker/utils/logger';
import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
import { useAppStore, type ThemeMode } from '@/store/app-store';
import { setItem } from '@/lib/storage';
import { useAppStore, type ThemeMode, THEME_STORAGE_KEY } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import { waitForMigrationComplete } from './use-settings-migration';
import type { GlobalSettings } from '@automaker/types';
@@ -339,6 +340,11 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
const serverSettings = result.settings as unknown as GlobalSettings;
const currentAppState = useAppStore.getState();
// Save theme to localStorage for fallback when server settings aren't available
if (serverSettings.theme) {
setItem(THEME_STORAGE_KEY, serverSettings.theme);
}
useAppStore.setState({
theme: serverSettings.theme as unknown as ThemeMode,
sidebarOpen: serverSettings.sidebarOpen,