Spaces:
Sleeping
Sleeping
File size: 13,586 Bytes
1cf0854 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var express_1 = require("express");
var database_1 = require("../db/database");
var router = express_1.default.Router();
// Get widget configuration
router.get('/config/:tenantId', function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var tenantId, tenant, config, defaultConfig, widgetConfig, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
tenantId = req.params.tenantId;
return [4 /*yield*/, database_1.database.get('SELECT id, name, plan FROM tenants WHERE id = ?', [tenantId])];
case 1:
tenant = _a.sent();
if (!tenant) {
return [2 /*return*/, res.status(404).json({ error: 'Tenant not found' })];
}
return [4 /*yield*/, database_1.database.get('SELECT config FROM widget_configs WHERE tenant_id = ?', [tenantId])];
case 2:
config = _a.sent();
defaultConfig = {
theme: {
primaryColor: '#6366f1',
backgroundColor: '#ffffff',
textColor: '#374151',
borderRadius: '12px'
},
position: {
bottom: '20px',
right: '20px'
},
size: 'medium',
welcome: {
title: "Hi! I'm ".concat(tenant.name, "'s AI assistant"),
message: 'How can I help you today?',
showAvatar: true
},
features: {
fileUpload: false,
typing: true,
ratings: true
}
};
widgetConfig = config ? __assign(__assign({}, defaultConfig), JSON.parse(config.config)) :
defaultConfig;
res.json({
tenantId: tenantId,
config: widgetConfig
});
return [3 /*break*/, 4];
case 3:
error_1 = _a.sent();
console.error('Get widget config error:', error_1);
res.status(500).json({ error: 'Internal server error' });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
// Get widget script
router.get('/script/:tenantId', function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var tenantId, tenant, script, error_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
tenantId = req.params.tenantId;
return [4 /*yield*/, database_1.database.get('SELECT id FROM tenants WHERE id = ?', [tenantId])];
case 1:
tenant = _a.sent();
if (!tenant) {
return [2 /*return*/, res.status(404).json({ error: 'Tenant not found' })];
}
script = "\n(function() {\n // MCP Chat Widget\n const TENANT_ID = '".concat(tenantId, "';\n const API_URL = '").concat(process.env.FRONTEND_URL || 'http://localhost:3001', "/api';\n \n // Create widget container\n const widget = document.createElement('div');\n widget.id = 'mcp-chat-widget';\n widget.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 350px;\n height: 500px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.1);\n z-index: 10000;\n display: none;\n flex-direction: column;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n border: 1px solid #e5e7eb;\n `;\n\n // Create chat button\n const button = document.createElement('div');\n button.id = 'mcp-chat-button';\n button.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 60px;\n height: 60px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n border-radius: 50%;\n cursor: pointer;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);\n transition: transform 0.2s ease;\n `;\n \n button.innerHTML = `\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n `;\n\n button.onmouseover = () => button.style.transform = 'scale(1.05)';\n button.onmouseout = () => button.style.transform = 'scale(1)';\n\n // Widget header\n const header = document.createElement('div');\n header.style.cssText = `\n padding: 16px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: white;\n border-radius: 12px 12px 0 0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n `;\n \n header.innerHTML = `\n <div>\n <h3 style=\"margin: 0; font-size: 16px; font-weight: 600;\">Chat Support</h3>\n <p style=\"margin: 4px 0 0 0; font-size: 12px; opacity: 0.9;\">We're here to help!</p>\n </div>\n <button id=\"mcp-close-btn\" style=\"background: none; border: none; color: white; font-size: 20px; cursor: pointer; padding: 4px;\">\u00D7</button>\n `;\n\n // Messages container\n const messages = document.createElement('div');\n messages.id = 'mcp-messages';\n messages.style.cssText = `\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n `;\n\n // Input container\n const inputContainer = document.createElement('div');\n inputContainer.style.cssText = `\n padding: 16px;\n border-top: 1px solid #e5e7eb;\n display: flex;\n gap: 8px;\n `;\n\n const input = document.createElement('input');\n input.type = 'text';\n input.placeholder = 'Type your message...';\n input.style.cssText = `\n flex: 1;\n padding: 12px;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n outline: none;\n font-size: 14px;\n `;\n\n const sendBtn = document.createElement('button');\n sendBtn.textContent = 'Send';\n sendBtn.style.cssText = `\n padding: 12px 16px;\n background: #6366f1;\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n `;\n\n // Assemble widget\n inputContainer.appendChild(input);\n inputContainer.appendChild(sendBtn);\n widget.appendChild(header);\n widget.appendChild(messages);\n widget.appendChild(inputContainer);\n\n // Add to page\n document.body.appendChild(button);\n document.body.appendChild(widget);\n\n // Session management\n let sessionToken = null;\n let isVisible = false;\n\n // Toggle widget\n function toggleWidget() {\n isVisible = !isVisible;\n widget.style.display = isVisible ? 'flex' : 'none';\n button.style.display = isVisible ? 'none' : 'flex';\n \n if (isVisible && !sessionToken) {\n initializeSession();\n }\n }\n\n // Initialize chat session\n async function initializeSession() {\n try {\n const response = await fetch(`${API_URL}/chat/sessions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n tenantId: TENANT_ID,\n domain: window.location.hostname,\n userAgent: navigator.userAgent\n })\n });\n \n const data = await response.json();\n sessionToken = data.sessionToken;\n \n // Add welcome message\n addMessage('ai', 'Hello! How can I help you today?');\n } catch (error) {\n console.error('Failed to initialize session:', error);\n addMessage('ai', 'Sorry, I\\'m having trouble connecting. Please try again later.');\n }\n }\n\n // Send message\n async function sendMessage(message) {\n if (!sessionToken || !message.trim()) return;\n\n addMessage('user', message);\n input.value = '';\n \n // Show typing indicator\n const typingEl = addMessage('ai', 'Typing...', true);\n \n try {\n const response = await fetch(`${API_URL}/chat/messages`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sessionToken,\n message,\n tenantId: TENANT_ID\n })\n });\n \n const data = await response.json();\n \n // Remove typing indicator\n typingEl.remove();\n \n // Add AI response\n addMessage('ai', data.response);\n \n } catch (error) {\n typingEl.remove();\n addMessage('ai', 'Sorry, I encountered an error. Please try again.');\n }\n }\n\n // Add message to chat\n function addMessage(sender, text, isTyping = false) {\n const messageEl = document.createElement('div');\n messageEl.style.cssText = `\n max-width: 80%;\n padding: 12px 16px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.4;\n ${sender === 'user' ? \n 'background: #6366f1; color: white; align-self: flex-end; margin-left: auto;' : \n 'background: #f3f4f6; color: #374151; align-self: flex-start;'\n }\n ${isTyping ? 'opacity: 0.7; font-style: italic;' : ''}\n `;\n \n messageEl.textContent = text;\n messages.appendChild(messageEl);\n messages.scrollTop = messages.scrollHeight;\n \n return messageEl;\n }\n\n // Event listeners\n button.onclick = toggleWidget;\n header.querySelector('#mcp-close-btn').onclick = toggleWidget;\n sendBtn.onclick = () => sendMessage(input.value);\n input.onkeypress = (e) => {\n if (e.key === 'Enter') sendMessage(input.value);\n };\n\n console.log('MCP Chat Widget loaded successfully');\n})();\n");
res.setHeader('Content-Type', 'application/javascript');
res.send(script);
return [3 /*break*/, 3];
case 2:
error_2 = _a.sent();
console.error('Get widget script error:', error_2);
res.status(500).json({ error: 'Internal server error' });
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
}); });
exports.default = router;
|