apps/web/src/lib/api/httpClient.ts 暴露两个 Axios 实例,它们都指向这同一个源、共享同一个鉴权拦截器。所有网络调用都走它们——不要直接 fetch,不要为某个功能再 axios.create() 一个。
单一源
NEXT_PUBLIC_API_URL 是唯一的后端变量,浏览器端和服务端用同一套解析——没有按服务拆分的历史回退。它指向网关(apps/gateway,dev 里 :3110),网关按路径前缀转发到对应上游(platform-api 还是 agent-service)。前端从不直接寻址某个具体服务。
API_ORIGIN 放在无依赖的 routes.ts(而非 httpClient.ts),这样 client 和 server 都能 import 它、又不会顺带把 Axios 拉进来。两个实例,同一个源
两者都是
createClient(API_ORIGIN)——同一个 base URL。拆成两个纯粹是为了调用侧语义(CRUD 和 AI 分开读起来更清楚),并不是两个后端。每条 /api/... 具体去哪,由网关决定。
鉴权拦截器
configureHttpClient(getter) 在两个客户端上注册同一个请求拦截器:
getAuthToken(),拿到 token 就设 Authorization: Bearer <jwt>(除非调用方已自带)。getAuthToken() 同时缓存最近一次有效 token;getCachedAuthToken() 把它同步暴露出来,供退出时的 keepalive 请求用(见 resumeApi.syncResumeKeepalive)——那种场景页面随时会死,等不起异步取 token。
集中存路由
所有路径放在src/lib/api/routes.ts,且都在 /api/* 前缀下(网关的命名空间):
- 不允许内联路径字符串——每个后端路径都进这几张表。
API_ROUTES/AGENT_ROUTES打到网关;WEB_AGENT_ROUTES是 web 应用自己的 Next.js 路由处理器(app/api/chat-agent/*),再由它代理到 agent。
加一个新接口
- 把路径加到
routes.ts里对应的表。 - 在
src/lib/api/<feature>.ts里加一个带类型的封装,返回类型化响应。 - 用
httpClient.api或httpClient.agent——不要再axios.create()第三个客户端。 - 如果是云端专属功能,在调用侧用
APP_MODE === 'cloud'做开关。
