Feature: ChatBot integration
This commit is contained in:
@@ -1104,6 +1104,7 @@
|
||||
<script src="/js/pages/SeriesPage.js?v=1"></script>
|
||||
<script src="/js/pages/Settings.js?v=3"></script>
|
||||
<script src="/js/pages/WatchPage.js?v=3"></script>
|
||||
<script src="/js/components/ChatBot.js?v=2"></script>
|
||||
<script src="/js/app.js?v=5"></script>
|
||||
<script>
|
||||
// Check for SSO token in URL
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
/**
|
||||
* KiraBot — floating chat widget for KiraTV
|
||||
*/
|
||||
class ChatBot {
|
||||
constructor() {
|
||||
this._open = false;
|
||||
this._tab = 'chat'; // 'chat' | 'recordings'
|
||||
this._messages = [];
|
||||
this._recordings = [];
|
||||
this._loading = false;
|
||||
this._token = null;
|
||||
this._init();
|
||||
}
|
||||
|
||||
_init() {
|
||||
// Inject styles
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
#kirabot-btn {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
z-index: 9000;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 20px rgba(124,58,237,.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform .2s, box-shadow .2s;
|
||||
}
|
||||
#kirabot-btn:hover { transform: scale(1.1); box-shadow: 0 6px 28px rgba(124,58,237,.7); }
|
||||
#kirabot-btn svg { width: 26px; height: 26px; fill: #fff; }
|
||||
#kirabot-badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
border-radius: 9999px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
#kirabot-panel {
|
||||
position: fixed;
|
||||
bottom: 90px;
|
||||
right: 24px;
|
||||
z-index: 9001;
|
||||
width: 380px;
|
||||
max-width: calc(100vw - 32px);
|
||||
height: 560px;
|
||||
max-height: calc(100vh - 110px);
|
||||
background: #1a1a2e;
|
||||
border: 1px solid rgba(124,58,237,.4);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 16px 48px rgba(0,0,0,.6);
|
||||
transition: opacity .25s, transform .25s;
|
||||
overflow: hidden;
|
||||
}
|
||||
#kirabot-panel.hidden { opacity: 0; transform: translateY(12px) scale(.97); pointer-events: none; }
|
||||
.kb-header {
|
||||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.kb-header-title { font-weight: 700; font-size: 15px; color: #fff; flex: 1; }
|
||||
.kb-header-sub { font-size: 11px; color: rgba(255,255,255,.7); }
|
||||
.kb-close-btn {
|
||||
background: rgba(255,255,255,.15);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 28px; height: 28px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.kb-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid rgba(255,255,255,.08);
|
||||
background: #16162a;
|
||||
}
|
||||
.kb-tab {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,.5);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: color .2s;
|
||||
}
|
||||
.kb-tab.active { color: #a78bfa; border-bottom: 2px solid #7c3aed; font-weight: 600; }
|
||||
.kb-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
.kb-messages::-webkit-scrollbar { width: 4px; }
|
||||
.kb-messages::-webkit-scrollbar-track { background: transparent; }
|
||||
.kb-messages::-webkit-scrollbar-thumb { background: rgba(255,255,255,.15); border-radius: 2px; }
|
||||
.kb-bubble {
|
||||
max-width: 88%;
|
||||
padding: 10px 13px;
|
||||
border-radius: 14px;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.kb-bubble.user {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||||
color: #fff;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.kb-bubble.bot {
|
||||
align-self: flex-start;
|
||||
background: rgba(255,255,255,.07);
|
||||
color: #e2e8f0;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.kb-bubble.error { background: rgba(239,68,68,.15); color: #fca5a5; border: 1px solid rgba(239,68,68,.3); }
|
||||
.kb-typing { align-self: flex-start; color: rgba(255,255,255,.4); font-size: 13px; font-style: italic; }
|
||||
.kb-input-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid rgba(255,255,255,.08);
|
||||
background: #16162a;
|
||||
}
|
||||
.kb-input {
|
||||
flex: 1;
|
||||
background: rgba(255,255,255,.07);
|
||||
border: 1px solid rgba(255,255,255,.12);
|
||||
border-radius: 12px;
|
||||
color: #e2e8f0;
|
||||
font-size: 13.5px;
|
||||
padding: 8px 12px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
.kb-input:focus { border-color: rgba(124,58,237,.6); }
|
||||
.kb-send-btn {
|
||||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.kb-send-btn:disabled { opacity: .4; cursor: default; }
|
||||
.kb-send-btn svg { width: 18px; height: 18px; fill: #fff; }
|
||||
.kb-recordings {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.kb-rec-item {
|
||||
background: rgba(255,255,255,.05);
|
||||
border: 1px solid rgba(255,255,255,.08);
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.kb-rec-title { font-weight: 600; font-size: 13px; color: #e2e8f0; }
|
||||
.kb-rec-meta { font-size: 11px; color: rgba(255,255,255,.45); margin-top: 3px; }
|
||||
.kb-rec-status { font-size: 11px; font-weight: 600; margin-top: 4px; }
|
||||
.kb-rec-status.scheduled { color: #fbbf24; }
|
||||
.kb-rec-status.recording { color: #f87171; }
|
||||
.kb-rec-status.completed { color: #34d399; }
|
||||
.kb-rec-status.failed { color: #f87171; }
|
||||
.kb-rec-status.cancelled { color: rgba(255,255,255,.3); }
|
||||
.kb-empty { text-align: center; color: rgba(255,255,255,.35); font-size: 13px; padding: 40px 0; }
|
||||
.kb-bot-avatar { width: 22px; height: 22px; flex-shrink: 0; }
|
||||
#kb-chat-view { display: flex; flex-direction: column; flex: 1; overflow: hidden; min-height: 0; }
|
||||
#kb-rec-view { display: none; flex-direction: column; flex: 1; overflow: hidden; min-height: 0; }
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Floating button
|
||||
const btn = document.createElement('button');
|
||||
btn.id = 'kirabot-btn';
|
||||
btn.title = 'KiraBot — Asistente de TV';
|
||||
btn.innerHTML = `
|
||||
<svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM9 11H7V9h2v2zm4 0h-2V9h2v2zm4 0h-2V9h2v2z"/></svg>
|
||||
<span id="kirabot-badge"></span>
|
||||
`;
|
||||
btn.addEventListener('click', () => this.toggle());
|
||||
document.body.appendChild(btn);
|
||||
|
||||
// Chat panel
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'kirabot-panel';
|
||||
panel.className = 'hidden';
|
||||
panel.innerHTML = `
|
||||
<div class="kb-header">
|
||||
<svg class="kb-bot-avatar" viewBox="0 0 24 24" fill="white"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></svg>
|
||||
<div>
|
||||
<div class="kb-header-title">KiraBot</div>
|
||||
<div class="kb-header-sub">Asistente de TV · IA</div>
|
||||
</div>
|
||||
<button class="kb-close-btn" id="kb-close">✕</button>
|
||||
</div>
|
||||
<div class="kb-tabs">
|
||||
<button class="kb-tab active" id="kb-tab-chat">💬 Chat</button>
|
||||
<button class="kb-tab" id="kb-tab-rec">📹 Grabaciones</button>
|
||||
</div>
|
||||
<div id="kb-chat-view">
|
||||
<div class="kb-messages" id="kb-messages"></div>
|
||||
<div class="kb-input-row">
|
||||
<textarea class="kb-input" id="kb-input" rows="1"
|
||||
placeholder="Pregunta por la programación, pide grabaciones..."></textarea>
|
||||
<button class="kb-send-btn" id="kb-send" title="Enviar">
|
||||
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kb-rec-view" style="display:none;flex:1;overflow:hidden;flex-direction:column;">
|
||||
<div class="kb-recordings" id="kb-recordings"></div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(panel);
|
||||
|
||||
// Events
|
||||
document.getElementById('kb-close').addEventListener('click', () => this.close());
|
||||
document.getElementById('kb-tab-chat').addEventListener('click', () => this._switchTab('chat'));
|
||||
document.getElementById('kb-tab-rec').addEventListener('click', () => this._switchTab('recordings'));
|
||||
document.getElementById('kb-send').addEventListener('click', () => this._sendMessage());
|
||||
|
||||
// Stop all keyboard and mouse events from bubbling to NodeCast's global handlers
|
||||
// (NodeCast intercepts spacebar for play/pause and other keys for media control)
|
||||
const inputEl = document.getElementById('kb-input');
|
||||
inputEl.addEventListener('keydown', (e) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this._sendMessage(); }
|
||||
});
|
||||
inputEl.addEventListener('keypress', (e) => e.stopPropagation());
|
||||
inputEl.addEventListener('keyup', (e) => e.stopPropagation());
|
||||
|
||||
panel.addEventListener('click', (e) => e.stopPropagation());
|
||||
panel.addEventListener('mousedown', (e) => e.stopPropagation());
|
||||
|
||||
// Welcome message
|
||||
this._appendBotMessage('¡Hola! Soy KiraBot 👋 Puedo ayudarte con la programación de TV y programar grabaciones. ¿En qué te puedo ayudar?');
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this._open ? this.close() : this.open();
|
||||
}
|
||||
|
||||
open() {
|
||||
this._open = true;
|
||||
document.getElementById('kirabot-panel').classList.remove('hidden');
|
||||
document.getElementById('kb-input').focus();
|
||||
this._scrollToBottom();
|
||||
}
|
||||
|
||||
close() {
|
||||
this._open = false;
|
||||
document.getElementById('kirabot-panel').classList.add('hidden');
|
||||
}
|
||||
|
||||
_switchTab(tab) {
|
||||
this._tab = tab;
|
||||
document.getElementById('kb-tab-chat').classList.toggle('active', tab === 'chat');
|
||||
document.getElementById('kb-tab-rec').classList.toggle('active', tab === 'recordings');
|
||||
document.getElementById('kb-chat-view').style.display = tab === 'chat' ? 'flex' : 'none';
|
||||
document.getElementById('kb-rec-view').style.display = tab === 'recordings' ? 'flex' : 'none';
|
||||
if (tab === 'recordings') this._loadRecordings();
|
||||
}
|
||||
|
||||
_appendBotMessage(text, isError = false) {
|
||||
const msgs = document.getElementById('kb-messages');
|
||||
const div = document.createElement('div');
|
||||
div.className = `kb-bubble bot${isError ? ' error' : ''}`;
|
||||
div.textContent = text;
|
||||
msgs.appendChild(div);
|
||||
this._scrollToBottom();
|
||||
return div;
|
||||
}
|
||||
|
||||
_appendUserMessage(text) {
|
||||
const msgs = document.getElementById('kb-messages');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'kb-bubble user';
|
||||
div.textContent = text;
|
||||
msgs.appendChild(div);
|
||||
this._scrollToBottom();
|
||||
}
|
||||
|
||||
_showTyping() {
|
||||
const msgs = document.getElementById('kb-messages');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'kb-typing';
|
||||
div.id = 'kb-typing';
|
||||
div.textContent = 'KiraBot está escribiendo...';
|
||||
msgs.appendChild(div);
|
||||
this._scrollToBottom();
|
||||
}
|
||||
|
||||
_removeTyping() {
|
||||
document.getElementById('kb-typing')?.remove();
|
||||
}
|
||||
|
||||
_scrollToBottom() {
|
||||
const msgs = document.getElementById('kb-messages');
|
||||
if (msgs) setTimeout(() => { msgs.scrollTop = msgs.scrollHeight; }, 10);
|
||||
}
|
||||
|
||||
async _sendMessage() {
|
||||
const input = document.getElementById('kb-input');
|
||||
const text = input.value.trim();
|
||||
if (!text || this._loading) return;
|
||||
|
||||
input.value = '';
|
||||
input.style.height = 'auto';
|
||||
this._appendUserMessage(text);
|
||||
this._loading = true;
|
||||
document.getElementById('kb-send').disabled = true;
|
||||
this._showTyping();
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/kirabot/chat', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`,
|
||||
},
|
||||
body: JSON.stringify({ message: text }),
|
||||
});
|
||||
const data = await resp.json();
|
||||
this._removeTyping();
|
||||
if (resp.ok) {
|
||||
this._appendBotMessage(data.response || 'Sin respuesta');
|
||||
} else {
|
||||
this._appendBotMessage(data.error || 'Error al conectar con KiraBot', true);
|
||||
}
|
||||
} catch (err) {
|
||||
this._removeTyping();
|
||||
this._appendBotMessage('No se pudo conectar con KiraBot. ¿Está el servidor activo?', true);
|
||||
} finally {
|
||||
this._loading = false;
|
||||
document.getElementById('kb-send').disabled = false;
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
|
||||
async _loadRecordings() {
|
||||
const container = document.getElementById('kb-recordings');
|
||||
container.innerHTML = '<div class="kb-empty">Cargando...</div>';
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/kirabot/recordings?limit=20', {
|
||||
headers: { 'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}` },
|
||||
});
|
||||
const recs = await resp.json();
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!Array.isArray(recs) || recs.length === 0) {
|
||||
container.innerHTML = '<div class="kb-empty">No tienes grabaciones todavía.<br>¡Pídele a KiraBot que grabe algo!</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const statusLabels = {
|
||||
scheduled: ['⏳', 'scheduled', 'Programada'],
|
||||
recording: ['🔴', 'recording', 'Grabando'],
|
||||
completed: ['✓', 'completed', 'Completada'],
|
||||
failed: ['✗', 'failed', 'Fallida'],
|
||||
cancelled: ['✗', 'cancelled', 'Cancelada'],
|
||||
};
|
||||
|
||||
recs.forEach((r) => {
|
||||
const [icon, cls, label] = statusLabels[r.status] || ['?', '', r.status];
|
||||
const start = r.start_time ? new Date(r.start_time).toLocaleString('es-ES', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
|
||||
const item = document.createElement('div');
|
||||
item.className = 'kb-rec-item';
|
||||
item.innerHTML = `
|
||||
<div class="kb-rec-title">${this._esc(r.title)}</div>
|
||||
<div class="kb-rec-meta">${this._esc(r.channel_name || '')}${start ? ' · ' + start : ''}</div>
|
||||
<div class="kb-rec-status ${cls}">${icon} ${label}</div>
|
||||
`;
|
||||
container.appendChild(item);
|
||||
});
|
||||
} catch (err) {
|
||||
container.innerHTML = '<div class="kb-empty" style="color:#f87171">Error al cargar las grabaciones.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
_esc(str) {
|
||||
return String(str || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when app is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Delay slightly to let auth check happen first
|
||||
setTimeout(() => {
|
||||
if (localStorage.getItem('authToken')) {
|
||||
window.kiraBot = new ChatBot();
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
@@ -187,6 +187,7 @@ app.use('/api/probe', require('./routes/probe'));
|
||||
app.use('/api/subtitle', require('./routes/subtitle'));
|
||||
app.use('/api/settings', require('./routes/settings'));
|
||||
app.use('/api/history', require('./routes/history'));
|
||||
app.use('/api/kirabot', require('./routes/kirabot'));
|
||||
|
||||
// Version endpoint
|
||||
app.get('/api/version', (req, res) => {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* KiraBot proxy — forwards /api/kirabot/* to KiraStream backend
|
||||
* authenticating on behalf of the current kiratv user.
|
||||
*/
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { requireAuth } = require('../auth');
|
||||
const { sources } = require('../db');
|
||||
const http = require('http');
|
||||
|
||||
const KIRASTREAM_BASE = process.env.KIRASTREAM_BASE_URL || 'http://10.10.10.234';
|
||||
|
||||
// Token cache: { userId: { token, expires } }
|
||||
const _tokenCache = new Map();
|
||||
|
||||
async function _getKirastreamToken(user) {
|
||||
const cached = _tokenCache.get(user.id);
|
||||
if (cached && cached.expires > Date.now()) {
|
||||
return cached.token;
|
||||
}
|
||||
|
||||
// Find user's xtream source pointing to kirastream
|
||||
let db;
|
||||
try {
|
||||
db = await require('../db').loadDb();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const src = db.sources.find(
|
||||
(s) => s.ownerId === user.id && s.type === 'xtream' && s.username && s.password
|
||||
);
|
||||
if (!src) return null;
|
||||
|
||||
// POST form to /api/user/auth/token
|
||||
return new Promise((resolve) => {
|
||||
const body = `username=${encodeURIComponent(src.username)}&password=${encodeURIComponent(src.password)}`;
|
||||
const url = new URL(`${KIRASTREAM_BASE}/api/user/auth/token`);
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || 80,
|
||||
path: url.pathname,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
},
|
||||
};
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => { data += chunk; });
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
if (json.access_token) {
|
||||
_tokenCache.set(user.id, {
|
||||
token: json.access_token,
|
||||
expires: Date.now() + 25 * 60 * 1000, // 25 min
|
||||
});
|
||||
resolve(json.access_token);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
} catch (e) {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', () => resolve(null));
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function _proxyToKirastream(req, res, path, method, body) {
|
||||
const token = await _getKirastreamToken(req.user);
|
||||
if (!token) {
|
||||
return res.status(401).json({ error: 'No se pudo autenticar con KiraStream' });
|
||||
}
|
||||
|
||||
const url = new URL(`${KIRASTREAM_BASE}/api/user/kirabot${path}`);
|
||||
const bodyStr = body ? JSON.stringify(body) : '';
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || 80,
|
||||
path: url.pathname + (url.search || ''),
|
||||
method: method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(bodyStr),
|
||||
},
|
||||
};
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const proxyReq = http.request(options, (proxyRes) => {
|
||||
let data = '';
|
||||
proxyRes.on('data', (chunk) => { data += chunk; });
|
||||
proxyRes.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
res.status(proxyRes.statusCode).json(json);
|
||||
} catch (e) {
|
||||
res.status(proxyRes.statusCode).send(data);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
proxyReq.on('error', (err) => {
|
||||
res.status(502).json({ error: 'Error de conexión con KiraStream', detail: err.message });
|
||||
resolve();
|
||||
});
|
||||
if (bodyStr) proxyReq.write(bodyStr);
|
||||
proxyReq.end();
|
||||
});
|
||||
}
|
||||
|
||||
router.use(requireAuth);
|
||||
|
||||
router.post('/chat', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, '/chat', 'POST', req.body);
|
||||
});
|
||||
|
||||
router.get('/chat/history', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, '/chat/history', 'GET', null);
|
||||
});
|
||||
|
||||
router.delete('/chat/history', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, '/chat/history', 'DELETE', null);
|
||||
});
|
||||
|
||||
router.get('/status', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, '/status', 'GET', null);
|
||||
});
|
||||
|
||||
router.get('/recordings', async (req, res) => {
|
||||
const qs = req.query.limit ? `?limit=${req.query.limit}` : '';
|
||||
await _proxyToKirastream(req, res, `/recordings${qs}`, 'GET', null);
|
||||
});
|
||||
|
||||
router.post('/recordings', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, '/recordings', 'POST', req.body);
|
||||
});
|
||||
|
||||
router.delete('/recordings/:id', async (req, res) => {
|
||||
await _proxyToKirastream(req, res, `/recordings/${req.params.id}`, 'DELETE', null);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user