n8n-nodes-agent-chat-interface
Version:
N8N custom node that serves a React chat interface as static content
97 lines (96 loc) • 5.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RuntimeThemeInjector = void 0;
class RuntimeThemeInjector {
static injectWebhookUrl(content, webhookUrl, fileExtension) {
if (!webhookUrl || webhookUrl === '') {
return content;
}
const cleanWebhookUrl = webhookUrl.endsWith('/') ? webhookUrl.slice(0, -1) : webhookUrl;
const cacheBuster = Date.now();
switch (fileExtension) {
case '.html':
return this.injectWebhookUrlIntoHtml(content, cleanWebhookUrl, cacheBuster);
case '.css':
return this.injectWebhookUrlIntoCss(content, cleanWebhookUrl, cacheBuster);
case '.js':
return this.injectWebhookUrlIntoJs(content, cleanWebhookUrl, cacheBuster);
default:
return content;
}
}
static injectWebhookUrlIntoHtml(htmlContent, webhookUrl, cacheBuster) {
const placeholder = '/__N8N_WEBHOOK_PLACEHOLDER__/';
const cleanWebhookUrl = webhookUrl.endsWith('/') ? webhookUrl.slice(0, -1) : webhookUrl;
return htmlContent.split(placeholder).join(`${cleanWebhookUrl}?cb=${cacheBuster}&path=`);
}
static injectWebhookUrlIntoCss(cssContent, webhookUrl, cacheBuster) {
const placeholder = '/__N8N_WEBHOOK_PLACEHOLDER__/';
const cleanWebhookUrl = webhookUrl.endsWith('/') ? webhookUrl.slice(0, -1) : webhookUrl;
return cssContent.split(placeholder).join(`${cleanWebhookUrl}?cb=${cacheBuster}&path=`);
}
static injectWebhookUrlIntoJs(jsContent, webhookUrl, cacheBuster) {
const placeholder = '/__N8N_WEBHOOK_PLACEHOLDER__/';
const cleanWebhookUrl = webhookUrl.endsWith('/') ? webhookUrl.slice(0, -1) : webhookUrl;
return jsContent.split(placeholder).join(`${cleanWebhookUrl}?cb=${cacheBuster}&path=`);
}
static injectThemeIntoHtml(htmlContent, themeConfig) {
const cleanConfig = Object.fromEntries(Object.entries(themeConfig).filter(([_, value]) => value !== undefined && value !== ''));
// Base64 encode footnote for safe HTML injection
if (cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE) {
cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE = Buffer.from(cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE, 'utf8').toString('base64');
}
if (cleanConfig.VITE_WELCOME_MESSAGE) {
cleanConfig.VITE_WELCOME_MESSAGE = Buffer.from(cleanConfig.VITE_WELCOME_MESSAGE, 'utf8').toString('base64');
}
const themeScript = `
<script>
window.__CHAT_THEME_CONFIG__ = ${JSON.stringify(cleanConfig)};
</script>`;
return htmlContent.replace('</head>', `${themeScript}\n</head>`);
}
static injectApiConfigIntoHtml(htmlContent, apiUrl, title) {
const apiConfig = {
VITE_CHAT_API_URL: apiUrl,
VITE_CHAT_TITLE: title
};
const configScript = `
<script>
window.__CHAT_THEME_CONFIG__ = Object.assign(window.__CHAT_THEME_CONFIG__ || {}, ${JSON.stringify(apiConfig)});
</script>`;
return htmlContent.replace('</head>', `${configScript}\n</head>`);
}
static injectFullConfig(htmlContent, fullConfig) {
const cleanConfig = Object.fromEntries(Object.entries(fullConfig).filter(([_, value]) => value !== undefined && value !== ''));
// Base64 encode footnote for safe HTML injection
if (cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE) {
cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE = Buffer.from(cleanConfig.VITE_AGENT_MESSAGE_FOOTNOTE, 'utf8').toString('base64');
}
if (cleanConfig.VITE_WELCOME_MESSAGE) {
cleanConfig.VITE_WELCOME_MESSAGE = Buffer.from(cleanConfig.VITE_WELCOME_MESSAGE, 'utf8').toString('base64');
}
// Break n8n internal caching
const timestamp = Date.now();
const randomValue = Math.random().toString(36).substring(2, 15);
cleanConfig.__N8N_CACHE_BUSTER__ = `${timestamp}-${randomValue}`;
const configScript = `
<script>
window.__CHAT_THEME_CONFIG__ = ${JSON.stringify(cleanConfig)};
window.__CONFIG_LOADED_AT__ = ${timestamp};
console.log('Config loaded at:', new Date(${timestamp}));
</script>`;
let modifiedHtml = htmlContent.replace('</head>', `${configScript}\n</head>`);
const chatTitle = fullConfig.VITE_CHAT_TITLE;
if (chatTitle) {
modifiedHtml = modifiedHtml.replace(/<title>.*?<\/title>/, `<title>${chatTitle}</title>`);
}
const customFavicon = fullConfig.VITE_CUSTOM_FAVICON;
if (customFavicon && customFavicon.trim() !== '') {
modifiedHtml = modifiedHtml.replace(/<link[^>]*rel=['"](icon|shortcut icon)['"'][^>]*>/gi, '');
const faviconLink = `<link rel="icon" type="image/png" href="${customFavicon}">`;
modifiedHtml = modifiedHtml.replace('</head>', ` ${faviconLink}\n</head>`);
}
return modifiedHtml;
}
}
exports.RuntimeThemeInjector = RuntimeThemeInjector;