2023-11-07 00:46:28 -05:00
|
|
|
{{define "theme-server-status/home"}}
|
|
|
|
{{template "theme-server-status/header" .}}
|
|
|
|
<div id="app">
|
|
|
|
{{template "theme-server-status/content-nav" .}}
|
2023-11-21 23:45:54 -05:00
|
|
|
<div class="container table-responsive content" style="max-width: 95vw">
|
2023-11-07 00:46:28 -05:00
|
|
|
<table class="table table-striped table-condensed table-hover">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th class="node-cell status center">🍀 {{tr "Status"}}</th>
|
|
|
|
<th class="node-cell name">🚀 {{tr "Name"}}</th>
|
|
|
|
<th class="node-cell os">🗂 {{tr "Platform"}}</th>
|
|
|
|
<th class="node-cell location center">🌍 {{tr "Location"}}</th>
|
|
|
|
<th class="node-cell uptime center">⏱️ {{tr "Uptime"}}</th>
|
|
|
|
<th class="node-cell load center">📋{{tr "Load"}}</th>
|
|
|
|
<th class="node-cell network center">🚦 {{tr "NetSpeed"}}↓|↑</th>
|
|
|
|
<th class="node-cell traffic center">📊 {{tr "NetTransfer"}}↓|↑</th>
|
|
|
|
<th class="node-cell cpu center">🎯 {{tr "CpuUsed"}}</th>
|
|
|
|
<th class="node-cell ram center">⚡ {{tr "MemUsed"}}</th>
|
|
|
|
<th class="node-cell hdd center">💾 {{tr "DiskUsed"}}</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody id="servers">
|
|
|
|
<template v-for="(node,index) in nodes">
|
|
|
|
<tr :id="'r'+index" data-toggle="collapse" :data-target="'#rt'+index" class="accordion-toggle"
|
|
|
|
:class="index % 2 === 0 ? 'odd': 'even'">
|
|
|
|
<td class="node-cell status center">
|
|
|
|
<div class="status-container">
|
|
|
|
<div v-if="node.online" class="status-icon online"></div>
|
|
|
|
<div v-else class="status-icon offline"></div>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td class="node-cell name">@#node.name#@</td>
|
|
|
|
<td class="node-cell os">
|
|
|
|
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
|
|
|
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
|
|
|
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
|
|
|
@#node.os#@
|
|
|
|
</td>
|
|
|
|
<td style="text-align: center;" class="node-cell location">
|
|
|
|
<i :class="node.location + ' flag'"></i>
|
2023-11-07 09:56:59 -05:00
|
|
|
<span class="text-uppercase">@#node.location#@</span>
|
2023-11-07 00:46:28 -05:00
|
|
|
</td>
|
|
|
|
<td style="text-align: center;" class="node-cell uptime">@#node.uptime#@</td>
|
|
|
|
<td style="text-align: center;" class="node-cell load">@#node.load#@</td>
|
|
|
|
<td style="text-align: center;" class="node-cell network">@#node.network#@</td>
|
|
|
|
<td style="text-align: center;" class="node-cell traffic">@#node.traffic#@</td>
|
|
|
|
<td class="node-cell cpu">
|
|
|
|
<div class="progress">
|
|
|
|
<div :style="node.cpu.style" :class="node.cpu.class"><small>@#node.cpu.percent#@%</small>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td class="node-cell memory">
|
|
|
|
<div class="progress">
|
|
|
|
<div :style="node.memory.style" :class="node.memory.class">
|
|
|
|
<small>@#node.memory.percent#@%</small>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td class="node-cell hdd">
|
|
|
|
<div class="progress">
|
|
|
|
<div :style="node.hdd.style" :class="node.hdd.class"><small>@#node.hdd.percent#@%</small>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr class="expandRow" :class="index % 2 === 0 ? 'odd': 'even'">
|
|
|
|
<td colspan="16">
|
|
|
|
<div class="accordian-body collapse" :id="'rt'+index">
|
|
|
|
<div style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
|
|
|
|
<div style="display: flex;align-items: flex-start;justify-content: center;flex-direction: column; width: 450px;max-width: 90vw">
|
|
|
|
<span class="node-cell-expand">
|
2023-11-08 00:17:42 -05:00
|
|
|
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
2023-11-07 00:46:28 -05:00
|
|
|
@#node.host.Platform#@
|
|
|
|
</span>
|
2023-11-26 09:29:55 -05:00
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "Location"}}:</span>
|
|
|
|
<i :class="node.location + ' flag'"></i>
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand" v-if="node.host.CPU">
|
2023-11-07 00:46:28 -05:00
|
|
|
<span class="node-cell-expand-label">CPU:</span>
|
|
|
|
@#node.host.CPU.join(",")#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand load">
|
|
|
|
<span class="node-cell-expand-label">{{tr "Load"}}:</span>
|
|
|
|
@#toFixed2(node.state.Load1)#@ / @#toFixed2(node.state.Load5)#@ /@#toFixed2(node.state.Load15)#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
|
|
|
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
|
|
|
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
|
|
|
<i class="arrow alternate circle down outline icon"
|
|
|
|
style="margin: 0"></i>@#formatByteSize(node.state.NetInTransfer)#@
|
|
|
|
<i class="arrow alternate circle up outline icon"
|
|
|
|
style="margin: 0"></i>@#formatByteSize(node.state.NetOutTransfer)#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
|
|
|
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
|
|
|
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "BootTime"}}:</span>
|
|
|
|
@#formatTimestamp(node.host.BootTime)#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "LastActive"}}:</span>
|
|
|
|
@#new Date(node.lastActive).toLocaleString()#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand" v-if="node.host.Virtualization">
|
|
|
|
<span class="node-cell-expand-label">{{tr "Virtualization"}}:</span>
|
|
|
|
@#node.host.Virtualization#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "ProcessCount"}}:</span>
|
|
|
|
@#node.state.ProcessCount#@
|
|
|
|
</span>
|
|
|
|
<span class="node-cell-expand">
|
|
|
|
<span class="node-cell-expand-label">{{tr "ConnCount"}}:</span>
|
|
|
|
TCP @#node.state.TcpConnCount#@ / UDP @#node.state.UdpConnCount#@
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
</div>
|
|
|
|
{{template "theme-server-status/content-footer" .}}
|
|
|
|
</div>
|
|
|
|
<script>
|
|
|
|
new Vue({
|
|
|
|
el: '#app',
|
|
|
|
delimiters: ['@#', '#@'],
|
|
|
|
data: {
|
|
|
|
nodes: [],
|
|
|
|
},
|
|
|
|
mixins: [mixinsVue],
|
|
|
|
created() {
|
|
|
|
const initData = JSON.parse('{{.Servers}}').servers;
|
|
|
|
this.nodes = this.handleNodes(initData);
|
|
|
|
this.initTheme()
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.connect();
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
isWindowsPlatform(str) {
|
|
|
|
return str.includes('Windows')
|
|
|
|
},
|
|
|
|
getFontLogoClass(str) {
|
|
|
|
if (["almalinux",
|
|
|
|
"alpine",
|
|
|
|
"aosc",
|
|
|
|
"apple",
|
|
|
|
"archlinux",
|
|
|
|
"archlabs",
|
|
|
|
"artix",
|
|
|
|
"budgie",
|
|
|
|
"centos",
|
|
|
|
"coreos",
|
|
|
|
"debian",
|
|
|
|
"deepin",
|
|
|
|
"devuan",
|
|
|
|
"docker",
|
|
|
|
"elementary",
|
|
|
|
"fedora",
|
|
|
|
"ferris",
|
|
|
|
"flathub",
|
|
|
|
"freebsd",
|
|
|
|
"gentoo",
|
|
|
|
"gnu-guix",
|
|
|
|
"illumos",
|
|
|
|
"kali-linux",
|
|
|
|
"linuxmint",
|
|
|
|
"mageia",
|
|
|
|
"mandriva",
|
|
|
|
"manjaro",
|
|
|
|
"nixos",
|
|
|
|
"openbsd",
|
|
|
|
"opensuse",
|
|
|
|
"pop-os",
|
|
|
|
"raspberry-pi",
|
|
|
|
"redhat",
|
|
|
|
"rocky-linux",
|
|
|
|
"sabayon",
|
|
|
|
"slackware",
|
|
|
|
"snappy",
|
|
|
|
"solus",
|
|
|
|
"tux",
|
|
|
|
"ubuntu",
|
|
|
|
"void",
|
|
|
|
"zorin"].indexOf(str)
|
|
|
|
> -1) {
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
|
|
|
|
return 'tux';
|
|
|
|
}
|
|
|
|
if (str == 'amazon') {
|
|
|
|
return 'redhat';
|
|
|
|
}
|
|
|
|
if (str == 'arch') {
|
|
|
|
return 'archlinux';
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
},
|
|
|
|
secondToDate(s) {
|
|
|
|
const d = Math.floor(s / 3600 / 24);
|
|
|
|
if (d > 0) {
|
2023-11-07 09:15:13 -05:00
|
|
|
return d + ' {{tr "Day"}}'
|
2023-11-07 00:46:28 -05:00
|
|
|
}
|
|
|
|
const h = Math.floor(s / 3600 % 24);
|
|
|
|
const m = Math.floor(s / 60 % 60);
|
|
|
|
const second = Math.floor(s % 60);
|
|
|
|
return h + ":" + ("0" + m).slice(-2) + ":" + ("0" + second).slice(-2);
|
|
|
|
},
|
|
|
|
formatTimestamp(t) {
|
|
|
|
return new Date(t * 1000).toLocaleString()
|
|
|
|
},
|
|
|
|
formatByteSize(bs) {
|
|
|
|
const x = this.readableBytes(bs)
|
|
|
|
return x !== "NaN undefined" ? x : '0B'
|
|
|
|
},
|
|
|
|
formatPercent(live, used, total) {
|
|
|
|
const percent = live ? (this.toFixed2(used / total * 100) || 0) : 1
|
|
|
|
return this.formatPercents(percent)
|
|
|
|
},
|
|
|
|
formatPercents(percent) {
|
|
|
|
if (percent <= 0) {
|
|
|
|
percent = 1;
|
|
|
|
}
|
|
|
|
if (!this.cache[percent]) {
|
|
|
|
this.cache[percent] = {
|
|
|
|
class: 'progress-bar progress-bar-success',
|
|
|
|
style: `width: ${parseInt(percent)}%`,
|
|
|
|
percent,
|
|
|
|
}
|
|
|
|
if (percent < 80) {
|
|
|
|
this.cache[percent].class = 'progress-bar progress-bar-success'
|
|
|
|
} else if (percent < 90) {
|
|
|
|
this.cache[percent].class = 'progress-bar progress-bar-warning'
|
|
|
|
} else {
|
|
|
|
this.cache[percent].class = 'progress-bar progress-bar-danger'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.cache[percent]
|
|
|
|
},
|
|
|
|
readableBytes(bytes) {
|
|
|
|
if (!bytes) {
|
|
|
|
return '0B'
|
|
|
|
}
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
|
|
sizes = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"];
|
|
|
|
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i];
|
|
|
|
},
|
|
|
|
connect() {
|
|
|
|
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws"
|
|
|
|
const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
|
|
|
|
ws.onopen = function () {
|
|
|
|
console.log("Connection open ...")
|
|
|
|
}
|
|
|
|
ws.onmessage = (evt) => {
|
|
|
|
let jsonData = evt.data
|
|
|
|
const data = JSON.parse(jsonData)
|
2023-11-07 09:15:13 -05:00
|
|
|
for (let i = 0; i < data.servers?.length; i++) {
|
2023-11-07 00:46:28 -05:00
|
|
|
const ns = data.servers[i];
|
|
|
|
if (!ns.Host) {
|
|
|
|
data.servers[i].live = false
|
|
|
|
} else {
|
|
|
|
const lastActive = new Date(ns.LastActive).getTime()
|
|
|
|
data.servers[i].live = data.now - lastActive <= 10 * 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.nodes = this.handleNodes(data.servers)
|
|
|
|
}
|
|
|
|
ws.onclose = () => {
|
|
|
|
setTimeout(function () {
|
|
|
|
this.connect()
|
|
|
|
}, 5000);
|
|
|
|
}
|
|
|
|
ws.onerror = function () {
|
|
|
|
ws.close()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
handleNodes(servers) {
|
|
|
|
let nodes = []
|
2023-11-07 09:15:13 -05:00
|
|
|
servers?.forEach(server => {
|
2023-11-07 00:46:28 -05:00
|
|
|
let platform = server.Host.Platform
|
|
|
|
if (this.isWindowsPlatform(server.Host.Platform)) {
|
|
|
|
platform = "windows"
|
|
|
|
}else if (platform === "immortalwrt") {
|
|
|
|
platform = "openwrt"
|
|
|
|
}
|
|
|
|
let node = {
|
|
|
|
name: server.Name,
|
|
|
|
os: platform,
|
|
|
|
location: server.Host.CountryCode,
|
|
|
|
uptime: this.secondToDate(server.State.Uptime),
|
|
|
|
load: this.toFixed2(server.State.Load1),
|
|
|
|
network: this.getNetworkSpeed(server.State.NetInSpeed, server.State.NetOutSpeed),
|
|
|
|
traffic: this.formatByteSize(server.State.NetInTransfer) + ' | ' + this.formatByteSize(server.State.NetOutTransfer),
|
|
|
|
cpu: this.formatPercents(this.toFixed2(server.State.CPU)),
|
|
|
|
memory: this.formatPercent(server.live, server.State.MemUsed, server.Host.MemTotal),
|
|
|
|
hdd: this.formatPercent(server.live, server.State.DiskUsed, server.Host.DiskTotal),
|
|
|
|
online: server.live,
|
|
|
|
state: server.State,
|
|
|
|
host: server.Host,
|
|
|
|
lastActive: server.LastActive,
|
|
|
|
}
|
|
|
|
nodes.push(node)
|
|
|
|
})
|
|
|
|
return nodes;
|
|
|
|
},
|
|
|
|
getNetworkSpeed(netInSpeed, netOutSpeed) {
|
|
|
|
return this.formatByteSize(netInSpeed) + ' | ' + this.formatByteSize(netOutSpeed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
{{template "theme-server-status/footer" .}}
|
|
|
|
{{end}}
|