diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts new file mode 100644 index 0000000..c6fb1f5 --- /dev/null +++ b/packages/backend/src/index.ts @@ -0,0 +1,34 @@ +import express from "express"; +import cors from "cors"; +import { createServer } from "http"; +import path from "path"; +import { fileURLToPath } from "url"; +import { initWebSocket, getClientCount } from "./services/ws.service.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const app = express(); +const server = createServer(app); + +app.use(cors()); +app.use(express.json()); + +initWebSocket(server); + +app.get("/api/health", (_req, res) => { + res.json({ + status: "ok", + wsClients: getClientCount(), + uptime: process.uptime(), + }); +}); + +const frontendDist = path.resolve(__dirname, "../../frontend/dist"); +app.use(express.static(frontendDist)); +app.get("*path", (_req, res) => { + res.sendFile(path.join(frontendDist, "index.html")); +}); + +const PORT = parseInt(process.env.PORT || "3000", 10); +server.listen(PORT, "0.0.0.0", () => { + console.log(`Layonara Forge listening on http://0.0.0.0:${PORT}`); +}); diff --git a/packages/backend/src/services/ws.service.ts b/packages/backend/src/services/ws.service.ts new file mode 100644 index 0000000..1432b4d --- /dev/null +++ b/packages/backend/src/services/ws.service.ts @@ -0,0 +1,46 @@ +import { WebSocketServer, WebSocket } from "ws"; +import type { Server } from "http"; + +type EventType = "docker" | "build" | "log" | "toolset" | "git"; + +interface ForgeEvent { + type: EventType; + action: string; + data: unknown; + timestamp: number; +} + +const clients = new Set(); + +let wss: WebSocketServer; + +export function initWebSocket(server: Server): WebSocketServer { + wss = new WebSocketServer({ server, path: "/ws" }); + + wss.on("connection", (ws) => { + clients.add(ws); + ws.on("close", () => clients.delete(ws)); + ws.on("error", () => clients.delete(ws)); + }); + + return wss; +} + +export function broadcast(type: EventType, action: string, data: unknown): void { + const event: ForgeEvent = { + type, + action, + data, + timestamp: Date.now(), + }; + const message = JSON.stringify(event); + for (const client of clients) { + if (client.readyState === WebSocket.OPEN) { + client.send(message); + } + } +} + +export function getClientCount(): number { + return clients.size; +}