chore: directory migration - gateway→server, web→client/electron

This commit is contained in:
2026-09-08 19:16:35 +08:00
parent fd9f99a3f9
commit f9d757b5e5
243 changed files with 5095 additions and 228 deletions

View File

@ -0,0 +1,68 @@
/**
* API 接入配置。
*
* WebUI 与第三方客户端调用的是**同一套 WebAPI**,差别只在两点:
* 1. 基地址:内嵌在 Gateway 里时是同源的 /api/v1;独立部署的客户端需要指向具体主机
* 2. 凭证:浏览器用登录 Cookie;第三方客户端用用户密钥(Authorization: Bearer)
*
* 这两点都在此处集中配置,业务代码不感知差异 —— 这样把 src/api/ 整个抽成 SDK 时
* 不需要改任何调用点。
*/
/** 运行时注入点:宿主页面可在加载 bundle 前设置这两个全局量 */
declare global {
interface Window {
__AGENTMAIL_API_BASE__?: string;
__AGENTMAIL_TOKEN__?: string;
}
}
/**
* 基地址优先级:运行时全局 > 构建期环境变量 > 同源默认值。
*
* 运行时优先是为了让同一份构建产物能部署到不同后端(容器镜像不必按环境重打)。
*/
function resolveBase(): string {
const runtime = typeof window !== 'undefined' ? window.__AGENTMAIL_API_BASE__ : undefined;
const build = import.meta.env?.VITE_API_BASE as string | undefined;
const base = (runtime || build || '/api/v1').trim();
// 统一去掉尾部斜杠,拼接时只在 path 侧带前导斜杠
return base.replace(/\/+$/, '');
}
export const API_BASE = resolveBase();
/** 当前用于 Authorization 头的令牌;空表示走 Cookie。 */
let bearerToken: string | null =
(typeof window !== 'undefined' ? window.__AGENTMAIL_TOKEN__ : undefined) ?? null;
/**
* 设置用户密钥。第三方客户端在启动时调用一次即可,
* 之后所有请求(含 SSE 与附件下载)自动带上。
*/
export function setToken(token: string | null) {
bearerToken = token && token.trim() !== '' ? token.trim() : null;
}
export function getToken(): string | null {
return bearerToken;
}
/** 认证请求头。用 Cookie 时返回空对象。 */
export function authHeaders(): Record<string, string> {
return bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {};
}
/**
* 给 URL 附加认证信息,供无法设置请求头的场景使用:
* - EventSource(SSE)不支持自定义头
* - <a download> / <img src> 由浏览器直接发起
*
* 服务端仅在 SSE 与附件下载这两处接受 ?access_token=,
* 其余接口一律要求请求头 —— URL 里的令牌会进访问日志。
*/
export function withToken(url: string): string {
if (!bearerToken) return url;
const sep = url.includes('?') ? '&' : '?';
return `${url}${sep}access_token=${encodeURIComponent(bearerToken)}`;
}