Skip to content

Commit 64284bc

Browse files
authored
Merge pull request #28 from mcserversoft/dev
v5.1.0
2 parents 92d558a + 849335b commit 64284bc

File tree

7 files changed

+952
-1338
lines changed

7 files changed

+952
-1338
lines changed

package-lock.json

Lines changed: 741 additions & 1333 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mcss-remote-panel",
3-
"version": "5.0.1",
3+
"version": "5.1.0",
44
"private": true,
55
"scripts": {
66
"dev": "vite dev",

src/lib/api.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@ export interface IServer {
1212
status: number;
1313
}
1414

15+
export interface Stats {
16+
cpu: number;
17+
memory: Memory;
18+
playersOnline: number;
19+
playerLimit: number;
20+
startDateUnix: number;
21+
startDate: string;
22+
uptime: string;
23+
}
24+
25+
export interface Memory {
26+
current: number;
27+
max: number;
28+
free: number;
29+
percentageFree: number;
30+
}
31+
1532
export enum Filter {
1633
None,
1734
Minimal,
@@ -129,4 +146,4 @@ export async function sendServerCommand(input: string) {
129146
logout();
130147
}
131148
});
132-
}
149+
}

src/lib/components/actionDropdown.svelte

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@
2222
}
2323
</script>
2424

25-
<div class="relative inline-flex" use:clickOutside on:click_outside={handleClickOutside}>
25+
<div class="relative" use:clickOutside on:click_outside={handleClickOutside}>
26+
2627
<button on:click={toggleDropdown} aria-controls="dropdown" aria-expanded={dropdownVisible} class="inline-flex justify-center items-center group p-2 rounded bg-blue-600 hover:bg-blue-700">
27-
<div class="flex items-center truncate text-zinc-200">
28+
<div class="flex items-center truncate text-zinc-100">
2829
<span class="truncate ml-2 text-xs font-medium uppercase">{statusName}</span>
2930
<ArrowDownSvg />
3031
</div>
3132
</button>
3233

3334
{#if dropdownVisible}
34-
<div id="dropdown" class="absolute top-full right-0 min-w-44 py-1.5 mt-1 rounded shadow-lg overflow-hidden bg-custom-gray-lightest ">
35+
<div id="dropdown" class="absolute top-full right-0 min-w-44 py-1.5 mt-1 rounded shadow-lg overflow-hidden bg-custom-gray-lightest">
3536
<ul class="w-28">
3637
{#each actions as action}
3738
<li>

src/lib/components/serverStats.svelte

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<script lang="ts">
2+
import { onDestroy } from 'svelte';
3+
import { get } from 'svelte/store';
4+
import { browser } from '$app/environment';
5+
import { auth, logout } from '$lib/auth';
6+
import { baseUrl } from '$lib/routing';
7+
import { selectedServerGuid, type Stats } from '$lib/api';
8+
9+
let stats: Stats = {
10+
cpu: 0,
11+
memory: { current: 0, max: 0, free: 0, percentageFree: 0 },
12+
playersOnline: 0,
13+
playerLimit: 0,
14+
startDateUnix: 0,
15+
startDate: '',
16+
uptime: 'offline'
17+
};
18+
19+
let isLoadingStats: boolean = true;
20+
21+
if (browser) {
22+
const unsubscribe = selectedServerGuid.subscribe((newGuid) => {
23+
isLoadingStats = true;
24+
updateServerStats();
25+
});
26+
27+
const updateConsole = setInterval(() => {
28+
updateServerStats();
29+
}, 2000);
30+
31+
onDestroy(unsubscribe);
32+
onDestroy(() => clearInterval(updateConsole));
33+
}
34+
35+
async function updateServerStats() {
36+
const guid = get(selectedServerGuid);
37+
38+
if (!guid) {
39+
return;
40+
}
41+
42+
//TODO move this to api.ts
43+
const request = new Request(`${baseUrl}/api/v1/servers/${guid}/stats`, {
44+
method: `GET`,
45+
headers: {
46+
apiKey: get(auth).apiKey
47+
}
48+
});
49+
50+
await fetch(request)
51+
.then((response) => {
52+
if (response.status === 200) {
53+
return response.json();
54+
}
55+
return Promise.reject(response);
56+
})
57+
.then((data) => {
58+
stats.cpu = data.latest.cpu ?? 0;
59+
stats.memory.current = data.latest.memoryUsed ?? 0;
60+
stats.memory.max = data.latest.memoryLimit ?? 0;
61+
stats.memory.free = stats.memory.max - stats.memory.current;
62+
stats.memory.percentageFree = Math.round((stats.memory.current / stats.memory.max) * 100);
63+
64+
stats.playersOnline = data.latest.playersOnline ?? 0;
65+
stats.playerLimit = data.latest.playerLimit ?? 0;
66+
67+
stats.uptime = calculateUptime(data.latest.startDate);
68+
stats.startDateUnix = data.latest.startDate;
69+
stats.startDate = new Date(data.latest.startDate * 1000).toLocaleString(window.navigator.language, {
70+
year: 'numeric',
71+
month: '2-digit',
72+
day: '2-digit',
73+
hour: '2-digit',
74+
minute: '2-digit'
75+
});
76+
77+
isLoadingStats = false;
78+
})
79+
.catch((error) => {
80+
if (error.status === 401) {
81+
logout();
82+
}
83+
})
84+
.finally(() => {
85+
isLoadingStats = false;
86+
});
87+
}
88+
89+
function calculateUptime(startUnixTimestamp: number): string {
90+
if (startUnixTimestamp <= 0) {
91+
return 'offline';
92+
}
93+
94+
let diff = Date.now() - new Date(startUnixTimestamp * 1000);
95+
96+
let days = Math.floor(diff / (1000 * 60 * 60 * 24));
97+
diff -= days * (1000 * 60 * 60 * 24);
98+
99+
let hours = Math.floor(diff / (1000 * 60 * 60));
100+
diff -= hours * (1000 * 60 * 60);
101+
102+
let minutes = Math.floor(diff / (1000 * 60));
103+
diff -= minutes * (1000 * 60);
104+
105+
if (days > 0) {
106+
return `${days}d ${hours}h ${minutes}m`;
107+
} else if (hours > 0) {
108+
return `${hours}h ${minutes}m`;
109+
} else {
110+
let seconds = Math.floor(diff / 1000);
111+
return `${minutes}m ${seconds}s`;
112+
}
113+
}
114+
</script>
115+
116+
<div class="col-span-full mb-4 xl:col-span-4 shadow-lg rounded-md bg-custom-gray-lighter">
117+
<div class="stats bg-inherit stats-vertical sm:stats-horizontal block sm:inline-grid" class:animate-pulse={isLoadingStats}>
118+
<div class="stat">
119+
<div class="stat-figure text-secondary block sm:hidden md:block">
120+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block w-8 h-8 stroke-current">
121+
<path
122+
stroke-linecap="round"
123+
stroke-linejoin="round"
124+
d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25zm.75-12h9v9h-9v-9z"
125+
/>
126+
</svg>
127+
</div>
128+
<div class="stat-title">CPU</div>
129+
<div class="stat-value">{stats.cpu} %</div>
130+
<div class="stat-desc">current usage</div>
131+
</div>
132+
133+
<div class="stat">
134+
<div class="stat-figure text-secondary block sm:hidden md:block">
135+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="inline-block w-8 h-8 stroke-current">
136+
<path fill-rule="evenodd" d="M2.25 13.5a8.25 8.25 0 018.25-8.25.75.75 0 01.75.75v6.75H18a.75.75 0 01.75.75 8.25 8.25 0 01-16.5 0z" clip-rule="evenodd" />
137+
<path fill-rule="evenodd" d="M12.75 3a.75.75 0 01.75-.75 8.25 8.25 0 018.25 8.25.75.75 0 01-.75.75h-7.5a.75.75 0 01-.75-.75V3z" clip-rule="evenodd" />
138+
</svg>
139+
</div>
140+
<div class="stat-title">Memory</div>
141+
<div class="stat-value">{stats.memory.current} MB</div>
142+
<div class="stat-desc">{stats.memory.percentageFree}% used of {stats.memory.max} MB</div>
143+
</div>
144+
145+
{#if stats.playerLimit != 0}
146+
<div class="stat">
147+
<div class="stat-figure text-secondary block sm:hidden md:block">
148+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="inline-block w-8 h-8 stroke-current">
149+
<path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd" />
150+
</svg>
151+
</div>
152+
<div class="stat-title">Players</div>
153+
<div class="stat-value">{stats.playersOnline}</div>
154+
<div class="stat-desc">out of {stats.playerLimit}</div>
155+
</div>
156+
{/if}
157+
158+
<div class="stat">
159+
<div class="stat-figure text-secondary block sm:hidden md:block">
160+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="inline-block w-8 h-8 stroke-current">
161+
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zM12.75 6a.75.75 0 00-1.5 0v6c0 .414.336.75.75.75h4.5a.75.75 0 000-1.5h-3.75V6z" clip-rule="evenodd" />
162+
</svg>
163+
</div>
164+
<div class="stat-title">Uptime</div>
165+
<div class="stat-value">{stats.uptime}</div>
166+
167+
{#if stats.startDateUnix > 0}
168+
<div class="stat-desc">since {stats.startDate}</div>
169+
{:else}
170+
<div class="stat-desc">server not operational</div>
171+
{/if}
172+
</div>
173+
</div>
174+
</div>

src/lib/pages/dashboard.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Sidebar from '$lib/components/sidebar.svelte';
44
import Server from '$lib/components/server.svelte';
55
import Console from '$lib/components/console.svelte';
6+
import ServerStats from '$lib/components/serverStats.svelte';
67
</script>
78

89
<svelte:head>
@@ -18,6 +19,7 @@
1819
<div class="mb-20">
1920
{#if $selectedServerGuid}
2021
<Server />
22+
<ServerStats />
2123
<Console />
2224
{:else}
2325
<div class="text-center">

tailwind.config.cjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ module.exports = {
4040
"--rounded-btn": "0.375rem",
4141
// "--tw-scale-x": "0.5",
4242
// "--tw-scale-y": "0.5",
43+
44+
".stats" : {
45+
"border-radius" : "0.375rem"
46+
},
47+
// server stats icon
48+
".stat-figure" : {
49+
"padding-top" : "0.7em"
50+
},
51+
".stat-value" : {
52+
"font-size" : "1.5rem",
53+
"line-height" : "1.5rem"
54+
},
4355
},
4456
},
4557
],

0 commit comments

Comments
 (0)