refactor(websocket): 重构 WebSocket 和 SSE 连接逻辑以支持开发和生产环境
- 统一处理 WebSocket 和 SSE 的 URL 构造逻辑 - 开发环境使用代理前缀,生产环境使用同域路径 - 移除硬编码端口,通过环境变量配置
This commit is contained in:
@ -144,40 +144,31 @@ export class CustomService {
|
||||
|
||||
// 构造 WS URL
|
||||
const base = (api.defaults.baseURL || '') as string; // 可能是 /api 或 http(s)://host/api
|
||||
function toWsUrl(httpUrl: string) {
|
||||
if (httpUrl.startsWith('https://')) return 'wss://' + httpUrl.slice('https://'.length);
|
||||
if (httpUrl.startsWith('http://')) return 'ws://' + httpUrl.slice('http://'.length);
|
||||
// 相对路径:拼 window.location
|
||||
const origin = window.location.origin; // http(s)://host:port
|
||||
const full = origin.replace(/^http/, 'ws') + (httpUrl.startsWith('/') ? httpUrl : '/' + httpUrl);
|
||||
return full;
|
||||
}
|
||||
const path = `/flows/${id}/run/ws`;
|
||||
// 取 token 放到查询参数(WS 握手无法自定义 Authorization 头部)
|
||||
const token = getToken();
|
||||
|
||||
// 新增:WS 使用独立端口,默认 8855,可通过 VITE_WS_PORT 覆盖
|
||||
const wsPort = (import.meta as any).env?.VITE_WS_PORT || '8855';
|
||||
let wsBase: string;
|
||||
// 解析出 API 的路径前缀(用于生产环境相对路径),默认 /api
|
||||
let apiPathPrefix = '/api';
|
||||
if (base.startsWith('http://') || base.startsWith('https://')) {
|
||||
try {
|
||||
const u = new URL(base);
|
||||
const proto = u.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
u.protocol = proto;
|
||||
u.port = wsPort; // 改为 WS 端口
|
||||
wsBase = `${u.protocol}//${u.host}${u.pathname.replace(/\/$/, '')}`;
|
||||
} catch {
|
||||
const loc = window.location;
|
||||
const proto = loc.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
wsBase = `${proto}//${loc.hostname}:${wsPort}${base.startsWith('/') ? base : '/' + base}`;
|
||||
}
|
||||
} else {
|
||||
const loc = window.location;
|
||||
const proto = loc.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
wsBase = `${proto}//${loc.hostname}:${wsPort}${base.startsWith('/') ? base : '/' + base}`;
|
||||
apiPathPrefix = (u.pathname.replace(/\/$/, '') || '/api');
|
||||
} catch {}
|
||||
} else if (base.startsWith('/')) {
|
||||
apiPathPrefix = (base.replace(/\/$/, '') || '/api');
|
||||
}
|
||||
|
||||
const wsUrl = wsBase + path + (token ? (wsBase.includes('?') ? `&access_token=${encodeURIComponent(token)}` : `?access_token=${encodeURIComponent(token)}`) : '');
|
||||
const path = `/flows/${id}/run/ws`;
|
||||
// 取 token 放到查询参数(WS 握手无法自定义 Authorization 头部)
|
||||
const token = getToken();
|
||||
const isDev = !!((import.meta as any).env?.DEV);
|
||||
// 开发:走 /ws 前缀(由 Vite 代理到 8855 并 rewrite 到 /api)
|
||||
// 生产:走同域 /api 前缀,由 Nginx/网关反代,不显式暴露端口
|
||||
const prefix = isDev ? '/ws' : apiPathPrefix;
|
||||
const qs = token ? `?access_token=${encodeURIComponent(token)}` : '';
|
||||
|
||||
// 始终构造绝对 WS URL,避免浏览器兼容性问题
|
||||
const loc = window.location;
|
||||
const proto = loc.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const origin = `${proto}//${loc.host}`; // host 里已包含端口(开发时 5173),线上通常无端口
|
||||
const wsUrl = `${origin}${prefix}${path}${qs}`;
|
||||
|
||||
let ws: WebSocket | null = null;
|
||||
let resolveDone: (v: RunResult | null) => void;
|
||||
@ -253,6 +244,13 @@ export class CustomService {
|
||||
return { cancel: () => {}, done: Promise.resolve<RunResult | null>(null) } as const;
|
||||
}
|
||||
|
||||
// 在开发环境通过 Vite 代理前缀 /sse 转发到 8866(vite.config.ts 已配置 rewrite 到 /api)
|
||||
const useSseProxy = !!((import.meta as any).env?.DEV);
|
||||
let url: string;
|
||||
|
||||
if (useSseProxy) {
|
||||
url = `/sse/flows/${id}/run/stream`;
|
||||
} else {
|
||||
const base = (api.defaults.baseURL || '') as string;
|
||||
// 参照 WS:SSE 使用独立端口,默认 8866,可通过 VITE_SSE_PORT 覆盖
|
||||
const ssePort = (import.meta as any).env?.VITE_SSE_PORT || '8866';
|
||||
@ -271,8 +269,8 @@ export class CustomService {
|
||||
const loc = window.location;
|
||||
sseBase = `${loc.protocol}//${loc.hostname}:${ssePort}${base.startsWith('/') ? base : '/' + base}`;
|
||||
}
|
||||
|
||||
const url = sseBase + `/flows/${id}/run/stream`;
|
||||
url = sseBase + `/flows/${id}/run/stream`;
|
||||
}
|
||||
|
||||
const { cancel, done } = postSSE<RunResult | null>(url, { input }, {
|
||||
onMessage: (json: any) => {
|
||||
|
||||
Reference in New Issue
Block a user