import moment from 'moment'; import superagent from 'superagent'; import { ansiToHtml } from 'anser'; import { ISTATES } from '../constants.js'; import { sleep } from 'pankow/utils'; // 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 = 1000; 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.onerror = errorHandler; // 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))); lineHandler(time, html); }; }, getDownloadUrl() { return `${origin}${downloadApi}?access_token=${accessToken}&format=short&lines=-1`; }, // TODO maybe move this into AppsModel.js async getApp() { let error, result; try { result = await superagent.get(`${origin}/api/v1/apps/${id}`).query({ access_token: accessToken }); } catch (e) { error = e; } if (error || result.statusCode !== 200) { console.error(`Invalid app ${id}`, error || result.statusCode); this.fatalError = `Invalid app ${id}`; return; } return result.body; }, async restartApp() { if (type !== 'app') return; let error, result; try { result = await superagent.post(`${origin}/api/v1/apps/${id}/restart`).query({ access_token: accessToken }); } catch (e) { error = e; } if (error || result.statusCode !== 202) { console.error(`Failed to restart app ${this.id}`, error || result.statusCode); return; } while(true) { let result; try { result = await superagent.get(`${origin}/api/v1/apps/${id}`).query({ access_token: accessToken }); } catch (e) { console.error(e); } if (result && result.statusCode === 200 && result.body.installationState === ISTATES.INSTALLED) break; await sleep(2000); } } }; } export default { create };