Created
          September 24, 2024 19:51 
        
      - 
      
- 
        Save MuhammadSawalhy/cc8f5dc0c83059ff9bfa4ed196ceca19 to your computer and use it in GitHub Desktop. 
Revisions
- 
        MuhammadSawalhy created this gist Sep 24, 2024 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,97 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Chat App</title> </head> <body> <h1>Group Chat</h1> <div id="chat-box" style="height: 300px; overflow-y: auto; border: 1px solid black; padding: 10px;"></div> <div style="margin-top: 30px;"> <p id="username"></p> <form style="margin-top:10px" id="msg-form"> <input type="text" id="message-input" placeholder="Type your message"> <button>Send</button> </form> </div> <script> // Function to generate a random readable name const generateRandomName = () => { const adjectives = ["Brave", "Clever", "Witty", "Jolly", "Swift", "Bold", "Lucky"]; const animals = ["Lion", "Eagle", "Fox", "Bear", "Shark", "Wolf", "Hawk"]; const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; const animal = animals[Math.floor(Math.random() * animals.length)]; return `${adjective} ${animal}`; }; // Get or generate a readable username let username = localStorage.getItem("username"); if (!username) { username = generateRandomName(); localStorage.setItem("username", username); } // Display the username const usernameElement = document.getElementById("username"); usernameElement.textContent = `Hello, ${username}!`; // EventSource for SSE const eventSource = new EventSource("/stream"); // Handle incoming messages eventSource.onmessage = function (event) { const message = JSON.parse(event.data); displayMessage(message); }; // Display messages in the chat box function displayMessage(message) { // if the message is from the current user, align right if (message.username === username) { const chatBox = document.getElementById("chat-box"); const messageElem = document.createElement("p"); const time = new Date(message.timestamp).toLocaleTimeString(); messageElem.textContent = `[${time}] ${message.username}: ${message.message}`; messageElem.style.textAlign = "right"; chatBox.appendChild(messageElem); chatBox.scrollTop = chatBox.scrollHeight; return; } const chatBox = document.getElementById("chat-box"); const messageElem = document.createElement("p"); const time = new Date(message.timestamp).toLocaleTimeString(); messageElem.textContent = `[${time}] ${message.username}: ${message.message}`; chatBox.appendChild(messageElem); chatBox.scrollTop = chatBox.scrollHeight; } // Send message to the server document.getElementById("msg-form").addEventListener("submit", (e) => { e.preventDefault(); const messageInput = document.getElementById("message-input"); const message = messageInput.value; if (!message.trim()) return; // POST the message to the server fetch("/message", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ username, message }), }) .then(response => response.json()) .then(data => { if (data.status === "Message sent.") { messageInput.value = ""; // Clear input after sending } }); }); </script> </body> </html> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,83 @@ import express from "express"; import type { Request, Response } from "express"; import { v4 as uuidv4 } from "uuid"; const app = express(); const PORT = 3000; app.use(express.json()); type Message = { id: string; username: string; message: string; timestamp: number; }; // In-memory message store const messages: Message[] = []; // List of connected clients let clients: Response[] = []; // SSE connection route app.get("/stream", (req, res) => { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); // use Last-Event-ID to send the lost messages only const lastId = req.headers['Last-Event-ID']; if (lastId) { const startIndex = messages.findIndex(msg => msg.id === lastId); for (const msg of messages.slice(startIndex + 1)) sendSSE(res, msg); } else { // Send all previous messages to the new client for (const msg of messages) sendSSE(res, msg); } clients.push(res); // Remove client when the connection closes req.on("close", () => { clients = clients.filter((client) => client !== res); }); }); // POST new message app.post("/message", (req: Request, res: Response) => { const { username, message } = req.body; // Validate request if (!username || !message) { return res.status(400).json({ error: "Username and message are required." }); } const newMessage: Message = { id: uuidv4(), // Generate unique ID for each message username, message, timestamp: Date.now(), }; messages.push(newMessage); // Store the message in-memory // Broadcast message to all connected clients for (const clientRes of clients) sendSSE(clientRes, newMessage); return res.status(201).json({ status: "Message sent." }); }); // Helper function to send SSE function sendSSE(res: Response, message: Message) { res.write(`data: ${JSON.stringify(message)}\n\n`); } app.get("/", (req: Request, res: Response) => { res.sendFile("./chat.html", { root: __dirname }); }); // Start server app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });