Skip to main content
Glama
server.md11 kB
```typescript import { Server, Socket } from "socket.io"; import { createServer } from "http"; import express from "express"; // ============================================ // Types // ============================================ interface User { id: string; username: string; socketId: string; rooms: Set<string>; status: "online" | "away" | "busy"; } interface Message { id: string; roomId: string; userId: string; username: string; content: string; timestamp: Date; type: "text" | "system" | "file"; } interface Room { id: string; name: string; createdBy: string; members: Set<string>; isPrivate: boolean; createdAt: Date; } // Server -> Client events interface ServerToClientEvents { "user:joined": (data: { userId: string; username: string; roomId: string; }) => void; "user:left": (data: { userId: string; username: string; roomId: string; }) => void; "user:status": (data: { userId: string; status: User["status"] }) => void; "message:new": (message: Message) => void; "message:typing": (data: { userId: string; username: string; roomId: string; }) => void; "room:created": (room: { id: string; name: string }) => void; "room:members": (data: { roomId: string; members: { id: string; username: string }[]; }) => void; error: (data: { message: string; code: string }) => void; } // Client -> Server events interface ClientToServerEvents { "user:login": ( data: { username: string }, callback: (response: { userId: string }) => void, ) => void; "user:status": (data: { status: User["status"] }) => void; "room:join": (data: { roomId: string }) => void; "room:leave": (data: { roomId: string }) => void; "room:create": ( data: { name: string; isPrivate?: boolean }, callback: (response: { roomId: string }) => void, ) => void; "message:send": (data: { roomId: string; content: string; type?: Message["type"]; }) => void; "message:typing": (data: { roomId: string }) => void; } // ============================================ // Data Store // ============================================ class DataStore { users = new Map<string, User>(); rooms = new Map<string, Room>(); messages = new Map<string, Message[]>(); // roomId -> messages private nextUserId = 1; private nextMessageId = 1; constructor() { // Create default room this.createRoom("general", "system", false); } createUser(username: string, socketId: string): User { const user: User = { id: `user-${this.nextUserId++}`, username, socketId, rooms: new Set(), status: "online", }; this.users.set(user.id, user); return user; } getUserBySocketId(socketId: string): User | undefined { return Array.from(this.users.values()).find((u) => u.socketId === socketId); } removeUser(socketId: string): User | undefined { const user = this.getUserBySocketId(socketId); if (user) { this.users.delete(user.id); } return user; } createRoom(name: string, createdBy: string, isPrivate: boolean): Room { const id = name.toLowerCase().replace(/\s+/g, "-"); const room: Room = { id, name, createdBy, members: new Set(), isPrivate, createdAt: new Date(), }; this.rooms.set(id, room); this.messages.set(id, []); return room; } addMessage( roomId: string, userId: string, username: string, content: string, type: Message["type"] = "text", ): Message { const message: Message = { id: `msg-${this.nextMessageId++}`, roomId, userId, username, content, timestamp: new Date(), type, }; const messages = this.messages.get(roomId) ?? []; messages.push(message); this.messages.set(roomId, messages); return message; } getRecentMessages(roomId: string, limit = 50): Message[] { const messages = this.messages.get(roomId) ?? []; return messages.slice(-limit); } getRoomMembers(roomId: string): User[] { const room = this.rooms.get(roomId); if (!room) return []; return Array.from(room.members) .map((id) => this.users.get(id)) .filter((u): u is User => u !== undefined); } } const store = new DataStore(); // ============================================ // Server Setup // ============================================ const app = express(); const httpServer = createServer(app); const io = new Server<ClientToServerEvents, ServerToClientEvents>(httpServer, { cors: { origin: process.env.CORS_ORIGIN || "*", methods: ["GET", "POST"], }, pingTimeout: 60000, pingInterval: 25000, }); // ============================================ // Middleware // ============================================ io.use((socket, next) => { // Add authentication here if needed const token = socket.handshake.auth.token; if (token) { // Verify token in production socket.data.authenticated = true; } next(); }); // ============================================ // Connection Handler // ============================================ io.on( "connection", (socket: Socket<ClientToServerEvents, ServerToClientEvents>) => { console.log(`Socket connected: ${socket.id}`); // User Login socket.on("user:login", ({ username }, callback) => { const user = store.createUser(username, socket.id); socket.data.userId = user.id; socket.data.username = username; // Auto-join general room const generalRoom = store.rooms.get("general"); if (generalRoom) { socket.join("general"); user.rooms.add("general"); generalRoom.members.add(user.id); // Notify room socket.to("general").emit("user:joined", { userId: user.id, username, roomId: "general", }); // Send system message const systemMsg = store.addMessage( "general", "system", "System", `${username} joined the chat`, "system", ); io.to("general").emit("message:new", systemMsg); } callback({ userId: user.id }); }); // Update Status socket.on("user:status", ({ status }) => { const user = store.getUserBySocketId(socket.id); if (!user) return; user.status = status; // Notify all rooms user is in user.rooms.forEach((roomId) => { socket.to(roomId).emit("user:status", { userId: user.id, status }); }); }); // Join Room socket.on("room:join", ({ roomId }) => { const user = store.getUserBySocketId(socket.id); const room = store.rooms.get(roomId); if (!user || !room) { socket.emit("error", { message: "Invalid room", code: "INVALID_ROOM" }); return; } socket.join(roomId); user.rooms.add(roomId); room.members.add(user.id); // Notify room socket.to(roomId).emit("user:joined", { userId: user.id, username: user.username, roomId, }); // Send room members const members = store .getRoomMembers(roomId) .map((m) => ({ id: m.id, username: m.username })); socket.emit("room:members", { roomId, members }); // Send recent messages const recentMessages = store.getRecentMessages(roomId); recentMessages.forEach((msg) => socket.emit("message:new", msg)); }); // Leave Room socket.on("room:leave", ({ roomId }) => { const user = store.getUserBySocketId(socket.id); const room = store.rooms.get(roomId); if (!user || !room) return; socket.leave(roomId); user.rooms.delete(roomId); room.members.delete(user.id); socket.to(roomId).emit("user:left", { userId: user.id, username: user.username, roomId, }); }); // Create Room socket.on("room:create", ({ name, isPrivate = false }, callback) => { const user = store.getUserBySocketId(socket.id); if (!user) { socket.emit("error", { message: "Not logged in", code: "UNAUTHORIZED", }); return; } const room = store.createRoom(name, user.id, isPrivate); // Auto-join creator socket.join(room.id); user.rooms.add(room.id); room.members.add(user.id); // Notify all (for public rooms) if (!isPrivate) { io.emit("room:created", { id: room.id, name: room.name }); } callback({ roomId: room.id }); }); // Send Message socket.on("message:send", ({ roomId, content, type = "text" }) => { const user = store.getUserBySocketId(socket.id); if (!user || !user.rooms.has(roomId)) { socket.emit("error", { message: "Not in room", code: "NOT_IN_ROOM" }); return; } const message = store.addMessage( roomId, user.id, user.username, content, type, ); io.to(roomId).emit("message:new", message); }); // Typing Indicator socket.on("message:typing", ({ roomId }) => { const user = store.getUserBySocketId(socket.id); if (!user || !user.rooms.has(roomId)) return; socket.to(roomId).emit("message:typing", { userId: user.id, username: user.username, roomId, }); }); // Disconnect socket.on("disconnect", (reason) => { console.log(`Socket disconnected: ${socket.id} - ${reason}`); const user = store.removeUser(socket.id); if (user) { user.rooms.forEach((roomId) => { const room = store.rooms.get(roomId); if (room) { room.members.delete(user.id); socket.to(roomId).emit("user:left", { userId: user.id, username: user.username, roomId, }); // System message const systemMsg = store.addMessage( roomId, "system", "System", `${user.username} left the chat`, "system", ); io.to(roomId).emit("message:new", systemMsg); } }); } }); }, ); // ============================================ // HTTP Endpoints // ============================================ app.get("/health", (_, res) => { res.json({ status: "healthy", connections: io.engine.clientsCount, rooms: store.rooms.size, users: store.users.size, }); }); app.get("/rooms", (_, res) => { const publicRooms = Array.from(store.rooms.values()) .filter((r) => !r.isPrivate) .map((r) => ({ id: r.id, name: r.name, members: r.members.size })); res.json(publicRooms); }); // ============================================ // Start Server // ============================================ const PORT = process.env.PORT || 3000; httpServer.listen(PORT, () => { console.log(`🔌 WebSocket Server: http://localhost:${PORT}`); }); export { io, store }; ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/millsydotdev/Code-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server