{{define "theme-mdui/home"}} <!doctype html> <html lang="{{.Conf.Language}}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>{{.Title}}</title> <link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" /> <!-- MDUI CSS --> <link rel="stylesheet" href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/mdui/1.0.2/css/mdui.min.css"/> <link rel="stylesheet" href="/static/theme-mdui/mdui.css" type="text/css"> <style> .mdui-table td, .mdui-table th{padding: 6px;} .progress{width: 10%;min-width: 75px;} .progress-text{font-size: 16px;font-weight: 800;position: relative;top: 4px;left: 6px;} .offline st,.offline at,.offline gt,.offline .progress-text{color: grey;} a{text-decoration:none;color:#333;}.mdui-theme-layout-dark a{color:#fff;} </style> {{if ts .CustomCode}} {{.CustomCode|safe}} {{end}} </head> <body> {{template "theme-mdui/menu" .}} <div id="app"> <div id="container" class="mdui-container"> <button @click="toggleView" class="mdui-fab mdui-fab-wrapper mdui-fab-fixed mdui-ripple mdui-color-pink-accent"> <i v-if="showCard" class="mdui-icon material-icons">list</i> <i v-else class="mdui-icon material-icons">apps</i> </button> <div v-if="showCard" class="mdui-row-xs-1 mdui-row-sm-2 mdui-row-md-3 mdui-row-lg-4"> <div id="servers"> <div class="mdui-col" v-for='server in servers' :id="server.ID"> <div :class="'mdui-card mt' + (server.live?'':' offline')"> <div class="mdui-card-header"> <img class="mdui-card-header-avatar" :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/1x1/' + (server.Host.CountryCode?server.Host.CountryCode:'cn') + '.svg'"/> <div class="mdui-card-header-title">@#server.Name#@</div> <div class="mdui-card-header-subtitle">@#server.Host.CountryCode.toUpperCase()#@ | @#server.Host.Platform#@ @#server.Host.PlatformVersion#@</div> </div> <div v-if="server.live" class="mdui-card-menu"> <i :id="'info-' + server.ID" class="mdui-icon material-icons">info_outline</i> </div> <div v-else class="mdui-card-menu mdui-typo-title mdui-text-color-grey">Offline</div> <div class="mdui-card-content"> <ul class="mdui-list"> <li class="mdui-list-item"> <i class="mdui-list-item-icon mdui-icon material-icons">memory</i> <div class="mdui-list-item-content"> <st class="mdui-list-item-title mdui-list-item-one-line">CPU <span>@#server.live?parseInt(server.State.CPU):'NaN'#@%</span></st> <div class="mdui-list-item-text" style="opacity:1;"> <div class="mdui-progress"> <div class="mdui-progress-determinate mdui-color-indigo-400" :style="'width: ' + (server.live?server.State.CPU:'0') + '%;'"></div> </div> </div> </div> </li> <li class="mdui-list-item" :id="'mem-' + server.ID"> <i class="mdui-list-item-icon mdui-icon material-icons">straighten</i> <div class="mdui-list-item-content"> <at class="mdui-list-item-title mdui-list-item-one-line">MEM <span>@#server.live?parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0):'NaN'#@%</span></at> <div class="mdui-progress"> <div class="mdui-progress-determinate mdui-color-pink-400" :style="'width: ' + (server.live?parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0):'0') + '%;'"></div> </div> </div> </li> <li class="mdui-list-item"> <i class="mdui-list-item-icon mdui-icon material-icons">swap_vert</i> <div class="mdui-list-item-content"> <div class="mdui-list-item-title">{{tr "UpNetTransfer"}}</div> <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;"> <at><span>@#formatNetByteSize(server.State.NetOutSpeed)#@</span></at> </div> </div> <div class="mdui-list-item-content"> <div class="mdui-list-item-title">{{tr "DownNetTransfer"}}</div> <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;"> <st><span>@#formatNetByteSize(server.State.NetInSpeed)#@</span></st> </div> </div> </li> <li class="mdui-list-item"> <i class="mdui-list-item-icon mdui-icon material-icons">swap_horiz</i> <div class="mdui-list-item-content"> <div class="mdui-list-item-title">{{tr "TotalUpNetTransfer"}}</div> <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;"> <at><span>@#formatByteSize(server.State.NetOutTransfer)#@</span></at> </div> </div> <div class="mdui-list-item-content"> <div class="mdui-list-item-title">{{tr "TotalDownNetTransfer"}}</div> <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;"> <st><span>@#formatByteSize(server.State.NetInTransfer)#@</span></st> </div> </div> </li> </ul> </div> </div> </div> </div> </div> <div v-else class="mdui-table-fluid mdui-m-t-1"> <table class="mdui-table mdui-table-hoverable"> <thead> <tr> <th class="mdui-text-center">ID</th> <th class="mdui-text-center">{{tr "Name"}}</th> <th class="mdui-text-center">{{tr "UpNetTransfer"}}</th> <th class="mdui-text-center">{{tr "DownNetTransfer"}}</th> <th class="mdui-text-center">{{tr "TotalUpNetTransfer"}}</th> <th class="mdui-text-center">{{tr "TotalDownNetTransfer"}}</th> <th class="mdui-text-center">CPU</th> <th class="mdui-text-center">RAM</th> <th class="mdui-text-center">{{tr "Uptime"}}</th> </tr> </thead> <tbody> <tr :class="(server.live?'':'offline')" v-for="server in servers"> <td class="mdui-text-center">@#server.ID#@</td> <td class="mdui-text-center">@#server.Name#@</td> <td class="mdui-text-center"><at>@#formatNetByteSize(server.State.NetOutSpeed)#@</at></td> <td class="mdui-text-center"><st>@#formatNetByteSize(server.State.NetInSpeed)#@</st></td> <td class="mdui-text-center"><at>@#formatByteSize(server.State.NetOutTransfer)#@</at></td> <td class="mdui-text-center"><st>@#formatByteSize(server.State.NetInTransfer)#@</st></td> <td class="progress"> <div class="mdui-progress" style="height: 30px; background-color: #edbbd2;"> <div class="mdui-progress-determinate mdui-color-pink-a400" :style="'width: ' + (server.live?server.State.CPU:'0') + '%;'"> <span class="mdui-text-truncate progress-text">@#server.live?parseInt(server.State.CPU):'NaN'#@%</span> </div> </div> </td> <td class="progress"> <div class="mdui-progress" style="height: 30px;"> <div class="mdui-progress-determinate mdui-color-indigo-400" :style="'width: ' + parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0) + '%;'"> <span class="mdui-text-truncate progress-text">@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</span> </div> </div> </td> <td class="mdui-text-center">@#secondToDate(server.State.Uptime)#@</td> </tr> </tbody> </table> </div> </div> </div> {{template "theme-mdui/footer" .}} <script src="/static/theme-mdui/mdui.js"></script> <script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/mdui/1.0.2/js/mdui.min.js"></script> <script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/jquery/3.6.0/jquery.min.js"></script> <script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script> <script> var container=document.querySelector("#container"); container.style.minHeight=window.innerHeight-document.body.clientHeight+container.clientHeight+'px'; mdui.mutation(); const initData = JSON.parse('{{.Servers}}').servers; var statusCards = new Vue({ el: '#app', delimiters: ['@#', '#@'], data: { servers: initData, cache: [], showCard: true }, methods: { toggleView() { this.showCard = !this.showCard }, toFixed2(f) { return f.toFixed(2) }, secondToDate(s) { var d = Math.floor(s / 3600 / 24); if (d > 0) { return d + " {{tr "Day"}}" } var h = Math.floor(s / 3600 % 24); var m = Math.floor(s / 60 % 60); var s = Math.floor(s % 60); return h + ":" + ("0" + m).slice(-2) + ":" + ("0" + s).slice(-2); }, readableBytes(bytes) { if (!bytes) { return '0B' } var i = Math.floor(Math.log(bytes) / Math.log(1024)), sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i]; }, readableNetBytes(bytes) { if (!bytes) { return '0B' } var Kbps=125, Mbps=Kbps*1000, Gbps=Mbps*1000, Tbps=Gbps*1000; if (bytes < Kbps) return (bytes * 8).toFixed(2) + 'bps'; if (bytes < Mbps) return (bytes / Kbps).toFixed(2) + 'Kbps'; if (bytes < Gbps) return (bytes / Mbps).toFixed(2) + 'Mbps'; if (bytes < Tbps) return (bytes / Gbps).toFixed(2) + 'Gbps'; else return (bytes / Tbps).toFixed(2) + 'Tbps'; }, formatTimestamp(t) { return new Date(t * 1000).toLocaleString() }, formatByteSize(bs) { const x = this.readableBytes(bs) return x != "NaN undefined" ? x : 'NaN' }, formatNetByteSize(bs) { const x = this.readableNetBytes(bs) return x != "NaN undefined" ? x : 'NaN' }, formatTooltip(server) { var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal); var upTime = this.secondToDate(server.State.Uptime); var tooltip = `{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`; return tooltip } } }) const wsProtocol = window.location.protocol == "https:" ? "wss" : "ws" let canShowError = true; function connect() { const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws'); ws.onopen = function (evt) { canShowError = true; mdui.snackbar({ message: '{{tr "RealtimeChannelEstablished"}}', timeout: 2000, position: 'top', onClosed: function () { mdui.mutation(); } }); } var infoTooltip = {}, memTooltip = {}; ws.onmessage = function (evt) { const data = JSON.parse(evt.data) statusCards.servers = data.servers for (let i = 0; i < statusCards.servers.length; i++) { const ns = statusCards.servers[i]; if (!ns.Host) ns.live = false else { const lastActive = new Date(ns.LastActive).getTime() if (data.now - lastActive > 10 * 1000) { ns.live = false } else { ns.live = true if (statusCards.showCard) { if (infoTooltip[ns.ID]) { var disk = statusCards.formatByteSize(ns.State.DiskUsed) + '/' + statusCards.formatByteSize(ns.Host.DiskTotal); var upTime = statusCards.secondToDate(ns.State.Uptime); var content = `System: ${ns.Host.Platform}-${ns.Host.PlatformVersion}[${ns.Host.Arch}] CPU: ${ns.Host.CPU} Disk: ${disk} Online: ${upTime} Version: ${ns.Host.Version}`; infoTooltip[ns.ID].$element[0].innerText = content; } else { if (document.getElementById(`info-${ns.ID}`)) infoTooltip[ns.ID] = new mdui.Tooltip(`#info-${ns.ID}`, {}); } if (memTooltip[ns.ID]) { var content = `${statusCards.formatByteSize(ns.State.MemUsed)}/${statusCards.formatByteSize(ns.Host.MemTotal)}`; memTooltip[ns.ID].$element[0].innerText = content; } else { if (document.getElementById(`mem-${ns.ID}`)) memTooltip[ns.ID] = new mdui.Tooltip(`#mem-${ns.ID}`, {}); } } else { mdui.$('div').remove('.mdui-tooltip'); infoTooltip = {}; memTooltip = {}; } } } } mdui.mutation(); } ws.onclose = function () { if (canShowError) { canShowError = false; mdui.snackbar({ message: '{{tr "RealtimeChannelDisconnect"}}', timeout: 2000, position: 'top', }); } setTimeout(function () { connect() }, 3000); } ws.onerror = function () { ws.close() } } connect(); </script> </body> </html> {{end}}