Compare commits

..

No commits in common. "c5d1880589e7deccb64697edd8902057ee4ab064" and "c7edf8c66c5630a50b6cf8eef66296b23a8eeb3c" have entirely different histories.

View File

@ -52,19 +52,13 @@ export function createTransport(opts: TransportOpts): Transport {
function connectWs() { function connectWs() {
if (closed) return; if (closed) return;
// The browser WebSocket constructor does NOT accept custom headers, so const wsUrl =
// we encode credentials into the Sec-WebSocket-Protocol list (the opts.serverUrl.replace(/^http/, 'ws') +
// standard workaround). The bot reads `req.headers['sec-websocket- `/webchat/ws?botId=${encodeURIComponent(opts.botId)}` +
// protocol']` and parses these tokens. See bot's `webchat/subprotocol.ts`. `&apiKey=${encodeURIComponent(opts.apiKey)}` +
const wsUrl = opts.serverUrl.replace(/^http/, 'ws') + '/webchat/ws'; `&visitorId=${encodeURIComponent(opts.visitorId)}`;
const subprotocols = [
'messenzy.v1',
`messenzy-bot.${opts.botId}`,
`messenzy-visitor.${opts.visitorId}`,
`messenzy-key.${opts.apiKey}`,
];
try { try {
ws = new WebSocket(wsUrl, subprotocols); ws = new WebSocket(wsUrl);
} catch { } catch {
onWsClose(); onWsClose();
return; return;
@ -124,19 +118,14 @@ export function createTransport(opts: TransportOpts): Transport {
async function startPolling() { async function startPolling() {
pollTimer = setInterval(async () => { pollTimer = setInterval(async () => {
if (closed) return; if (closed) return;
// HTTP fallback uses Authorization: Bearer (browser fetch DOES allow
// custom headers, unlike the WebSocket constructor). botId/visitorId
// are public identifiers and stay in the query string.
const url = const url =
`${opts.serverUrl}/webchat/history?` + `${opts.serverUrl}/webchat/history?` +
`botId=${encodeURIComponent(opts.botId)}` + `botId=${encodeURIComponent(opts.botId)}` +
`&apiKey=${encodeURIComponent(opts.apiKey)}` +
`&visitorId=${encodeURIComponent(opts.visitorId)}` + `&visitorId=${encodeURIComponent(opts.visitorId)}` +
(lastSeenAt ? `&since=${encodeURIComponent(lastSeenAt)}` : ''); (lastSeenAt ? `&since=${encodeURIComponent(lastSeenAt)}` : '');
try { try {
const res = await fetch(url, { const res = await fetch(url, { credentials: 'omit' });
credentials: 'omit',
headers: { authorization: `Bearer ${opts.apiKey}` },
});
if (!res.ok) return; if (!res.ok) return;
const data = (await res.json()) as { const data = (await res.json()) as {
messages?: Array<{ id: string; text: string; at: string }>; messages?: Array<{ id: string; text: string; at: string }>;
@ -164,12 +153,10 @@ export function createTransport(opts: TransportOpts): Transport {
try { try {
await fetch(`${opts.serverUrl}/webchat/msg`, { await fetch(`${opts.serverUrl}/webchat/msg`, {
method: 'POST', method: 'POST',
headers: { headers: { 'content-type': 'application/json' },
'content-type': 'application/json',
authorization: `Bearer ${opts.apiKey}`,
},
body: JSON.stringify({ body: JSON.stringify({
botId: opts.botId, botId: opts.botId,
apiKey: opts.apiKey,
visitorId: opts.visitorId, visitorId: opts.visitorId,
text: msg.text, text: msg.text,
idempotency_key: msg.id, idempotency_key: msg.id,