Merge remote-tracking branch 'upstream/dev' into feat/azure-ocr

This commit is contained in:
Anish Sarkar 2026-04-08 05:00:32 +05:30
commit 6038f6dfc0
84 changed files with 6041 additions and 1065 deletions

View file

@ -0,0 +1,63 @@
import {
type CreateVisionLLMConfigRequest,
createVisionLLMConfigRequest,
createVisionLLMConfigResponse,
deleteVisionLLMConfigResponse,
getGlobalVisionLLMConfigsResponse,
getModelListResponse,
getVisionLLMConfigsResponse,
type UpdateVisionLLMConfigRequest,
updateVisionLLMConfigRequest,
updateVisionLLMConfigResponse,
} from "@/contracts/types/new-llm-config.types";
import { ValidationError } from "../error";
import { baseApiService } from "./base-api.service";
class VisionLLMConfigApiService {
getModels = async () => {
return baseApiService.get(`/api/v1/vision-models`, getModelListResponse);
};
getGlobalConfigs = async () => {
return baseApiService.get(
`/api/v1/global-vision-llm-configs`,
getGlobalVisionLLMConfigsResponse
);
};
createConfig = async (request: CreateVisionLLMConfigRequest) => {
const parsed = createVisionLLMConfigRequest.safeParse(request);
if (!parsed.success) {
const msg = parsed.error.issues.map((i) => i.message).join(", ");
throw new ValidationError(`Invalid request: ${msg}`);
}
return baseApiService.post(`/api/v1/vision-llm-configs`, createVisionLLMConfigResponse, {
body: parsed.data,
});
};
getConfigs = async (searchSpaceId: number) => {
const params = new URLSearchParams({
search_space_id: String(searchSpaceId),
}).toString();
return baseApiService.get(`/api/v1/vision-llm-configs?${params}`, getVisionLLMConfigsResponse);
};
updateConfig = async (request: UpdateVisionLLMConfigRequest) => {
const parsed = updateVisionLLMConfigRequest.safeParse(request);
if (!parsed.success) {
const msg = parsed.error.issues.map((i) => i.message).join(", ");
throw new ValidationError(`Invalid request: ${msg}`);
}
const { id, data } = parsed.data;
return baseApiService.put(`/api/v1/vision-llm-configs/${id}`, updateVisionLLMConfigResponse, {
body: data,
});
};
deleteConfig = async (id: number) => {
return baseApiService.delete(`/api/v1/vision-llm-configs/${id}`, deleteVisionLLMConfigResponse);
};
}
export const visionLLMConfigApiService = new VisionLLMConfigApiService();

View file

@ -15,6 +15,7 @@ const PUBLIC_ROUTE_PREFIXES = [
"/login",
"/register",
"/auth",
"/desktop/login",
"/docs",
"/public",
"/invite",
@ -34,6 +35,11 @@ export function isPublicRoute(pathname: string): boolean {
return PUBLIC_ROUTE_PREFIXES.some((prefix) => pathname.startsWith(prefix));
}
export function getLoginPath(): string {
if (typeof window !== "undefined" && window.electronAPI) return "/desktop/login";
return "/login";
}
/**
* Clears tokens and optionally redirects to login.
* Call this when a 401 response is received.
@ -55,7 +61,7 @@ export function handleUnauthorized(): void {
if (!excludedPaths.includes(pathname)) {
localStorage.setItem(REDIRECT_PATH_KEY, currentPath);
}
window.location.href = "/login";
window.location.href = getLoginPath();
}
}
@ -87,6 +93,7 @@ export function getBearerToken(): string | null {
export function setBearerToken(token: string): void {
if (typeof window === "undefined") return;
localStorage.setItem(BEARER_TOKEN_KEY, token);
syncTokensToElectron();
}
/**
@ -111,6 +118,7 @@ export function getRefreshToken(): string | null {
export function setRefreshToken(token: string): void {
if (typeof window === "undefined") return;
localStorage.setItem(REFRESH_TOKEN_KEY, token);
syncTokensToElectron();
}
/**
@ -129,6 +137,44 @@ export function clearAllTokens(): void {
clearRefreshToken();
}
/**
* Pushes the current localStorage tokens into the Electron main process
* so that other BrowserWindows (Quick Ask, Autocomplete) can access them.
*/
function syncTokensToElectron(): void {
if (typeof window === "undefined" || !window.electronAPI?.setAuthTokens) return;
const bearer = localStorage.getItem(BEARER_TOKEN_KEY) || "";
const refresh = localStorage.getItem(REFRESH_TOKEN_KEY) || "";
if (bearer) {
window.electronAPI.setAuthTokens(bearer, refresh);
}
}
/**
* Attempts to pull auth tokens from the Electron main process into localStorage.
* Useful for popup windows (Quick Ask, Autocomplete) on platforms where
* localStorage is not reliably shared across BrowserWindow instances.
* Returns true if tokens were found and written to localStorage.
*/
export async function ensureTokensFromElectron(): Promise<boolean> {
if (typeof window === "undefined" || !window.electronAPI?.getAuthTokens) return false;
if (getBearerToken()) return true;
try {
const tokens = await window.electronAPI.getAuthTokens();
if (tokens?.bearer) {
localStorage.setItem(BEARER_TOKEN_KEY, tokens.bearer);
if (tokens.refresh) {
localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh);
}
return true;
}
} catch {
// IPC failure — fall through
}
return false;
}
/**
* Logout the current user by revoking the refresh token and clearing localStorage.
* Returns true if logout was successful (or tokens were cleared), false otherwise.
@ -181,13 +227,12 @@ export function redirectToLogin(): void {
const currentPath = window.location.pathname + window.location.search + window.location.hash;
// Don't save auth-related paths or home page
const excludedPaths = ["/auth", "/auth/callback", "/", "/login", "/register"];
const excludedPaths = ["/auth", "/auth/callback", "/", "/login", "/register", "/desktop/login"];
if (!excludedPaths.includes(window.location.pathname)) {
localStorage.setItem(REDIRECT_PATH_KEY, currentPath);
}
// Redirect to login page
window.location.href = "/login";
window.location.href = getLoginPath();
}
/**

View file

@ -39,6 +39,12 @@ export const cacheKeys = {
byId: (configId: number) => ["image-gen-configs", "detail", configId] as const,
global: () => ["image-gen-configs", "global"] as const,
},
visionLLMConfigs: {
all: (searchSpaceId: number) => ["vision-llm-configs", searchSpaceId] as const,
byId: (configId: number) => ["vision-llm-configs", "detail", configId] as const,
global: () => ["vision-llm-configs", "global"] as const,
modelList: () => ["vision-models", "catalogue"] as const,
},
auth: {
user: ["auth", "user"] as const,
},