soiz1 commited on
Commit
73d36eb
·
verified ·
1 Parent(s): 3f9c588

Create index.js

Browse files
Files changed (1) hide show
  1. index.js +122 -0
index.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Server } = require("socket.io");
2
+
3
+ // Node.js 18+ならfetchは標準搭載
4
+ const port = process.env.PORT || 3000;
5
+ const io = new Server(port, {
6
+ cors: {
7
+ origin: "*",
8
+ }
9
+ });
10
+
11
+ let activeUsers = 0;
12
+ const userSessions = {}; // 現在のセッション
13
+ const userSessionHistory = []; // 過去のセッション履歴も含む
14
+
15
+ // プライベートIPアドレスかどうかを判定する関数
16
+ function isPrivateIP(ip) {
17
+ const parts = ip.split('.').map(part => parseInt(part, 10));
18
+ if (parts.length !== 4) return false;
19
+ const [a, b] = parts;
20
+
21
+ return (
22
+ a === 10 || // 10.0.0.0/8
23
+ (a === 172 && b >= 16 && b <= 31) || // 172.16.0.0/12
24
+ (a === 192 && b === 168) || // 192.168.0.0/16
25
+ a === 127 || // 127.0.0.1 (loopback)
26
+ a === 0 // 0.0.0.0 (unspecified)
27
+ );
28
+ }
29
+
30
+ async function fetchIpInfo(ip) {
31
+ try {
32
+ const controller = new AbortController();
33
+ const timeout = setTimeout(() => controller.abort(), 5000); // 5s timeout
34
+
35
+ const res = await fetch(`https://ipinfo.io/${ip}/json`, {
36
+ signal: controller.signal,
37
+ });
38
+
39
+ clearTimeout(timeout);
40
+
41
+ if (!res.ok) throw new Error(`HTTP error ${res.status}`);
42
+ return await res.json();
43
+ } catch (e) {
44
+ if (e.name === 'AbortError') {
45
+ console.error("IP info fetch timed out for IP:", ip);
46
+ } else {
47
+ console.error("IP info fetch failed:", e);
48
+ }
49
+ return null;
50
+ }
51
+ }
52
+
53
+ io.on("connection", async (socket) => {
54
+ if (socket.handshake.query.isAdmin === "true") {
55
+ // 管理画面接続時に過去履歴を送信
56
+ socket.emit("userSessionHistory", userSessionHistory);
57
+
58
+ // 現在の接続数と現在のセッションも送信
59
+ socket.emit("updateUserCount", activeUsers);
60
+ socket.emit("userSessionsUpdate", userSessions);
61
+
62
+ return;
63
+ }
64
+
65
+ console.log(`New connection from ${socket.id}, isAdmin: ${socket.handshake.query.isAdmin}`);
66
+
67
+ activeUsers++;
68
+
69
+ // IPアドレスを取得
70
+ let ips = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address;
71
+ if (typeof ips === 'string') {
72
+ ips = ips.split(',').map(ip => ip.trim());
73
+ } else {
74
+ ips = [ips];
75
+ }
76
+
77
+ const ipString = ips.join(', '); // 全IPをカンマ区切りで保存
78
+ const connectTime = new Date();
79
+
80
+ userSessions[socket.id] = {
81
+ socketId: socket.id,
82
+ ip: ipString, // 全部のIPアドレス
83
+ connectTime,
84
+ disconnectTime: null,
85
+ loc: null,
86
+ city: null,
87
+ region: null,
88
+ country: null,
89
+ };
90
+
91
+ // 最初の公開IPだけをAPIに渡す
92
+ const firstPublicIp = ips.find(ip => ip && !isPrivateIP(ip));
93
+ if (firstPublicIp) {
94
+ const ipInfo = await fetchIpInfo(firstPublicIp);
95
+ if (ipInfo) {
96
+ userSessions[socket.id].loc = ipInfo.loc || null;
97
+ userSessions[socket.id].city = ipInfo.city || null;
98
+ userSessions[socket.id].region = ipInfo.region || null;
99
+ userSessions[socket.id].country = ipInfo.country || null;
100
+ }
101
+ }
102
+
103
+ io.emit("updateUserCount", activeUsers);
104
+ io.emit("userSessionsUpdate", userSessions);
105
+
106
+ socket.on("disconnect", () => {
107
+ activeUsers--;
108
+ const disconnectTime = new Date();
109
+
110
+ if (userSessions[socket.id]) {
111
+ userSessions[socket.id].disconnectTime = disconnectTime;
112
+
113
+ // 履歴にも保存
114
+ userSessionHistory.push({ ...userSessions[socket.id] });
115
+
116
+ // ※ 現在のセッションは保持しておく
117
+ }
118
+
119
+ io.emit("updateUserCount", activeUsers);
120
+ io.emit("userSessionsUpdate", userSessions);
121
+ });
122
+ });