mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-28 22:17:48 +02:00
- Add GET /api/config endpoint exposing cdn_domain from CLOUDFRONT_DOMAIN - Create packages/core/config/ zustand store, fetched at app startup - Extract file card preprocessing to packages/ui/markdown/file-cards.ts with isCdnUrl(url, cdnDomain) using exact hostname match - Add file card support to packages/ui/markdown/Markdown.tsx (was missing) - Remove hardcoded .copilothub.ai hostname check from file-card.tsx - Fix LocalStorage.CdnDomain() to return hostname not full URL - Always run preprocessFileCards regardless of cdnDomain availability (!file syntax works without CDN domain, only legacy matching needs it) - Use useConfigStore hook in common/markdown.tsx for reactive updates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
89 lines
2.8 KiB
TypeScript
89 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, type ReactNode } from "react";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { getApi } from "../api";
|
|
import { useAuthStore } from "../auth";
|
|
import { useWorkspaceStore } from "../workspace";
|
|
import { configStore } from "../config";
|
|
import { workspaceKeys } from "../workspace/queries";
|
|
import { createLogger } from "../logger";
|
|
import { defaultStorage } from "./storage";
|
|
import type { StorageAdapter } from "../types/storage";
|
|
|
|
const logger = createLogger("auth");
|
|
|
|
export function AuthInitializer({
|
|
children,
|
|
onLogin,
|
|
onLogout,
|
|
storage = defaultStorage,
|
|
cookieAuth,
|
|
}: {
|
|
children: ReactNode;
|
|
onLogin?: () => void;
|
|
onLogout?: () => void;
|
|
storage?: StorageAdapter;
|
|
cookieAuth?: boolean;
|
|
}) {
|
|
const qc = useQueryClient();
|
|
|
|
useEffect(() => {
|
|
const api = getApi();
|
|
const wsId = storage.getItem("multica_workspace_id");
|
|
|
|
// Fetch app config (CDN domain, etc.) in the background — non-blocking.
|
|
api.getConfig().then((cfg) => {
|
|
if (cfg.cdn_domain) configStore.getState().setCdnDomain(cfg.cdn_domain);
|
|
}).catch(() => { /* config is optional — legacy file card matching degrades gracefully */ });
|
|
|
|
if (cookieAuth) {
|
|
// Cookie mode: the HttpOnly cookie is sent automatically by the browser.
|
|
// Call the API to check if the session is still valid.
|
|
Promise.all([api.getMe(), api.listWorkspaces()])
|
|
.then(([user, wsList]) => {
|
|
onLogin?.();
|
|
useAuthStore.setState({ user, isLoading: false });
|
|
qc.setQueryData(workspaceKeys.list(), wsList);
|
|
useWorkspaceStore.getState().hydrateWorkspace(wsList, wsId);
|
|
})
|
|
.catch((err) => {
|
|
logger.error("cookie auth init failed", err);
|
|
onLogout?.();
|
|
useAuthStore.setState({ user: null, isLoading: false });
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Token mode: read from localStorage (Electron / legacy).
|
|
const token = storage.getItem("multica_token");
|
|
if (!token) {
|
|
onLogout?.();
|
|
useAuthStore.setState({ isLoading: false });
|
|
return;
|
|
}
|
|
|
|
api.setToken(token);
|
|
|
|
Promise.all([api.getMe(), api.listWorkspaces()])
|
|
.then(([user, wsList]) => {
|
|
onLogin?.();
|
|
useAuthStore.setState({ user, isLoading: false });
|
|
// Seed React Query cache so components don't need a second fetch
|
|
qc.setQueryData(workspaceKeys.list(), wsList);
|
|
useWorkspaceStore.getState().hydrateWorkspace(wsList, wsId);
|
|
})
|
|
.catch((err) => {
|
|
logger.error("auth init failed", err);
|
|
api.setToken(null);
|
|
api.setWorkspaceId(null);
|
|
storage.removeItem("multica_token");
|
|
storage.removeItem("multica_workspace_id");
|
|
onLogout?.();
|
|
useAuthStore.setState({ user: null, isLoading: false });
|
|
});
|
|
}, []);
|
|
|
|
return <>{children}</>;
|
|
}
|