Files
dashboard-backend/server.js
2025-11-05 17:13:12 +08:00

174 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});