UNPKG

globular_admin

Version:
436 lines (367 loc) 16.3 kB
import { Panel } from "./panel"; // User interface section. import * as M from "materialize-css"; import "materialize-css/sass/materialize.scss"; import { getErrorMessage, getAllActions, eventHub, readLogs, clearAllLog, deleteLog, globular } from "./backend"; import { LogInfo, LogType } from "globular-web-client/lib/ressource/ressource_pb"; import { fireResize } from "./utility.js"; import "@davecourtois/elementui/components/table/table.js"; /** * Display log informations */ export class LogsPanel extends Panel { private editable: boolean; private ul: any; constructor(parent: any) { super("logs_panel") this.setParent(parent); } displayLogs(logs: Array<LogInfo>) { this.div.removeAllChilds(); this.ul = this.div.appendElement({ tag: "ul", class: "collapsible" }).down() let logs_ = new Map<string, Array<LogInfo>>(); for (var i = 0; i < logs.length; i++) { let header: any; let log = logs[i] if (!logs_.has(log.getMethod())) { logs_.set(log.getMethod(), new Array<LogInfo>()) let li: any if (log.getMethod().startsWith("/")) { li = this.ul.appendElement({ tag: "li" }).down() } else { // all service at top li = this.ul.prependElement({ tag: "li" }).down() } header = li.appendElement({ tag: "div", class: "collapsible-header", id: log.getMethod() + "_header" }).down() header.appendElement({ tag: "span", class: "col s12", innerHtml: log.getMethod() }).up() .appendElement({ tag: "div", class: "collapsible-body", id: log.getMethod() + "_body" }).down() if (this.editable) { header.element.firstChild.className = "col s11" let deleteBtn = header.appendElement({ tag: "i", class: "material-icons col s1", style: "text-align: right;", innerHtml: "delete" }).down() deleteBtn.element.onclick = (evt: any) => { evt.stopPropagation() // Here I will let logs = logs_.get(log.getMethod()) // I will get the list of logs. for (var i = 0; i < logs.length; i++) { deleteLog(logs[i], () => { /** nothing to do here. */ }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }) } li.delete() } } } logs_.get(log.getMethod()).push(log) this.displayLog(log); } M.Collapsible.init(this.ul.element) } displayLog(info: LogInfo) { let body = this.ul.getChildById(info.getMethod() + "_body") if (body != undefined) { let msg = info.getMessage().replace(new RegExp('\r?\n', 'g'), '<br />') if(msg.length == 0){ return } let row = body.appendElement({ tag: "div", class: "row ", style: "margin: 0px; border-bottom: solid 1px rgba(51,51,51,0.12);" }).down(); let p: any; if (info.getMethod().startsWith("/")) { let time = new Date(info.getDate() * 1000).toDateString() + " " + new Date(info.getDate() * 1000).toLocaleTimeString() p = row.appendElement({ tag: "span", class: "col s3", innerHtml: time }) .appendElement({ tag: "p", class: "col s9", style: "overflow-y: scroll; margin-top: 0px;", innerHtml: msg }).down() } else { p = row.appendElement({ tag: "p", class: "col s12", style: "overflow-y: scroll; margin-top: 4px; margin-bottom: 4px;", innerHtml: msg}).down() } if (this.editable == true) { let deleteBtn = row.appendElement({ tag: "i", class: "material-icons col s1", style: "text-align: right;", innerHtml: "delete" }).down() deleteBtn.element.onmouseenter = function () { this.style.cursor = "pointer"; } deleteBtn.element.onmouseout = function () { this.style.cursor = "default"; } p.element.className = "col s11"; deleteBtn.element.onclick = (evt: any) => { evt.stopPropagation() // Here I will deleteLog(info, () => { /** nothing to do here. */ }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }) row.delete() } } } } onlogin(data: any) { // overide... this.editable = true; } onlogout() { // overide... this.editable = false; } } /** * This class is use to manage file on the server. */ export class LogManager extends Panel { // if true it means the log is editable. private logsDiv: any; private errorsDiv: any; private errorsPanel: LogsPanel; private servicesConsole: LogsPanel; private servicesConsoleDiv: any private listeners: Map<string, string>; private logs: Array<LogInfo>; private errors: Array<LogInfo>; private consoles: Array<LogInfo>; // File panel constructor. constructor(id: string) { super(id); // Keep track of listeners. this.listeners = new Map<string, string>(); this.logs = new Array<LogInfo>(); this.errors = new Array<LogInfo>(); this.consoles = new Array<LogInfo>(); let ul = this.div.appendElement({ tag: "div", class: "row", style: "margin: 0px" }).down() .appendElement({ tag: "div", class: "col s12", style: "padding: 10px;" }).down() .appendElement({ tag: "ul", class: "tabs", id: "logs_tabs" }).down() // The log's table tab let logTab = ul.appendElement({ tag: "li", class: "tab col s4" }).down() .appendElement({ tag: "a", href: "javascript:void(0)", innerHtml: "Log(s)", class: "grey-text text-darken-3 active", title: "table of gRPC actions activity." }).down() this.logsDiv = this.div.appendElement({ tag: "div", class: "row" }).down() .appendElement({ tag: "div", class: "col s12" }).down() // The error's tab let errorTab = ul.appendElement({ tag: "li", class: "tab col s4" }).down() .appendElement({ tag: "a", href: "javascript:void(0)", innerHtml: "Errors(s)", class: "grey-text text-darken-3", title: "error's information" }).down() this.errorsDiv = this.div.appendElement({ tag: "div", class: "row", style: "display: none;" }).down() let errorsPanel = this.errorsDiv.appendElement({ tag: "div", class: "col s12" }).down() this.errorsPanel = new LogsPanel(errorsPanel); // The services console tab. let servicesConsoleTab = ul.appendElement({ tag: "li", class: "tab col s4" }).down() .appendElement({ tag: "a", href: "javascript:void(0)", innerHtml: "Services(s)", class: "grey-text text-darken-3", title: "sevices console output." }).down() this.servicesConsoleDiv = this.div.appendElement({ tag: "div", class: "row", style: "display: none;" }).down() let servicesConsolePanel = this.servicesConsoleDiv.appendElement({ tag: "div", class: "col s12" }).down() this.servicesConsole = new LogsPanel(servicesConsolePanel); // Connect the event listener. let connectListener = (channel: string) => { eventHub.subscribe(channel, (uuid) => { this.listeners.set(channel, uuid); }, (evt: any) => { // When log about user is created. evt = JSON.parse(evt) let info = new LogInfo() info.setApplication(evt.application) info.setDate(parseInt(evt.date)) info.setMethod(evt.method) info.setUserid(evt.userId) info.setUsername(evt.userName) info.setMessage(evt.message) // If the method start with / it's a grpc action log if (evt.method.startsWith("/")) { if (evt.message != undefined) { info.setType(LogType.ERROR) this.errors.push(info) this.errorsPanel.displayLogs(this.errors); // diplay the message in the toast to get attention of the admin. M.toast({ html: evt.message.replace(new RegExp('\r?\n', 'g'), "<br />"), displayLength: 4000 }); } else { info.setType(LogType.INFO) this.logs.push(info) let row = new Array<any>(); row.push(info.getMethod()) row.push(info.getApplication()) row.push(info.getUsername()) row.push(new Date(info.getDate() * 1000)) let table = <any>document.getElementById("log_table"); if (table != undefined) { table.data.unshift(row) table.sort(); table.refresh(); } fireResize(); } } else { // It's a services console log. info.setType(LogType.INFO) this.consoles.push(info) this.servicesConsole.displayLogs(this.consoles); // diplay the message in the toast to get attention of the admin. if (evt.message != undefined) { M.toast({ html: evt.message.replace(new RegExp('\r?\n', 'g'), "<br />"), displayLength: 4000 }); } } }, false) } // Connect listeners. getAllActions((actions) => { // Here I will connect a listener to each action.... for (var i = 0; i < actions.length; i++) { connectListener(actions[i]); } // I will also connect the listener to each services. for (let serviceId in globular.config.Services) { connectListener(serviceId); } }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); } ); // Here I will set the tabs logics. errorTab.element.onclick = () => { this.logsDiv.element.style.display = "none" this.errorsDiv.element.style.display = "block" this.servicesConsoleDiv.element.style.display = "none" } logTab.element.onclick = () => { this.errorsDiv.element.style.display = "none" this.logsDiv.element.style.display = "block" this.servicesConsoleDiv.element.style.display = "none" } servicesConsoleTab.element.onclick = () => { this.logsDiv.element.style.display = "none" this.errorsDiv.element.style.display = "none" this.servicesConsoleDiv.element.style.display = "block" } // overide... readLogs("", (logs: Array<LogInfo>) => { for (var i = 0; i < logs.length; i++) { let info = logs[i]; // if method start with / it's a gRpc action log if (info.getMethod().startsWith("/")) { if (info.getMessage().length > 0) { this.errors.push(info); } else { this.logs.push(info); } } else { // its a services console this.consoles.push(info); } } // display errors this.errorsPanel.displayLogs(this.errors); // display services console logs this.servicesConsole.displayLogs(this.consoles); // display the table. this.initLogTable(); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); } ); } // Create the table element that will be use to display log's initLogTable() { var table = <any>(document.createElement("table-element")) var header = <any>(document.createElement("table-header-element")) table.id = "log_table"; header.fixed = true; // Create the dom table element table.appendChild(header) table.rowheight = 35 table.width = 1200 table.style.maxHeight = screen.height - 235 + "px"; // use the screen to calculate the table heigth. table.data = [] for (var i = 0; i < this.logs.length; i++) { let row = new Array<any>(); row.push(this.logs[i].getMethod()) row.push(this.logs[i].getApplication()) row.push(this.logs[i].getUsername()) row.push(new Date(this.logs[i].getDate() * 1000)) table.data.push(row) } // Create the column headers. // The method var methodHeaderCell = <any>(document.createElement("table-header-cell-element")) methodHeaderCell.innerHTML = "<table-sorter-element></table-sorter-element><div>Method</div> <table-filter-element></table-filter-element>" methodHeaderCell.width = 400; header.appendChild(methodHeaderCell) // The application var applicationHeaderCell = <any>(document.createElement("table-header-cell-element")) applicationHeaderCell.innerHTML = "<table-sorter-element></table-sorter-element><div>Application</div> <table-filter-element></table-filter-element>" header.appendChild(applicationHeaderCell) // The application var userHeaderCell = <any>(document.createElement("table-header-cell-element")) userHeaderCell.innerHTML = "<table-sorter-element></table-sorter-element><div>User</div> <table-filter-element></table-filter-element>" header.appendChild(userHeaderCell) // The creation date var dateHeaderCell = <any>(document.createElement("table-header-cell-element")) dateHeaderCell.innerHTML = "<table-sorter-element></table-sorter-element><div>Date</div> <table-filter-element></table-filter-element>" dateHeaderCell.width = 300 dateHeaderCell.onrender = function (div: any, value: any) { if (value != undefined) { div.innerHTML = value.toDateString() + " " + value.toLocaleTimeString(); } } header.appendChild(dateHeaderCell) this.logsDiv.element.appendChild(table) if (table.menu != null) { table.menu.getChildById("delete-filtere-menu-item").element.action = () => { let values = table.getFilteredData(); // Now I will reset the table data. table.data = [] let logs = new Array<LogInfo>(); for (var i = 0; i < this.logs.length; i++) { // if the value is in filtered data I will not keep it... if (values.find((e: any) => e.index == i) == undefined) { let row = new Array<any>(); row.push(this.logs[i].getMethod()) row.push(this.logs[i].getApplication()) row.push(this.logs[i].getUsername()) row.push(new Date(this.logs[i].getDate() * 1000)) table.data.push(row) logs.push(this.logs[i]) } else { // delete log from the logs. deleteLog(this.logs[i], () => { }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }) } } // Set back logs and refresh the table. this.logs = logs; table.filtered = [] table.sorted = [] table.sort() table.refresh() fireResize(); } table.menu.getChildById("delete-all-data-menu-item").element.action = () => { clearAllLog(LogType.INFO, () => { M.toast({ html: "All logs are deleted!", displayLength: 2000 }); table.data = [] table.filtered = [] table.sorted = [] table.refresh(); fireResize(); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); } ); } } } onlogin(data: any) { this.errorsPanel.onlogin(null); // be sure the editable variable is set. this.errorsPanel.displayLogs(this.errors) this.servicesConsole.onlogin(null) this.servicesConsole.displayLogs(this.consoles) document.getElementById("delete-filtere-menu-item").style.display = "block" document.getElementById("delete-all-data-menu-item").style.display = "block" } onlogout() { this.errorsPanel.onlogin(null); // be sure the editable variable is set. this.errorsPanel.displayLogs(this.errors) this.servicesConsole.onlogin(null) this.servicesConsole.displayLogs(this.consoles) document.getElementById("delete-filtere-menu-item").style.display = "none" document.getElementById("delete-all-data-menu-item").style.display = "none" } }