T1/Phase 1: scaffold Vite SPA + Hono API + Bun worker

- apps/web: Vite + React 19 + TanStack Router/Query + shadcn/ui
   - apps/api: Hono + Bun on :3001 with /api/health, /api/auth/*, /mcp stubs
   - apps/worker: Bun worker stub, DB connection, graceful SIGTERM
   - apps/web-legacy/: old Next.js code moved aside (preserved for T2-T8 reference)
   - Dockerfiles: api (Bun), worker (Bun), spa (multi-stage Caddy)
   - Caddyfile: serves dist + reverse-proxies /api/* + /mcp to api
   - docker-compose.yml: 4-service target (api, spa, db, worker)
   - packages/db/src/client.ts: shared Drizzle client for api + worker
   - db/client.ts: root-level alias for convenience

   Parent: t_e1cbd87d -> t_24c9c3fd (T0)
This commit is contained in:
Hermes
2026-08-01 01:15:31 +00:00
parent 9203aee758
commit fca56ab77e
312 changed files with 3489 additions and 196 deletions
+9
View File
@@ -0,0 +1,9 @@
import { createRootRoute, Outlet } from "@tanstack/react-router";
export const Route = createRootRoute({
component: () => (
<div className="min-h-screen bg-background text-foreground">
<Outlet />
</div>
),
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as rootRoute } from "./__root";
function HomePage() {
return (
<div className="flex flex-col items-center justify-center min-h-screen p-8">
<h1 className="text-4xl font-bold mb-4">Project E UI Redesign</h1>
<p className="text-lg text-muted-foreground">Phase 1 Scaffold</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => rootRoute,
path: "/",
component: HomePage,
});
+76
View File
@@ -0,0 +1,76 @@
import { createRoute, useNavigate } from "@tanstack/react-router";
import { Route as rootRoute } from "./__root";
import { useState } from "react";
function LoginPage() {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
try {
const res = await fetch("/api/auth/credentials", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
const data = await res.json();
setError(data.message || "Login failed");
return;
}
navigate({ to: "/" });
} catch {
setError("Network error");
}
};
return (
<div className="flex items-center justify-center min-h-screen">
<form
onSubmit={handleSubmit}
className="w-full max-w-sm p-8 space-y-4 border rounded-lg"
>
<h1 className="text-2xl font-bold text-center">Sign In</h1>
{error && (
<p className="text-sm text-destructive text-center">{error}</p>
)}
<div>
<label className="block text-sm font-medium mb-1">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border rounded-md bg-background"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border rounded-md bg-background"
required
/>
</div>
<button
type="submit"
className="w-full py-2 px-4 bg-primary text-primary-foreground rounded-md hover:opacity-90"
>
Sign In
</button>
</form>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => rootRoute,
path: "/login",
component: LoginPage,
});