File size: 5,333 Bytes
47f5399
 
 
3436304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47f5399
 
3436304
bc22a6c
3436304
 
 
47f5399
3436304
47f5399
 
 
 
3436304
 
 
 
 
47f5399
 
3436304
47f5399
3436304
7c730f1
 
 
b31641d
7c730f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47f5399
7c730f1
 
37b203b
7c730f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3436304
7c730f1
 
 
 
 
3436304
7c730f1
 
 
3436304
47f5399
3436304
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<!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>