feat: add REST routes for devices, goals, and health

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sanju Sivalingam
2026-02-17 14:21:11 +05:30
parent 8fe3ad9926
commit 577c195862
5 changed files with 102 additions and 2 deletions

View File

@@ -8,6 +8,9 @@ import {
handleDashboardClose, handleDashboardClose,
} from "./ws/dashboard.js"; } from "./ws/dashboard.js";
import type { WebSocketData } from "./ws/sessions.js"; import type { WebSocketData } from "./ws/sessions.js";
import { devices } from "./routes/devices.js";
import { goals } from "./routes/goals.js";
import { health } from "./routes/health.js";
const app = new Hono(); const app = new Hono();
@@ -27,8 +30,10 @@ app.on(["POST", "GET"], "/api/auth/*", (c) => {
return auth.handler(c.req.raw); return auth.handler(c.req.raw);
}); });
// Health check // REST routes
app.get("/health", (c) => c.json({ status: "ok" })); app.route("/devices", devices);
app.route("/goals", goals);
app.route("/health", health);
// Start server with WebSocket support // Start server with WebSocket support
const server = Bun.serve<WebSocketData>({ const server = Bun.serve<WebSocketData>({

View File

@@ -0,0 +1,24 @@
import type { Context, Next } from "hono";
import { auth } from "../auth.js";
/** Hono Env type for routes protected by sessionMiddleware */
export type AuthEnv = {
Variables: {
user: { id: string; name: string; email: string; [key: string]: unknown };
session: { id: string; userId: string; [key: string]: unknown };
};
};
export async function sessionMiddleware(c: Context, next: Next) {
const session = await auth.api.getSession({
headers: c.req.raw.headers,
});
if (!session) {
return c.json({ error: "unauthorized" }, 401);
}
c.set("user", session.user);
c.set("session", session.session);
await next();
}

View File

@@ -0,0 +1,22 @@
import { Hono } from "hono";
import { sessionMiddleware, type AuthEnv } from "../middleware/auth.js";
import { sessions } from "../ws/sessions.js";
const devices = new Hono<AuthEnv>();
devices.use("*", sessionMiddleware);
devices.get("/", (c) => {
const user = c.get("user");
const userDevices = sessions.getDevicesForUser(user.id);
return c.json(
userDevices.map((d) => ({
deviceId: d.deviceId,
name: d.deviceInfo?.model ?? "Unknown Device",
deviceInfo: d.deviceInfo,
connectedAt: d.connectedAt.toISOString(),
}))
);
});
export { devices };

View File

@@ -0,0 +1,36 @@
import { Hono } from "hono";
import { sessionMiddleware, type AuthEnv } from "../middleware/auth.js";
import { sessions } from "../ws/sessions.js";
const goals = new Hono<AuthEnv>();
goals.use("*", sessionMiddleware);
goals.post("/", async (c) => {
const user = c.get("user");
const body = await c.req.json<{ deviceId: string; goal: string }>();
if (!body.deviceId || !body.goal) {
return c.json({ error: "deviceId and goal are required" }, 400);
}
const device = sessions.getDevice(body.deviceId);
if (!device) {
return c.json({ error: "device not connected" }, 404);
}
if (device.userId !== user.id) {
return c.json({ error: "device does not belong to you" }, 403);
}
// TODO (Task 6): start agent loop for this device+goal
const sessionId = crypto.randomUUID();
return c.json({
sessionId,
deviceId: body.deviceId,
goal: body.goal,
status: "queued",
});
});
export { goals };

View File

@@ -0,0 +1,13 @@
import { Hono } from "hono";
import { sessions } from "../ws/sessions.js";
const health = new Hono();
health.get("/", (c) => {
return c.json({
status: "ok",
connectedDevices: sessions.getStats().devices,
});
});
export { health };