initial commit

This commit is contained in:
xiaomai
2025-10-07 11:59:55 +08:00
commit 03776ee64a
9 changed files with 468 additions and 0 deletions

20
server/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# 使用 Node.js 官方镜像
FROM node:20-alpine
# 设置工作目录
WORKDIR /app
# 拷贝依赖清单
COPY package*.json ./
# 安装依赖(只安装生产依赖即可)
RUN npm install --production
# 拷贝源码
COPY . .
# 暴露 WebSocket 端口
EXPOSE 8080
# 启动命令
CMD ["node", "index.js"]

80
server/index.js Normal file
View File

@@ -0,0 +1,80 @@
import { WebSocketServer } from "ws";
import Redis from "ioredis";
const redisSub = new Redis({
host: process.env.REDIS_HOST || "main_redis",
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD || "",
});
const redisPub = new Redis({
host: process.env.REDIS_HOST || "main_redis",
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD || "",
});
const PORT = 8080;
const wss = new WebSocketServer({ port: PORT });
const clients = new Map(); // ws => { app, channels: Set }
function parseMessage(msg) {
try {
return JSON.parse(msg);
} catch {
return null;
}
}
wss.on("connection", (ws) => {
console.log("👤 新客户端连接");
clients.set(ws, { app: null, channels: new Set() });
ws.on("message", async (raw) => {
const msg = parseMessage(raw);
if (!msg) return;
const { type, app, channel, event, data, token } = msg;
if (type === "auth") {
clients.get(ws).app = app;
ws.send(JSON.stringify({ type: "auth_ok", app }));
return;
}
if (type === "subscribe") {
clients.get(ws).channels.add(channel);
ws.send(JSON.stringify({ type: "subscribed", channel }));
return;
}
if (type === "publish") {
const payload = { app, channel, event, data };
await redisPub.publish(channel, JSON.stringify(payload));
return;
}
});
ws.on("close", () => {
clients.delete(ws);
console.log("❌ 客户端断开");
});
});
// Redis 订阅所有频道
redisSub.psubscribe("*", (err) => {
if (err) console.error("Redis 订阅失败", err);
});
// 分发消息
redisSub.on("pmessage", (pattern, channel, message) => {
const payload = JSON.parse(message);
for (const [ws, info] of clients) {
if (info.channels.has(channel)) {
ws.send(JSON.stringify(payload));
}
}
});
console.log(`✅ WebSocket 服务运行在 ws://0.0.0.0:${PORT}`);

18
server/package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "ws.tootaio.com",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.17.0",
"dependencies": {
"ioredis": "^5.7.0",
"ws": "^8.18.3"
}
}