import { createSignal } from "solid-js" import { isElectronHost } from "./runtime-env" // Storage key for active user const ACTIVE_USER_KEY = "codenomad_active_user_id" const [isLoggedIn, setLoggedIn] = createSignal(false) const [isInitialized, setInitialized] = createSignal(false) export { isLoggedIn, setLoggedIn, isInitialized } /** * Set the active user ID */ export function setActiveUserId(userId: string | null): void { if (userId) { localStorage.setItem(ACTIVE_USER_KEY, userId) setLoggedIn(true) console.log(`[UserContext] Active user set to: ${userId}`) } else { localStorage.removeItem(ACTIVE_USER_KEY) setLoggedIn(false) console.log(`[UserContext] Active user cleared`) } } /** * Get the active user ID */ export function getActiveUserId(): string | null { return localStorage.getItem(ACTIVE_USER_KEY) } /** * Get headers with user ID for API requests */ export function getUserHeaders(): Record { const userId = getActiveUserId() if (userId) { return { "X-User-Id": userId } } return {} } /** * Create fetch options with user headers */ export function withUserHeaders(options: RequestInit = {}): RequestInit { const userHeaders = getUserHeaders() if (Object.keys(userHeaders).length === 0) return options const headers = new Headers(options.headers || {}) for (const [key, value] of Object.entries(userHeaders)) { headers.set(key, value) } return { ...options, headers, } } /** * Fetch wrapper that automatically includes user headers */ export async function userFetch(url: string | URL | Request, options: RequestInit = {}): Promise { return fetch(url, withUserHeaders(options)) } /** * Globally patch fetch to include user headers for all internal /api/* requests * This ensures compatibility with legacy code and 3rd party libraries. */ export function patchFetch(): void { if ((window as any)._codenomad_fetch_patched) return (window as any)._codenomad_fetch_patched = true const originalFetch = window.fetch window.fetch = async function (input: RequestInfo | URL, init?: RequestInit) { let url = "" if (typeof input === "string") { url = input } else if (input instanceof URL) { url = input.toString() } else if (input instanceof Request) { url = input.url } // Only inject headers for internal API calls if (url.startsWith("/api/") || url.includes(window.location.origin + "/api/")) { return originalFetch(input, withUserHeaders(init)) } return originalFetch(input, init) } console.log("[UserContext] Global fetch patched for /api/* requests") } /** * Initialize user context from Host (Electron/Tauri) or API * Call this on app startup */ export async function initializeUserContext(): Promise { console.log(`[UserContext] Initializing... host=${isElectronHost()}`) try { if (isElectronHost()) { const api = (window as any).electronAPI if (api && api.getActiveUser) { console.log(`[UserContext] Requesting active user via api.getActiveUser()...`) const activeUser = await api.getActiveUser() console.log(`[UserContext] getActiveUser result:`, activeUser) if (activeUser?.id) { console.log(`[UserContext] Host has active session: ${activeUser.id}`) setActiveUserId(activeUser.id) } else { console.log(`[UserContext] Host has no active session. Enforcing login.`) setActiveUserId(null) } } else { console.warn(`[UserContext] electronAPI.getActiveUser not found. Falling back to web mode.`) await handleWebInit() } } else { await handleWebInit() } } catch (error) { console.error(`[UserContext] Critical initialization error:`, error) setActiveUserId(null) } finally { setInitialized(true) } } async function handleWebInit() { console.log(`[UserContext] Web init - checking local cache...`) const existingId = getActiveUserId() // In "Mandatory Login" mode, we might want to clear this on every fresh load // but for now let's see if the server validates it. if (existingId) { // We could verify this ID with the server here if we had a /api/users/me endpoint // For now, let's keep it but mark it as "unverified" or just let the first API fail console.log(`[UserContext] Found cached ID: ${existingId}. Validating session...`) // Strategy: We want mandatory login. If this is a fresh launch, we should probably clear it. // For Electron it's already cleared in main.ts. For Web it's tricky. // Let's lean towards SECURITY: if no one explicitly logged in THIS RUN, show login. // Actually, if we are in Electron and we hit this, it's because IPC failed. // If we are in Web, we trust it for now but we'll see. setLoggedIn(true) } else { console.log(`[UserContext] No cached ID found.`) setLoggedIn(false) } }