feat: scaffold Hono server with auth and health check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sanju Sivalingam
2026-02-17 14:07:19 +05:30
parent 1e9e1f7e29
commit bc014fd587
9 changed files with 126 additions and 0 deletions

3
server/.env.example Normal file
View File

@@ -0,0 +1,3 @@
DATABASE_URL="postgres://user:password@host:port/db-name"
PORT=8080
CORS_ORIGIN="http://localhost:5173"

12
server/Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM oven/bun:1
WORKDIR /app
COPY packages/shared ./packages/shared
COPY server ./server
WORKDIR /app/server
RUN bun install
EXPOSE 8080
CMD ["bun", "src/index.ts"]

21
server/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "@droidclaw/server",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "bun --watch src/index.ts",
"start": "bun src/index.ts",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@droidclaw/shared": "workspace:*",
"hono": "^4.7.0",
"better-auth": "^1.3.27",
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.7"
},
"devDependencies": {
"@types/bun": "^1.1.0",
"typescript": "^5.9.2"
}
}

11
server/src/auth.ts Normal file
View File

@@ -0,0 +1,11 @@
import { betterAuth } from "better-auth";
import { apiKey } from "better-auth/plugins";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db.js";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
}),
plugins: [apiKey()],
});

6
server/src/db.ts Normal file
View File

@@ -0,0 +1,6 @@
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { env } from "./env.js";
const client = postgres(env.DATABASE_URL);
export const db = drizzle(client);

9
server/src/env.ts Normal file
View File

@@ -0,0 +1,9 @@
export const env = {
DATABASE_URL: process.env.DATABASE_URL!,
PORT: parseInt(process.env.PORT || "8080"),
CORS_ORIGIN: process.env.CORS_ORIGIN || "http://localhost:5173",
};
if (!env.DATABASE_URL) {
throw new Error("DATABASE_URL is not set");
}

44
server/src/index.ts Normal file
View File

@@ -0,0 +1,44 @@
import { Hono } from "hono";
import { cors } from "hono/cors";
import { auth } from "./auth.js";
import { env } from "./env.js";
const app = new Hono();
// CORS for dashboard
app.use(
"*",
cors({
origin: env.CORS_ORIGIN,
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true,
})
);
// Better Auth handler
app.on(["POST", "GET"], "/api/auth/*", (c) => {
return auth.handler(c.req.raw);
});
// Health check
app.get("/health", (c) => c.json({ status: "ok" }));
// Start server with WebSocket support
const server = Bun.serve({
port: env.PORT,
fetch: app.fetch,
websocket: {
open(ws) {
console.log("WebSocket connected");
},
message(ws, message) {
// placeholder — Task 4 implements device/dashboard handlers
},
close(ws) {
console.log("WebSocket disconnected");
},
},
});
console.log(`Server running on port ${server.port}`);

17
server/tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src",
"types": ["bun"],
"paths": {
"@droidclaw/shared": ["../packages/shared/src"]
}
},
"include": ["src/**/*.ts"]
}