Code cleanup and optimization - removed debug logs and comments
This commit is contained in:
195
README.md
195
README.md
@@ -0,0 +1,195 @@
|
|||||||
|
# Dashboard Backend
|
||||||
|
|
||||||
|
Real-time cryptocurrency price streaming server built with Express.js and Socket.IO
|
||||||
|
|
||||||
|
## 🎯 Features
|
||||||
|
|
||||||
|
- ✅ Real-time cryptocurrency prices via WebSocket
|
||||||
|
- ✅ CoinGecko API integration (free, no API key needed)
|
||||||
|
- ✅ Automatic data refresh every 5 seconds
|
||||||
|
- ✅ CORS enabled for cross-origin requests
|
||||||
|
- ✅ Connection tracking and client count broadcasting
|
||||||
|
- ✅ Graceful error handling
|
||||||
|
- ✅ Automatic reconnection support
|
||||||
|
|
||||||
|
## 🛠 Tech Stack
|
||||||
|
|
||||||
|
- **Framework**: Express.js v4.18+
|
||||||
|
- **Real-time**: Socket.IO v4.5+
|
||||||
|
- **HTTP Client**: Axios v1.5+
|
||||||
|
- **Environment**: Dotenv v16+
|
||||||
|
- **CORS**: CORS middleware v2.8+
|
||||||
|
|
||||||
|
## 📋 Prerequisites
|
||||||
|
|
||||||
|
- Node.js v16.0.0 or higher
|
||||||
|
- npm v8.0.0 or higher
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Environment Setup
|
||||||
|
|
||||||
|
Create a `.env` file:
|
||||||
|
|
||||||
|
```env
|
||||||
|
PORT=5000
|
||||||
|
NODE_ENV=development
|
||||||
|
CORS_ORIGIN=http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Start Development Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Server will run on `http://localhost:5000`
|
||||||
|
|
||||||
|
### 4. Start Production Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📡 WebSocket Events
|
||||||
|
|
||||||
|
### Client → Server
|
||||||
|
|
||||||
|
**requestData**
|
||||||
|
- Payload: `{ dataType: 'crypto' }`
|
||||||
|
- Description: Client requests crypto data
|
||||||
|
|
||||||
|
### Server → Client
|
||||||
|
|
||||||
|
**data**
|
||||||
|
- Payload: Real-time cryptocurrency data
|
||||||
|
- Emitted: Every 5 seconds
|
||||||
|
|
||||||
|
**clientCount**
|
||||||
|
- Payload: `{ count: number }`
|
||||||
|
- Description: Total connected clients
|
||||||
|
|
||||||
|
**error**
|
||||||
|
- Payload: `{ message: string, details: string }`
|
||||||
|
- Description: Error occurred
|
||||||
|
|
||||||
|
## 📊 Data Response Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "crypto",
|
||||||
|
"timestamp": "2025-10-28T12:00:00.000Z",
|
||||||
|
"prices": {
|
||||||
|
"bitcoin": {
|
||||||
|
"usd": 42500.50,
|
||||||
|
"usd_market_cap": 850000000000,
|
||||||
|
"usd_24h_vol": 25000000000,
|
||||||
|
"usd_24h_change": 2.5
|
||||||
|
},
|
||||||
|
"ethereum": {
|
||||||
|
"usd": 2300.75,
|
||||||
|
"usd_market_cap": 280000000000,
|
||||||
|
"usd_24h_vol": 15000000000,
|
||||||
|
"usd_24h_change": 1.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Supported Cryptocurrencies
|
||||||
|
|
||||||
|
- Bitcoin (BTC)
|
||||||
|
- Ethereum (ETH)
|
||||||
|
- Cardano (ADA)
|
||||||
|
- Solana (SOL)
|
||||||
|
- Ripple (XRP)
|
||||||
|
|
||||||
|
## 🔒 Security
|
||||||
|
|
||||||
|
- Environment variables for sensitive data
|
||||||
|
- CORS restrictions configurable
|
||||||
|
- Input validation on events
|
||||||
|
- Error messages sanitized
|
||||||
|
- No sensitive data exposed
|
||||||
|
|
||||||
|
## 🐛 Debugging
|
||||||
|
|
||||||
|
### Enable Verbose Logging
|
||||||
|
|
||||||
|
The server logs all important events:
|
||||||
|
- Client connections/disconnections
|
||||||
|
- Data requests
|
||||||
|
- API calls
|
||||||
|
- Errors
|
||||||
|
|
||||||
|
Check console output for troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Port Already in Use**
|
||||||
|
```bash
|
||||||
|
# Mac/Linux
|
||||||
|
lsof -i :5000
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
netstat -ano | findstr :5000
|
||||||
|
```
|
||||||
|
|
||||||
|
**CORS Errors**
|
||||||
|
- Check CORS_ORIGIN in .env matches frontend URL
|
||||||
|
- Verify frontend is running on http://localhost:3000
|
||||||
|
|
||||||
|
**API Not Responding**
|
||||||
|
- Check internet connection
|
||||||
|
- CoinGecko API may be rate-limited
|
||||||
|
- Server will auto-retry
|
||||||
|
|
||||||
|
## 📈 Performance
|
||||||
|
|
||||||
|
- Lightweight and fast
|
||||||
|
- Handles multiple concurrent connections
|
||||||
|
- Efficient data updates (5-second intervals)
|
||||||
|
- Minimal bandwidth usage
|
||||||
|
|
||||||
|
## 🚀 Deployment Ready
|
||||||
|
|
||||||
|
Tested and ready for deployment on:
|
||||||
|
- Railway.app ✅
|
||||||
|
- Render.com ✅
|
||||||
|
- Heroku ✅
|
||||||
|
- DigitalOcean ✅
|
||||||
|
- AWS ✅
|
||||||
|
|
||||||
|
## 📝 API Health Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "OK",
|
||||||
|
"message": "Server is running",
|
||||||
|
"timestamp": "2025-10-28T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
ISC
|
||||||
|
|
||||||
|
## 👤 Author
|
||||||
|
|
||||||
|
CloudForge Dev Team
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: Production Ready
|
||||||
|
**Last Updated**: 2025-10-28
|
||||||
107
server.js
107
server.js
@@ -9,6 +9,10 @@ const app = express();
|
|||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
|
let cachedPrices = null;
|
||||||
|
let lastFetchTime = 0;
|
||||||
|
const CACHE_DURATION = 30000;
|
||||||
|
|
||||||
const io = socketIo(server, {
|
const io = socketIo(server, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: process.env.CORS_ORIGIN || "*",
|
origin: process.env.CORS_ORIGIN || "*",
|
||||||
@@ -34,63 +38,69 @@ app.get("/health", (req, res) => {
|
|||||||
|
|
||||||
// ========== Socket.IO Logic ==========
|
// ========== Socket.IO Logic ==========
|
||||||
|
|
||||||
// 用於追蹤連接的客戶端數量
|
// Number of clients
|
||||||
let connectedClients = 0;
|
let connectedClients = 0;
|
||||||
|
|
||||||
// 當客戶端連接時觸發
|
// Trigger when client connected
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
// 增加客戶端計數
|
|
||||||
connectedClients++;
|
connectedClients++;
|
||||||
|
|
||||||
console.log(`✅ 客戶端已連接: ${socket.id}`);
|
console.log(`✅ Client Connected: ${socket.id}`);
|
||||||
console.log(`📊 當前連接數: ${connectedClients}`);
|
console.log(`📊 Current Number of Clients: ${connectedClients}`);
|
||||||
|
|
||||||
// 向所有客戶端廣播當前連接數
|
// Broadcast to all clients
|
||||||
io.emit("clientCount", connectedClients);
|
io.emit("clientCount", connectedClients);
|
||||||
|
|
||||||
// ========== 監聽客戶端事件 ==========
|
// ========== Monitoring client side events ==========
|
||||||
|
|
||||||
// 當客戶端要求數據時
|
// Request Data
|
||||||
socket.on("requestData", async (dataType) => {
|
socket.on("requestData", async (dataType) => {
|
||||||
console.log(`📤 數據請求: ${dataType}`);
|
console.log(`📤 Request Data: ${dataType}`);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
if (dataType === "crypto") {
|
if (dataType === "crypto") {
|
||||||
// 從 CoinGecko API 獲取加密貨幣數據
|
if (cachedPrices && now - lastFetchTime < CACHE_DURATION) {
|
||||||
|
console.log("📦 Use caching data");
|
||||||
|
data = cachedPrices;
|
||||||
|
} else {
|
||||||
|
// Get data from "CoinGecko API"
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
"https://api.coingecko.com/api/v3/simple/price",
|
"https://api.coingecko.com/api/v3/simple/price",
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
// 要查詢的加密貨幣
|
// list of crypto requested to be shown
|
||||||
ids: "bitcoin,ethereum,cardano,solana,ripple",
|
ids: "bitcoin,ethereum,cardano,solana,ripple",
|
||||||
// 使用 USD 幣種
|
|
||||||
vs_currencies: "usd",
|
vs_currencies: "usd",
|
||||||
// 包含市值
|
|
||||||
include_market_cap: true,
|
include_market_cap: true,
|
||||||
// 包含24小時交易量
|
|
||||||
include_24hr_vol: true,
|
include_24hr_vol: true,
|
||||||
// 包含24小時漲跌幅
|
|
||||||
include_24hr_change: true,
|
include_24hr_change: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 組織數據格式
|
// Construct data format
|
||||||
data = {
|
data = {
|
||||||
type: "crypto",
|
type: "crypto",
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
prices: response.data,
|
prices: response.data,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update Caches
|
||||||
|
cachedPrices = data;
|
||||||
|
lastFetchTime = now;
|
||||||
|
console.log("✅ Cached New Data");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 發送數據給要求的客戶端
|
// Emit data to client side
|
||||||
socket.emit("data", data);
|
socket.emit("data", data);
|
||||||
console.log(`✅ 數據已發送給客戶端: ${socket.id}`);
|
console.log(`✅ Data sent to client: ${socket.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 獲取數據出錯:", error.message);
|
console.error("❌ Error on Getting Data:", error.message);
|
||||||
// 發送錯誤信息給客戶端
|
// Emit Error message to client side
|
||||||
socket.emit("error", {
|
socket.emit("error", {
|
||||||
message: "Failed to fetch data",
|
message: "Failed to fetch data",
|
||||||
details: error.message,
|
details: error.message,
|
||||||
@@ -98,12 +108,19 @@ io.on("connection", (socket) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========== 定期推送數據 ==========
|
// ========== Push Data by interval ==========
|
||||||
|
|
||||||
// 創建定時器,每5秒推送一次數據給該客戶端
|
// Push every 5 seconds
|
||||||
const dataInterval = setInterval(async () => {
|
const dataInterval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
// 獲取最新的加密貨幣數據
|
const now = new Date();
|
||||||
|
|
||||||
|
if (cachedPrices && now - lastFetchTime < CACHE_DURATION) {
|
||||||
|
socket.emit("data", cachedPrices);
|
||||||
|
console.log(`[${now.toISOString()}]📦 Push Cached Data`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get latest data
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
"https://api.coingecko.com/api/v3/simple/price",
|
"https://api.coingecko.com/api/v3/simple/price",
|
||||||
{
|
{
|
||||||
@@ -117,57 +134,59 @@ io.on("connection", (socket) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 向該客戶端發送數據(實時更新)
|
const data = {
|
||||||
socket.emit("data", {
|
|
||||||
type: "crypto",
|
type: "crypto",
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
prices: response.data,
|
prices: response.data,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
cachedPrices = data;
|
||||||
|
lastFetchTime = now;
|
||||||
|
|
||||||
|
socket.emit("data", data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 定時推送出錯:", error.message);
|
console.error("❌ Error on pushing:", error.message);
|
||||||
}
|
}
|
||||||
}, 5000); // 每5000毫秒(5秒)執行一次
|
}, CACHE_DURATION); // run every 30 seconds
|
||||||
|
|
||||||
// ========== 客戶端斷開連接 ==========
|
// ========== Client Disconnected ==========
|
||||||
|
|
||||||
// 當客戶端斷開連接時觸發
|
// Trigger when client disconnected
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
connectedClients--;
|
connectedClients--;
|
||||||
console.log(`❌ 客戶端已斷開: ${socket.id}`);
|
console.log(`❌ Client Disconnected: ${socket.id}`);
|
||||||
console.log(`📊 當前連接數: ${connectedClients}`);
|
console.log(`📊 Current Number of Clients: ${connectedClients}`);
|
||||||
|
|
||||||
// 向所有客戶端廣播更新的連接數
|
// Broadcast number of clients
|
||||||
io.emit("clientCount", connectedClients);
|
io.emit("clientCount", connectedClients);
|
||||||
|
|
||||||
// 清除該客戶端的定時器
|
// Clear client's interval
|
||||||
clearInterval(dataInterval);
|
clearInterval(dataInterval);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========== 啟動服務器 ==========
|
// ========== Start service ==========
|
||||||
|
|
||||||
// 從環境變數獲取端口,或使用默認5000
|
|
||||||
const PORT = process.env.PORT || 5000;
|
const PORT = process.env.PORT || 5000;
|
||||||
|
|
||||||
// 啟動伺服器
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log("");
|
console.log("");
|
||||||
console.log("═══════════════════════════════════════");
|
console.log("═══════════════════════════════════════");
|
||||||
console.log(`✅ 服務器運行在: http://localhost:${PORT}`);
|
console.log(`✅ Server running on: http://localhost:${PORT}`);
|
||||||
console.log("📡 WebSocket 已準備接收連接");
|
console.log("📡 WebSocket ready to connect");
|
||||||
console.log("🔗 CORS 來源:", process.env.CORS_ORIGIN || "*");
|
console.log("🔗 CORS Source:", process.env.CORS_ORIGIN || "*");
|
||||||
console.log("═══════════════════════════════════════");
|
console.log("═══════════════════════════════════════");
|
||||||
console.log("");
|
console.log("");
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========== 錯誤處理 ==========
|
// ========== Error Handling ==========
|
||||||
|
|
||||||
// 處理未捕獲的異常
|
// Exception Handling
|
||||||
process.on("unhandledRejection", (err) => {
|
process.on("unhandledRejection", (err) => {
|
||||||
console.error("❌ 未捕獲的異常:", err);
|
console.error("❌ Exceptions:", err);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("uncaughtException", (err) => {
|
process.on("uncaughtException", (err) => {
|
||||||
console.error("❌ 未捕獲的異常:", err);
|
console.error("❌ Exceptions:", err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user