Files
frontend/src/views/ServersView.vue

144 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>