Added a weird little chat for shits and giggles
This commit is contained in:
85
routes/api/chat.ts
Normal file
85
routes/api/chat.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
|
||||
const chatConnections = new Set<WebSocket>();
|
||||
|
||||
// HTML sanitization
|
||||
function sanitizeText(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
// Process and sanitize message object
|
||||
function processChatMessage(message: string): string {
|
||||
try {
|
||||
const parsed = JSON.parse(message);
|
||||
|
||||
if (typeof parsed.text === "string") {
|
||||
parsed.text = sanitizeText(parsed.text.trim().slice(0, 2000));
|
||||
}
|
||||
|
||||
if (typeof parsed.sender === "string") {
|
||||
parsed.sender = sanitizeText(parsed.sender.trim().slice(0, 50));
|
||||
}
|
||||
|
||||
if (!parsed.timestamp) {
|
||||
parsed.timestamp = new Date().toISOString();
|
||||
}
|
||||
|
||||
return JSON.stringify(parsed);
|
||||
} catch (error) {
|
||||
console.error("Invalid message format:", error);
|
||||
return JSON.stringify({
|
||||
text: "Error: Invalid message format",
|
||||
sender: "System",
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast current user count to all clients
|
||||
function broadcastUserCount() {
|
||||
const count = chatConnections.size;
|
||||
const message = JSON.stringify({
|
||||
type: "user_count",
|
||||
count: count,
|
||||
});
|
||||
|
||||
for (const client of chatConnections) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const handler = (req: Request, _ctx: FreshContext): Response => {
|
||||
const { socket, response } = Deno.upgradeWebSocket(req);
|
||||
|
||||
socket.onopen = () => {
|
||||
chatConnections.add(socket);
|
||||
console.log(`New connection: ${chatConnections.size} users connected`);
|
||||
broadcastUserCount();
|
||||
};
|
||||
|
||||
// Handle messages
|
||||
socket.onmessage = (event) => {
|
||||
const sanitizedMessage = processChatMessage(event.data);
|
||||
|
||||
for (const client of chatConnections) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(sanitizedMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
chatConnections.delete(socket);
|
||||
console.log(`Connection closed: ${chatConnections.size} users connected`);
|
||||
broadcastUserCount();
|
||||
};
|
||||
|
||||
return response;
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
|
||||
// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/
|
||||
const JOKES = [
|
||||
"Why do Java developers often wear glasses? They can't C#.",
|
||||
"A SQL query walks into a bar, goes up to two tables and says “can I join you?”",
|
||||
"Wasn't hard to crack Forrest Gump's password. 1forrest1.",
|
||||
"I love pressing the F5 key. It's refreshing.",
|
||||
"Called IT support and a chap from Australia came to fix my network connection. I asked “Do you come from a LAN down under?”",
|
||||
"There are 10 types of people in the world. Those who understand binary and those who don't.",
|
||||
"Why are assembly programmers often wet? They work below C level.",
|
||||
"My favourite computer based band is the Black IPs.",
|
||||
"What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.",
|
||||
"An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.",
|
||||
];
|
||||
|
||||
export const handler = (_req: Request, _ctx: FreshContext): Response => {
|
||||
const randomIndex = Math.floor(Math.random() * JOKES.length);
|
||||
const body = JOKES[randomIndex];
|
||||
return new Response(body);
|
||||
};
|
5
routes/api/ping.ts
Normal file
5
routes/api/ping.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
|
||||
export const handler = (_req: Request, _ctx: FreshContext): Response => {
|
||||
return new Response("pong");
|
||||
};
|
20
routes/chat.tsx
Normal file
20
routes/chat.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import Chat from "../islands/Chat.tsx";
|
||||
|
||||
export default function ChatPage() {
|
||||
return (
|
||||
<div class="min-h-screen p-4 sm:p-8">
|
||||
<h1 class="text-3xl sm:text-4xl font-bold text-secondary mb-6 sm:mb-8 text-center">
|
||||
Chat Room <div className="badge badge-dash badge-primary">Demo</div>
|
||||
</h1>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<Chat />
|
||||
</div>
|
||||
<div class="mt-8 text-center text-sm text-gray-500">
|
||||
<p>
|
||||
This is an ephemeral chat room. Messages are only visible to users
|
||||
currently online and aren't stored after you leave.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { CSS, render } from "@deno/gfm";
|
||||
import { Head } from "$fresh/runtime.ts";
|
||||
import { render } from "@deno/gfm";
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
|
||||
import { getPost, Post } from "../../lib/posts.ts";
|
||||
@ -21,9 +20,6 @@ export default function PostPage(props: PageProps<Post>) {
|
||||
const post = props.data;
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<style dangerouslySetInnerHTML={{ __html: CSS }} />
|
||||
</Head>
|
||||
<div class="min-h-screen p-4 md:p-8">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="p-4 md:p-8">
|
||||
@ -73,6 +69,7 @@ export default function PostPage(props: PageProps<Post>) {
|
||||
class="max-w-none prose"
|
||||
data-color-mode="dark"
|
||||
data-dark-theme="dark"
|
||||
// deno-lint-ignore react-no-danger
|
||||
dangerouslySetInnerHTML={{ __html: render(post.content) }}
|
||||
/>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user