File size: 5,734 Bytes
47f5399
 
 
3436304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47f5399
 
3436304
bc22a6c
3436304
 
 
47f5399
3436304
47f5399
 
 
 
3436304
 
 
 
 
47f5399
 
3436304
47f5399
3436304
1557dea
 
 
 
 
3436304
47f5399
4bf0c24
47f5399
 
 
 
 
bc22a6c
b31641d
3436304
 
b31641d
 
4bf0c24
b31641d
4bf0c24
3436304
 
 
 
 
 
 
 
b31641d
47f5399
b31641d
3436304
b31641d
 
3436304
 
4bf0c24
3436304
045586e
3436304
 
4bf0c24
3436304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bf0c24
37b203b
3436304
 
 
 
045586e
3436304
 
 
 
47f5399
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
<!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 previousSessions = 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) => {
        console.log("userSessionsUpdate受信", userSessions);

        tbody.innerHTML = "";

        for (let key in markers) {
            map.removeLayer(markers[key]);
        }
        markers = {};

        for (const [socketId, info] of Object.entries(userSessions)) {
            const tr = document.createElement("tr");

            function createTd(text, elementKey) {
                const td = document.createElement("td");
                td.textContent = text;

                const prevValue = previousSessions[socketId] ? previousSessions[socketId][elementKey] : undefined;
                const currValue = info[elementKey];

                const changed = prevValue !== currValue;

                if (changed) {
                    td.classList.add("highlight");
                    setTimeout(() => td.classList.remove("highlight"), 1000);
                }

                return td;
            }

            tr.appendChild(createTd(socketId, "socketId"));
            tr.appendChild(createTd(info.ip || "-", "ip"));
            tr.appendChild(createTd(info.connectTime ? new Date(info.connectTime).toLocaleString() : "-", "connectTime"));
            tr.appendChild(createTd(info.disconnectTime ? new Date(info.disconnectTime).toLocaleString() : "-", "disconnectTime"));
            tr.appendChild(createTd(info.loc || "-", "loc"));
            tr.appendChild(createTd(info.city || "-", "city"));
            tr.appendChild(createTd(info.region || "-", "region"));
            tr.appendChild(createTd(info.country || "-", "country"));

            tbody.appendChild(tr);

            // 位置情報がある場合、地図にマーカーを追加
            if (info.loc) {
                const [lat, lon] = info.loc.split(',').map(Number);
                const prevLoc = previousSessions[socketId]?.loc;
                const changedPos = prevLoc !== info.loc;
                const color = changedPos ? 'red' : 'blue';

                const locationText = [
                    `SocketID: ${socketId}`,
                    `IP: ${info.ip || "-"}`,
                    `都市: ${info.city || "-"}`,
                    `地域: ${info.region || "-"}`,
                    `国: ${info.country || "-"}`
                ].join('<br>');

                const marker = createCircleMarker(lat, lon, color, 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(userSessions));
        previousSessions = userSessions;
    });

</script>
</body>
</html>