sing-song-player-3vx0f39v / web-socket.html
soiz1's picture
Update web-socket.html
7c730f1
raw
history blame
5.33 kB
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://soiz1-eruda3.hf.space/eruda.js"></script>
<script>eruda.init();</script>
<meta charset="UTF-8" />
<title>管理ページ(IP・接続時間表示 + 地図表示)</title>
<script src="https://cdn.socket.io/4.0.1/socket.io.min.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/leaflet.css"
crossorigin=""
/>
<script
src="https://unpkg.com/[email protected]/dist/leaflet.js"
crossorigin=""
></script>
<style>
#map {
height: 400px;
width: 100%;
border: 1px solid #ccc;
margin-bottom: 20px;
}
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ccc;
padding: 8px;
text-align: left;
}
th {
background-color: #eee;
}
.highlight {
background-color: #f99;
transition: background-color 1s ease;
}
</style>
</head>
<body>
<div id="map"></div>
<h1>現在の接続ユーザー数: <span id="userCount">0</span></h1>
<div>更新はページ再読み込みで行います。</div>
<table>
<thead>
<tr>
<th>Socket ID</th>
<th>IPアドレス</th>
<th>接続時間</th>
<th>切断時間</th>
<th>位置情報</th>
<th>都市</th>
<th>地域</th>
<th></th>
</tr>
</thead>
<tbody id="userTableBody"></tbody>
</table>
<script>
const socket = io("https://web-socket-server-14ap.onrender.com/", {
query: {
isAdmin: true // 管理画面からの接続であることを示す
}
});
const STORAGE_KEY = "userSessions";
// 過去・現在含む全セッション情報を管理
let allSessions = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
const userCountElem = document.getElementById("userCount");
const tbody = document.getElementById("userTableBody");
const map = L.map('map').setView([35.681236, 139.767125], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
let markers = {};
function createCircleMarker(lat, lon, color, popupText) {
return L.circleMarker([lat, lon], {
radius: 10,
fillColor: color,
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.9
}).bindPopup(popupText);
}
// ユーザー数更新
socket.on("updateUserCount", (count) => {
userCountElem.textContent = count;
});
// 現在接続中のセッション更新(リアルタイム)
socket.on("userSessionsUpdate", (userSessions) => {
// 現在のセッションをallSessionsにマージ(上書き)
for (const [socketId, info] of Object.entries(userSessions)) {
allSessions[socketId] = info;
}
renderTable(allSessions);
});
// 過去セッション履歴受信時
socket.on("userSessionHistory", (sessionHistory) => {
// 履歴の各セッションをallSessionsに追加(既存のsocketIdを上書きしない)
for (const session of sessionHistory) {
if (!allSessions[session.socketId]) {
allSessions[session.socketId] = session;
}
}
renderTable(allSessions);
});
function renderTable(sessions) {
tbody.innerHTML = "";
// 既存マーカー削除
for (let key in markers) {
map.removeLayer(markers[key]);
}
markers = {};
for (const [socketId, info] of Object.entries(sessions)) {
const tr = document.createElement("tr");
function createTd(text) {
const td = document.createElement("td");
td.textContent = text;
return td;
}
tr.appendChild(createTd(socketId));
tr.appendChild(createTd(info.ip || "-"));
tr.appendChild(createTd(info.connectTime ? new Date(info.connectTime).toLocaleString() : "-"));
tr.appendChild(createTd(info.disconnectTime ? new Date(info.disconnectTime).toLocaleString() : "-"));
tr.appendChild(createTd(info.loc || "-"));
tr.appendChild(createTd(info.city || "-"));
tr.appendChild(createTd(info.region || "-"));
tr.appendChild(createTd(info.country || "-"));
tbody.appendChild(tr);
// 位置情報がある場合は地図にマーカーを追加
if (info.loc) {
const [lat, lon] = info.loc.split(',').map(Number);
const locationText = [
`SocketID: ${socketId}`,
`IP: ${info.ip || "-"}`,
`都市: ${info.city || "-"}`,
`地域: ${info.region || "-"}`,
`国: ${info.country || "-"}`
].join('<br>');
const marker = createCircleMarker(lat, lon, 'blue', locationText);
marker.addTo(map);
markers[socketId] = marker;
}
}
// マーカーがあれば地図を調整
if (Object.keys(markers).length > 0) {
const group = L.featureGroup(Object.values(markers));
map.fitBounds(group.getBounds().pad(0.5));
}
// ローカルストレージに保存
localStorage.setItem(STORAGE_KEY, JSON.stringify(sessions));
}
</script>
</body>
</html>