101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import axios from 'axios';
|
|
|
|
const BOT_PASSWORD_HEADER = 'X-Bot-Password';
|
|
|
|
let initialized = false;
|
|
const memoryMap = new Map<string, string>();
|
|
|
|
function normalizeBotId(raw: string): string {
|
|
return String(raw || '').trim();
|
|
}
|
|
|
|
function resolveAbsoluteUrl(input: string): string {
|
|
const url = String(input || '').trim();
|
|
if (!url) return '';
|
|
try {
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
return new URL(url).pathname;
|
|
}
|
|
if (typeof window !== 'undefined') {
|
|
return new URL(url, window.location.origin).pathname;
|
|
}
|
|
return url;
|
|
} catch {
|
|
return url;
|
|
}
|
|
}
|
|
|
|
export function extractBotIdFromApiPath(rawPath: string): string | null {
|
|
const path = resolveAbsoluteUrl(rawPath);
|
|
if (!path) return null;
|
|
const match = path.match(/\/api\/bots\/([^/?#]+)/i);
|
|
if (!match?.[1]) return null;
|
|
try {
|
|
return decodeURIComponent(match[1]).trim() || null;
|
|
} catch {
|
|
return String(match[1]).trim() || null;
|
|
}
|
|
}
|
|
|
|
export function getBotAccessPassword(botId: string): string {
|
|
const key = normalizeBotId(botId);
|
|
if (!key) return '';
|
|
return memoryMap.get(key) || '';
|
|
}
|
|
|
|
export function setBotAccessPassword(botId: string, password: string): void {
|
|
const key = normalizeBotId(botId);
|
|
const value = String(password || '').trim();
|
|
if (!key || !value) return;
|
|
memoryMap.set(key, value);
|
|
}
|
|
|
|
export function clearBotAccessPassword(botId: string): void {
|
|
const key = normalizeBotId(botId);
|
|
if (!key) return;
|
|
memoryMap.delete(key);
|
|
}
|
|
|
|
export function clearAllBotAccessPasswords(): void {
|
|
if (memoryMap.size === 0) return;
|
|
memoryMap.clear();
|
|
}
|
|
|
|
export function isBotUnauthorizedError(error: any, botId?: string): boolean {
|
|
if (!axios.isAxiosError(error)) return false;
|
|
if (Number(error.response?.status) !== 401) return false;
|
|
if (!botId) return true;
|
|
|
|
const fromConfig = extractBotIdFromApiPath(String(error.config?.url || ''));
|
|
const fromRequest = extractBotIdFromApiPath(String(error.request?.responseURL || ''));
|
|
const expected = normalizeBotId(botId);
|
|
return expected === fromConfig || expected === fromRequest;
|
|
}
|
|
|
|
export function buildMonitorWsUrl(base: string, botId: string): string {
|
|
const target = `${String(base || '').replace(/\/$/, '')}/${encodeURIComponent(botId)}`;
|
|
const password = getBotAccessPassword(botId);
|
|
if (!password) return target;
|
|
const joiner = target.includes('?') ? '&' : '?';
|
|
return `${target}${joiner}access_password=${encodeURIComponent(password)}`;
|
|
}
|
|
|
|
export function setupBotAccessAuth(): void {
|
|
if (initialized) return;
|
|
initialized = true;
|
|
|
|
axios.interceptors.request.use((config) => {
|
|
const botId = extractBotIdFromApiPath(String(config.url || ''));
|
|
if (!botId) return config;
|
|
const password = getBotAccessPassword(botId);
|
|
if (!password) return config;
|
|
|
|
const headers = config.headers || {};
|
|
if (!(BOT_PASSWORD_HEADER in (headers as Record<string, unknown>))) {
|
|
(headers as Record<string, string>)[BOT_PASSWORD_HEADER] = password;
|
|
config.headers = headers;
|
|
}
|
|
return config;
|
|
});
|
|
}
|