const express = require("express"); const http = require("http"); const socketIo = require("socket.io"); const cors = require("cors"); const axios = require("axios"); require("dotenv").config(); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: process.env.CORS_ORIGIN || "*", methods: ["GET", "POST"], }, }); // ========== (Middleware) ========== app.use(cors()); app.use(express.json()); // ========== HTTP Route ========== app.get("/health", (req, res) => { res.json({ status: "OK", message: "Server is running", timestamp: new Date().toISOString(), }); }); // ========== Socket.IO Logic ========== // 用於追蹤連接的客戶端數量 let connectedClients = 0; // 當客戶端連接時觸發 io.on("connection", (socket) => { // 增加客戶端計數 connectedClients++; console.log(`✅ 客戶端已連接: ${socket.id}`); console.log(`📊 當前連接數: ${connectedClients}`); // 向所有客戶端廣播當前連接數 io.emit("clientCount", connectedClients); // ========== 監聽客戶端事件 ========== // 當客戶端要求數據時 socket.on("requestData", async (dataType) => { console.log(`📤 數據請求: ${dataType}`); try { let data; if (dataType === "crypto") { // 從 CoinGecko API 獲取加密貨幣數據 const response = await axios.get( "https://api.coingecko.com/api/v3/simple/price", { params: { // 要查詢的加密貨幣 ids: "bitcoin,ethereum,cardano,solana,ripple", // 使用 USD 幣種 vs_currencies: "usd", // 包含市值 include_market_cap: true, // 包含24小時交易量 include_24hr_vol: true, // 包含24小時漲跌幅 include_24hr_change: true, }, } ); // 組織數據格式 data = { type: "crypto", timestamp: new Date(), prices: response.data, }; } // 發送數據給要求的客戶端 socket.emit("data", data); console.log(`✅ 數據已發送給客戶端: ${socket.id}`); } catch (error) { console.error("❌ 獲取數據出錯:", error.message); // 發送錯誤信息給客戶端 socket.emit("error", { message: "Failed to fetch data", details: error.message, }); } }); // ========== 定期推送數據 ========== // 創建定時器,每5秒推送一次數據給該客戶端 const dataInterval = setInterval(async () => { try { // 獲取最新的加密貨幣數據 const response = await axios.get( "https://api.coingecko.com/api/v3/simple/price", { params: { ids: "bitcoin,ethereum,cardano,solana,ripple", vs_currencies: "usd", include_market_cap: true, include_24hr_vol: true, include_24hr_change: true, }, } ); // 向該客戶端發送數據(實時更新) socket.emit("data", { type: "crypto", timestamp: new Date(), prices: response.data, }); } catch (error) { console.error("❌ 定時推送出錯:", error.message); } }, 5000); // 每5000毫秒(5秒)執行一次 // ========== 客戶端斷開連接 ========== // 當客戶端斷開連接時觸發 socket.on("disconnect", () => { connectedClients--; console.log(`❌ 客戶端已斷開: ${socket.id}`); console.log(`📊 當前連接數: ${connectedClients}`); // 向所有客戶端廣播更新的連接數 io.emit("clientCount", connectedClients); // 清除該客戶端的定時器 clearInterval(dataInterval); }); }); // ========== 啟動服務器 ========== // 從環境變數獲取端口,或使用默認5000 const PORT = process.env.PORT || 5000; // 啟動伺服器 server.listen(PORT, () => { console.log(""); console.log("═══════════════════════════════════════"); console.log(`✅ 服務器運行在: http://localhost:${PORT}`); console.log("📡 WebSocket 已準備接收連接"); console.log("🔗 CORS 來源:", process.env.CORS_ORIGIN || "*"); console.log("═══════════════════════════════════════"); console.log(""); }); // ========== 錯誤處理 ========== // 處理未捕獲的異常 process.on("unhandledRejection", (err) => { console.error("❌ 未捕獲的異常:", err); }); process.on("uncaughtException", (err) => { console.error("❌ 未捕獲的異常:", err); process.exit(1); });