feat(servers): implement saervers monitoring
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<header>
|
||||
<nav>
|
||||
<router-link to="/">Главная</router-link> |
|
||||
|
||||
<router-link to="/">Главная</router-link> | <router-link to="/servers">Мониторинг</router-link> |
|
||||
<template v-if="!authStore.isAuthenticated">
|
||||
<router-link to="/login">Вход</router-link> |
|
||||
<router-link to="/register">Регистрация</router-link>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -23,6 +24,11 @@ const routes = [
|
||||
component: () => import("../views/AccountView.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/servers",
|
||||
name: "servers",
|
||||
component: () => import("../views/ServersView.vue"),
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
143
src/views/ServersView.vue
Normal file
143
src/views/ServersView.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="servers-container">
|
||||
<h1>Мониторинг серверов</h1>
|
||||
<div v-if="isLoading" class="loading">Загрузка данных...</div>
|
||||
<div v-if="error" class="error-message">{{ error }}</div>
|
||||
|
||||
<div class="ping-info">
|
||||
Ваш пинг до сервера:
|
||||
<span v-if="clientProxyPing !== null">{{ clientProxyPing }} мс</span>
|
||||
<span v-else>измерение...</span>
|
||||
</div>
|
||||
|
||||
<div class="server-list">
|
||||
<div v-for="server in servers" :key="server.id" class="server-card">
|
||||
<h3>{{ server.name }}</h3>
|
||||
<p class="motd" v-html="formatMotd(server.motd)"></p>
|
||||
<div class="status">
|
||||
<span class="online-status" :class="{ 'is-online': server.player_count !== null }">
|
||||
{{ server.player_count !== null ? "Online" : "Offline" }}
|
||||
</span>
|
||||
<span class="players" v-if="server.player_count !== null"> {{ server.player_count }} / {{ server.max_players }} </span>
|
||||
</div>
|
||||
<div class="ping">
|
||||
Общий пинг:
|
||||
<span v-if="totalPing(server) !== null">{{ totalPing(server) }} мс</span>
|
||||
<span v-else>N/A</span>
|
||||
</div>
|
||||
<div class="version">{{ server.version_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import apiClient from "@/api/axios";
|
||||
|
||||
// Тип для сервера, можно вынести в types.ts
|
||||
interface GameServer {
|
||||
id: number;
|
||||
name: string;
|
||||
motd: string | null;
|
||||
player_count: number | null;
|
||||
max_players: number | null;
|
||||
version_name: string | null;
|
||||
ping_proxy_server: number | null;
|
||||
}
|
||||
|
||||
const servers = ref<GameServer[]>([]);
|
||||
const isLoading = ref(true);
|
||||
const error = ref<string | null>(null);
|
||||
const clientProxyPing = ref<number | null>(null);
|
||||
|
||||
let ws: WebSocket | null = null;
|
||||
let pingInterval: number | null = null;
|
||||
|
||||
const fetchServers = async () => {
|
||||
try {
|
||||
const response = await apiClient.get<GameServer[]>("/servers");
|
||||
servers.value = response.data;
|
||||
} catch (e) {
|
||||
error.value = "Не удалось загрузить список серверов.";
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const setupWebSocket = () => {
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/ws/ping`;
|
||||
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log("WebSocket connection established.");
|
||||
pingInterval = window.setInterval(() => {
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.send(Date.now().toString());
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const serverTimestamp = parseInt(event.data, 10);
|
||||
clientProxyPing.value = Date.now() - serverTimestamp;
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log("WebSocket connection closed.");
|
||||
if (pingInterval) clearInterval(pingInterval);
|
||||
};
|
||||
|
||||
ws.onerror = (err) => {
|
||||
console.error("WebSocket error:", err);
|
||||
error.value = "Ошибка подключения к WebSocket.";
|
||||
};
|
||||
};
|
||||
|
||||
const totalPing = (server: GameServer) => {
|
||||
if (clientProxyPing.value !== null && server.ping_proxy_server !== null) {
|
||||
return clientProxyPing.value + server.ping_proxy_server;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Функция для форматирования MOTD с кодами цвета Minecraft
|
||||
const formatMotd = (motd: string | null) => {
|
||||
if (!motd) return "";
|
||||
return motd.replace(/§[0-9a-fk-or]/g, "");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchServers();
|
||||
setupWebSocket();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
if (pingInterval) {
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ... добавьте стили для карточек серверов ... */
|
||||
.server-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.server-card {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
.ping-info {
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user