|
<!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) => { |
|
|
|
for (const [socketId, info] of Object.entries(userSessions)) { |
|
allSessions[socketId] = info; |
|
} |
|
renderTable(allSessions); |
|
}); |
|
|
|
|
|
socket.on("userSessionHistory", (sessionHistory) => { |
|
|
|
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> |