File size: 3,418 Bytes
73d36eb dc1562a 73d36eb |
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 |
const { Server } = require("socket.io");
// Node.js 18+ならfetchは標準搭載
const port = 7860;
const io = new Server(port, {
cors: {
origin: "*",
}
});
let activeUsers = 0;
const userSessions = {}; // 現在のセッション
const userSessionHistory = []; // 過去のセッション履歴も含む
// プライベートIPアドレスかどうかを判定する関数
function isPrivateIP(ip) {
const parts = ip.split('.').map(part => parseInt(part, 10));
if (parts.length !== 4) return false;
const [a, b] = parts;
return (
a === 10 || // 10.0.0.0/8
(a === 172 && b >= 16 && b <= 31) || // 172.16.0.0/12
(a === 192 && b === 168) || // 192.168.0.0/16
a === 127 || // 127.0.0.1 (loopback)
a === 0 // 0.0.0.0 (unspecified)
);
}
async function fetchIpInfo(ip) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); // 5s timeout
const res = await fetch(`https://ipinfo.io/${ip}/json`, {
signal: controller.signal,
});
clearTimeout(timeout);
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
return await res.json();
} catch (e) {
if (e.name === 'AbortError') {
console.error("IP info fetch timed out for IP:", ip);
} else {
console.error("IP info fetch failed:", e);
}
return null;
}
}
io.on("connection", async (socket) => {
if (socket.handshake.query.isAdmin === "true") {
// 管理画面接続時に過去履歴を送信
socket.emit("userSessionHistory", userSessionHistory);
// 現在の接続数と現在のセッションも送信
socket.emit("updateUserCount", activeUsers);
socket.emit("userSessionsUpdate", userSessions);
return;
}
console.log(`New connection from ${socket.id}, isAdmin: ${socket.handshake.query.isAdmin}`);
activeUsers++;
// IPアドレスを取得
let ips = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address;
if (typeof ips === 'string') {
ips = ips.split(',').map(ip => ip.trim());
} else {
ips = [ips];
}
const ipString = ips.join(', '); // 全IPをカンマ区切りで保存
const connectTime = new Date();
userSessions[socket.id] = {
socketId: socket.id,
ip: ipString, // 全部のIPアドレス
connectTime,
disconnectTime: null,
loc: null,
city: null,
region: null,
country: null,
};
// 最初の公開IPだけをAPIに渡す
const firstPublicIp = ips.find(ip => ip && !isPrivateIP(ip));
if (firstPublicIp) {
const ipInfo = await fetchIpInfo(firstPublicIp);
if (ipInfo) {
userSessions[socket.id].loc = ipInfo.loc || null;
userSessions[socket.id].city = ipInfo.city || null;
userSessions[socket.id].region = ipInfo.region || null;
userSessions[socket.id].country = ipInfo.country || null;
}
}
io.emit("updateUserCount", activeUsers);
io.emit("userSessionsUpdate", userSessions);
socket.on("disconnect", () => {
activeUsers--;
const disconnectTime = new Date();
if (userSessions[socket.id]) {
userSessions[socket.id].disconnectTime = disconnectTime;
// 履歴にも保存
userSessionHistory.push({ ...userSessions[socket.id] });
// ※ 現在のセッションは保持しておく
}
io.emit("updateUserCount", activeUsers);
io.emit("userSessionsUpdate", userSessions);
});
}); |