feat: remote location via WebSocket
This commit is contained in:
69
src/main.ts
69
src/main.ts
@ -4,6 +4,9 @@ import { cors } from "hono/cors";
|
|||||||
import { pool } from "./db";
|
import { pool } from "./db";
|
||||||
import { post } from "./ai";
|
import { post } from "./ai";
|
||||||
import { rateLimiter } from "hono-rate-limiter";
|
import { rateLimiter } from "hono-rate-limiter";
|
||||||
|
import { createBunWebSocket } from "hono/bun";
|
||||||
|
import type { ServerWebSocket } from "bun";
|
||||||
|
import type { WSContext } from "hono/ws";
|
||||||
|
|
||||||
const app = new Hono<{
|
const app = new Hono<{
|
||||||
Variables: {
|
Variables: {
|
||||||
@ -11,6 +14,7 @@ const app = new Hono<{
|
|||||||
session: typeof auth.$Infer.Session.session | null
|
session: typeof auth.$Infer.Session.session | null
|
||||||
}
|
}
|
||||||
}>();
|
}>();
|
||||||
|
const { upgradeWebSocket, websocket } = createBunWebSocket<ServerWebSocket>();
|
||||||
|
|
||||||
async function setupDB() {
|
async function setupDB() {
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
@ -171,8 +175,71 @@ if(process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
|
|||||||
app.post("/api/ai", post);
|
app.post("/api/ai", post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let wsSubscribers: Record<string, WSContext<ServerWebSocket>[]> = {};
|
||||||
|
|
||||||
|
app.get("/api/ws", upgradeWebSocket((c) => {
|
||||||
|
let advertising = "";
|
||||||
|
return {
|
||||||
|
onOpen(e, ws) {
|
||||||
|
console.log("WebSocket connection opened");
|
||||||
|
ws.send(JSON.stringify({ type: "welcome", message: "Welcome to TrafficCue WebSocket!" }));
|
||||||
|
},
|
||||||
|
onMessage(e, ws) {
|
||||||
|
const data = JSON.parse(e.data.toString());
|
||||||
|
console.log("WebSocket message received:", data);
|
||||||
|
|
||||||
|
if (data.type === "advertise") {
|
||||||
|
const code = data.code || randomCode();
|
||||||
|
wsSubscribers[code] = wsSubscribers[code] || [];
|
||||||
|
advertising = code;
|
||||||
|
ws.send(JSON.stringify({ type: "advertising", code }));
|
||||||
|
} else if (data.type === "subscribe") {
|
||||||
|
const code = data.code;
|
||||||
|
if (!code || !wsSubscribers[code]) {
|
||||||
|
ws.send(JSON.stringify({ type: "error", message: "Invalid or unknown code" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wsSubscribers[code].push(ws);
|
||||||
|
ws.send(JSON.stringify({ type: "subscribed", code }));
|
||||||
|
} else if (data.type === "location") {
|
||||||
|
const subscribers = wsSubscribers[advertising] || [];
|
||||||
|
subscribers.forEach(subscriber => {
|
||||||
|
if (subscriber !== ws) {
|
||||||
|
subscriber.send(JSON.stringify({ type: "location", location: data.location, route: data.route }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ws.send(JSON.stringify({ type: "error", message: "Unknown message type" }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose(e, ws) {
|
||||||
|
// If they are subscribing, remove them from the subscribers list
|
||||||
|
for (const code in wsSubscribers) {
|
||||||
|
if (wsSubscribers[code]) {
|
||||||
|
wsSubscribers[code] = wsSubscribers[code].filter(subscriber => subscriber !== ws);
|
||||||
|
if (wsSubscribers[code].length === 0) {
|
||||||
|
delete wsSubscribers[code];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
function randomCode(length: number = 6): string {
|
||||||
|
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
let result = "";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
app.get("/", (c) => {
|
app.get("/", (c) => {
|
||||||
return c.text("TrafficCue Server");
|
return c.text("TrafficCue Server");
|
||||||
})
|
})
|
||||||
|
|
||||||
export default app
|
export default {
|
||||||
|
fetch: app.fetch,
|
||||||
|
websocket
|
||||||
|
}
|
Reference in New Issue
Block a user