Update web-socket.html
Browse files- web-socket.html +59 -44
web-socket.html
CHANGED
@@ -12,22 +12,34 @@
|
|
12 |
src="https://unpkg.com/[email protected]/dist/leaflet.js"
|
13 |
></script>
|
14 |
<style>
|
15 |
-
table { border-collapse: collapse; width: 100%; margin-bottom: 20px;}
|
16 |
-
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
|
17 |
-
th { background-color: #eee; }
|
18 |
-
.highlight {
|
19 |
-
background-color: #f99;
|
20 |
-
transition: background-color 1s ease;
|
21 |
-
}
|
22 |
#map {
|
23 |
height: 400px;
|
24 |
width: 100%;
|
25 |
-
margin-top: 20px;
|
26 |
border: 1px solid #ccc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
}
|
28 |
</style>
|
29 |
</head>
|
30 |
<body>
|
|
|
|
|
31 |
<h1>現在の接続ユーザー数: <span id="userCount">0</span></h1>
|
32 |
<div>更新はページ再読み込みで行います。</div>
|
33 |
<table>
|
@@ -42,57 +54,43 @@
|
|
42 |
<tbody id="userTableBody"></tbody>
|
43 |
</table>
|
44 |
|
45 |
-
<div id="map"></div>
|
46 |
-
|
47 |
<script>
|
48 |
const socket = io("https://web-socket-server-14ap.onrender.com/");
|
49 |
|
50 |
const STORAGE_KEY = "userSessions";
|
51 |
|
52 |
-
// 前回セッション情報
|
53 |
let previousSessions = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
|
54 |
|
55 |
const userCountElem = document.getElementById("userCount");
|
56 |
const tbody = document.getElementById("userTableBody");
|
57 |
-
const mapDiv = document.getElementById("map");
|
58 |
|
59 |
-
// Leaflet
|
60 |
-
const map = L.map('map').setView([35.681236, 139.767125], 5);
|
61 |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
62 |
maxZoom: 18,
|
63 |
attribution: '© OpenStreetMap contributors'
|
64 |
}).addTo(map);
|
65 |
|
66 |
-
// マーカー管理用(socketId => marker)
|
67 |
let markers = {};
|
68 |
|
69 |
-
// IP
|
70 |
async function fetchGeo(ip) {
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
return { lat: data.lat, lon: data.lon };
|
77 |
-
} else {
|
78 |
-
console.warn(`IP位置情報取得失敗: ${ip} - ${data.message}`);
|
79 |
-
return null;
|
80 |
-
}
|
81 |
-
} catch (e) {
|
82 |
-
console.error("IP位置情報取得エラー", e);
|
83 |
-
return null;
|
84 |
-
}
|
85 |
}
|
|
|
86 |
|
87 |
-
// マーカーの色を指定するためのCircleMarkerを作る関数
|
88 |
function createCircleMarker(lat, lon, color, popupText) {
|
89 |
return L.circleMarker([lat, lon], {
|
90 |
-
radius:
|
91 |
fillColor: color,
|
92 |
color: '#000',
|
93 |
weight: 1,
|
94 |
opacity: 1,
|
95 |
-
fillOpacity: 0.
|
96 |
}).bindPopup(popupText);
|
97 |
}
|
98 |
|
@@ -101,15 +99,17 @@
|
|
101 |
});
|
102 |
|
103 |
socket.on("userSessionsUpdate", async (userSessions) => {
|
|
|
|
|
104 |
tbody.innerHTML = "";
|
105 |
|
106 |
-
//
|
107 |
for(let key in markers) {
|
108 |
map.removeLayer(markers[key]);
|
109 |
}
|
110 |
markers = {};
|
111 |
|
112 |
-
//
|
113 |
const positions = {};
|
114 |
await Promise.all(Object.entries(userSessions).map(async ([socketId, info]) => {
|
115 |
positions[socketId] = await fetchGeo(info.ip);
|
@@ -142,17 +142,13 @@
|
|
142 |
|
143 |
tbody.appendChild(tr);
|
144 |
|
145 |
-
// 位置情報が取得できた場合、マーカー表示
|
146 |
const pos = positions[socketId];
|
147 |
if(pos) {
|
148 |
-
// 前回位置と比較(緯度経度を比較)
|
149 |
const prevPos = previousSessions[socketId]?.geoPosition;
|
150 |
let changedPos = true;
|
151 |
if(prevPos && Math.abs(prevPos.lat - pos.lat) < 0.0001 && Math.abs(prevPos.lon - pos.lon) < 0.0001) {
|
152 |
changedPos = false;
|
153 |
}
|
154 |
-
|
155 |
-
// 色を決定:変化あれば赤丸、なければ黒丸
|
156 |
const color = changedPos ? 'red' : 'black';
|
157 |
|
158 |
const marker = createCircleMarker(pos.lat, pos.lon, color, `SocketID: ${socketId}<br>IP: ${info.ip || "-"}`);
|
@@ -161,7 +157,14 @@
|
|
161 |
}
|
162 |
}
|
163 |
|
164 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
const sessionsToStore = {};
|
166 |
for(const [socketId, info] of Object.entries(userSessions)) {
|
167 |
sessionsToStore[socketId] = {
|
@@ -174,10 +177,22 @@
|
|
174 |
previousSessions = sessionsToStore;
|
175 |
});
|
176 |
|
177 |
-
//
|
178 |
-
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
</script>
|
182 |
</body>
|
183 |
</html>
|
|
|
12 |
src="https://unpkg.com/[email protected]/dist/leaflet.js"
|
13 |
></script>
|
14 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
#map {
|
16 |
height: 400px;
|
17 |
width: 100%;
|
|
|
18 |
border: 1px solid #ccc;
|
19 |
+
margin-bottom: 20px;
|
20 |
+
}
|
21 |
+
table {
|
22 |
+
border-collapse: collapse;
|
23 |
+
width: 100%;
|
24 |
+
margin-bottom: 20px;
|
25 |
+
}
|
26 |
+
th, td {
|
27 |
+
border: 1px solid #ccc;
|
28 |
+
padding: 8px;
|
29 |
+
text-align: left;
|
30 |
+
}
|
31 |
+
th {
|
32 |
+
background-color: #eee;
|
33 |
+
}
|
34 |
+
.highlight {
|
35 |
+
background-color: #f99;
|
36 |
+
transition: background-color 1s ease;
|
37 |
}
|
38 |
</style>
|
39 |
</head>
|
40 |
<body>
|
41 |
+
<div id="map"></div>
|
42 |
+
|
43 |
<h1>現在の接続ユーザー数: <span id="userCount">0</span></h1>
|
44 |
<div>更新はページ再読み込みで行います。</div>
|
45 |
<table>
|
|
|
54 |
<tbody id="userTableBody"></tbody>
|
55 |
</table>
|
56 |
|
|
|
|
|
57 |
<script>
|
58 |
const socket = io("https://web-socket-server-14ap.onrender.com/");
|
59 |
|
60 |
const STORAGE_KEY = "userSessions";
|
61 |
|
|
|
62 |
let previousSessions = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
|
63 |
|
64 |
const userCountElem = document.getElementById("userCount");
|
65 |
const tbody = document.getElementById("userTableBody");
|
|
|
66 |
|
67 |
+
// Leafletマップ初期化(東京近辺)
|
68 |
+
const map = L.map('map').setView([35.681236, 139.767125], 5);
|
69 |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
70 |
maxZoom: 18,
|
71 |
attribution: '© OpenStreetMap contributors'
|
72 |
}).addTo(map);
|
73 |
|
|
|
74 |
let markers = {};
|
75 |
|
76 |
+
// --- 簡易テスト用IP座標返し関数 ---
|
77 |
async function fetchGeo(ip) {
|
78 |
+
const knownIPs = {
|
79 |
+
"8.8.8.8": {lat: 37.386, lon: -122.0838}, // Google DNS
|
80 |
+
"1.1.1.1": {lat: -33.4940, lon: 143.2104} // Cloudflare DNS
|
81 |
+
};
|
82 |
+
return knownIPs[ip] || null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
}
|
84 |
+
// ----------------------------------
|
85 |
|
|
|
86 |
function createCircleMarker(lat, lon, color, popupText) {
|
87 |
return L.circleMarker([lat, lon], {
|
88 |
+
radius: 10,
|
89 |
fillColor: color,
|
90 |
color: '#000',
|
91 |
weight: 1,
|
92 |
opacity: 1,
|
93 |
+
fillOpacity: 0.9
|
94 |
}).bindPopup(popupText);
|
95 |
}
|
96 |
|
|
|
99 |
});
|
100 |
|
101 |
socket.on("userSessionsUpdate", async (userSessions) => {
|
102 |
+
console.log("userSessionsUpdate受信", userSessions);
|
103 |
+
|
104 |
tbody.innerHTML = "";
|
105 |
|
106 |
+
// 既存マーカーを削除
|
107 |
for(let key in markers) {
|
108 |
map.removeLayer(markers[key]);
|
109 |
}
|
110 |
markers = {};
|
111 |
|
112 |
+
// IPごとの位置情報を取得
|
113 |
const positions = {};
|
114 |
await Promise.all(Object.entries(userSessions).map(async ([socketId, info]) => {
|
115 |
positions[socketId] = await fetchGeo(info.ip);
|
|
|
142 |
|
143 |
tbody.appendChild(tr);
|
144 |
|
|
|
145 |
const pos = positions[socketId];
|
146 |
if(pos) {
|
|
|
147 |
const prevPos = previousSessions[socketId]?.geoPosition;
|
148 |
let changedPos = true;
|
149 |
if(prevPos && Math.abs(prevPos.lat - pos.lat) < 0.0001 && Math.abs(prevPos.lon - pos.lon) < 0.0001) {
|
150 |
changedPos = false;
|
151 |
}
|
|
|
|
|
152 |
const color = changedPos ? 'red' : 'black';
|
153 |
|
154 |
const marker = createCircleMarker(pos.lat, pos.lon, color, `SocketID: ${socketId}<br>IP: ${info.ip || "-"}`);
|
|
|
157 |
}
|
158 |
}
|
159 |
|
160 |
+
// マーカー全体を囲んで地図を移動
|
161 |
+
const markerPositions = Object.values(markers).map(m => m.getLatLng());
|
162 |
+
if(markerPositions.length > 0) {
|
163 |
+
const group = L.featureGroup(Object.values(markers));
|
164 |
+
map.fitBounds(group.getBounds().pad(0.5));
|
165 |
+
}
|
166 |
+
|
167 |
+
// localStorageに保存
|
168 |
const sessionsToStore = {};
|
169 |
for(const [socketId, info] of Object.entries(userSessions)) {
|
170 |
sessionsToStore[socketId] = {
|
|
|
177 |
previousSessions = sessionsToStore;
|
178 |
});
|
179 |
|
180 |
+
// テスト用に手動発火
|
181 |
+
window.addEventListener("load", () => {
|
182 |
+
const testData = {
|
183 |
+
"test-socket-1": {
|
184 |
+
ip: "8.8.8.8",
|
185 |
+
connectTime: Date.now(),
|
186 |
+
disconnectTime: null
|
187 |
+
},
|
188 |
+
"test-socket-2": {
|
189 |
+
ip: "1.1.1.1",
|
190 |
+
connectTime: Date.now(),
|
191 |
+
disconnectTime: null
|
192 |
+
}
|
193 |
+
};
|
194 |
+
socket.emit("userSessionsUpdate", testData);
|
195 |
+
});
|
196 |
</script>
|
197 |
</body>
|
198 |
</html>
|