Created
September 24, 2024 19:51
-
-
Save MuhammadSawalhy/cc8f5dc0c83059ff9bfa4ed196ceca19 to your computer and use it in GitHub Desktop.
Server Sent Events Demo
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 characters
<!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 characters
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}`); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment