import moment from 'moment'; import { ansiToHtml } from 'anser'; // https://github.com/janl/mustache.js/blob/master/mustache.js#L60 const entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''', '/': '/', '`': '`', '=': '=' }; function escapeHtml(string) { return String(string).replace(/[&<>"'`=/]/g, function fromEntityMap (s) { return entityMap[s]; }); } function ab2str(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf)); } export function create(origin, accessToken, type, id) { const INITIAL_STREAM_LINES = 100; let eventSource = null; let streamApi = ''; let downloadApi = ''; if (type === 'platform') { streamApi = '/api/v1/system/logstream/box'; downloadApi = '/api/v1/system/logs/box'; } else if (type === 'crash') { streamApi = `/api/v1/system/logstream/crash-${id}`; downloadApi = `/api/v1/system/logs/crash-${id}`; } else if (type === 'app') { streamApi = `/api/v1/apps/${id}/logstream`; downloadApi = `/api/v1/apps/${id}/logs`; } else if (type === 'service') { streamApi = `/api/v1/services/${id}/logstream`; downloadApi = `/api/v1/services/${id}/logs`; } else if (type === 'task') { streamApi = `/api/v1/tasks/${id}/logstream`; downloadApi = `/api/v1/tasks/${id}/logs`; } else { console.error('unsupported logs type', type); } return { name: 'LogsModel', stream(lineHandler, errorHandler) { eventSource = new EventSource(`${origin}${streamApi}?lines=${INITIAL_STREAM_LINES}&access_token=${accessToken}`); eventSource._lastMessage = null; eventSource.onerror = function ( /* uselessError */) { if (eventSource.readyState === EventSource.CLOSED) { // eventSource does not give us the HTTP error code. We have to resort to message count check and guess the reason const msg = eventSource._lastMessage === null ? `Logs unavailable. Maybe the logs were logrotated.` : `Connection closed.`; const e = new Error(msg); e.time = moment().format('MMM DD HH:mm:ss'); e.html = ansiToHtml(e.message); errorHandler(e); } }; // eventSource.onopen = function () { console.log('stream is open'); }; eventSource.onmessage = function (message) { var data; try { data = JSON.parse(message.data); } catch (e) { return console.error(e); } const time = data.realtimeTimestamp ? moment(data.realtimeTimestamp/1000).format('MMM DD HH:mm:ss') : ''; const html = ansiToHtml(escapeHtml(typeof data.message === 'string' ? data.message : ab2str(data.message))); eventSource._lastMessage = { time, html }; lineHandler(time, html); }; }, getDownloadUrl() { return `${origin}${downloadApi}?access_token=${accessToken}&format=short&lines=-1`; } }; } export default { create };