first commit

This commit is contained in:
2026-01-10 04:57:43 +00:00
parent 16a76a2cd6
commit 232968de1e
131 changed files with 43262 additions and 0 deletions

176
routes/websocket.js Normal file
View File

@@ -0,0 +1,176 @@
import { wsManager } from "../utils/websocket.js";
import { authenticate } from "../middleware/auth.js";
/**
* WebSocket routes and handlers
* @param {FastifyInstance} fastify - Fastify instance
*/
export default async function websocketRoutes(fastify, options) {
// WebSocket endpoint
fastify.get("/ws", { websocket: true }, (connection, request) => {
// In @fastify/websocket, the connection parameter IS the WebSocket directly
// It has properties like _socket, _readyState, etc.
wsManager.handleConnection(connection, request.raw || request);
});
// Get WebSocket stats (authenticated endpoint)
fastify.get(
"/ws/stats",
{
preHandler: authenticate,
},
async (request, reply) => {
const stats = {
totalConnections: wsManager.getTotalSocketCount(),
authenticatedUsers: wsManager.getAuthenticatedUserCount(),
userConnected: wsManager.isUserConnected(request.user._id.toString()),
};
return reply.send({
success: true,
stats: stats,
});
}
);
// Broadcast to all users (admin only - requires staff level 3+)
fastify.post(
"/ws/broadcast",
{
preHandler: [
authenticate,
async (request, reply) => {
if (request.user.staffLevel < 3) {
return reply.status(403).send({
error: "Forbidden",
message: "Insufficient permissions",
});
}
},
],
schema: {
body: {
type: "object",
required: ["type", "data"],
properties: {
type: { type: "string" },
data: { type: "object" },
excludeUsers: { type: "array", items: { type: "string" } },
},
},
},
},
async (request, reply) => {
try {
const { type, data, excludeUsers = [] } = request.body;
const count = wsManager.broadcastToAll(
{
type,
data,
timestamp: Date.now(),
},
excludeUsers
);
return reply.send({
success: true,
message: `Broadcast sent to ${count} clients`,
recipientCount: count,
});
} catch (error) {
console.error("Error broadcasting:", error);
return reply.status(500).send({
error: "InternalServerError",
message: "Failed to broadcast message",
});
}
}
);
// Send message to specific user (admin only)
fastify.post(
"/ws/send/:userId",
{
preHandler: [
authenticate,
async (request, reply) => {
if (request.user.staffLevel < 2) {
return reply.status(403).send({
error: "Forbidden",
message: "Insufficient permissions",
});
}
},
],
schema: {
body: {
type: "object",
required: ["type", "data"],
properties: {
type: { type: "string" },
data: { type: "object" },
},
},
},
},
async (request, reply) => {
try {
const { userId } = request.params;
const { type, data } = request.body;
const sent = wsManager.sendToUser(userId, {
type,
data,
timestamp: Date.now(),
});
if (!sent) {
return reply.status(404).send({
error: "NotFound",
message: "User not connected",
});
}
return reply.send({
success: true,
message: "Message sent to user",
});
} catch (error) {
console.error("Error sending message:", error);
return reply.status(500).send({
error: "InternalServerError",
message: "Failed to send message",
});
}
}
);
// Check if user is online
fastify.get(
"/ws/status/:userId",
{
preHandler: authenticate,
},
async (request, reply) => {
try {
const { userId } = request.params;
const isConnected = wsManager.isUserConnected(userId);
const metadata = wsManager.getUserMetadata(userId);
return reply.send({
success: true,
userId: userId,
online: isConnected,
metadata: metadata || null,
});
} catch (error) {
console.error("Error checking user status:", error);
return reply.status(500).send({
error: "InternalServerError",
message: "Failed to check user status",
});
}
}
);
}