Replace Next.js/Supabase/Vercel stack with a self-hostable setup: Vite+React frontend, Fastify+Drizzle+PostgreSQL backend with own JWT auth, deploy via git pull + rsync + systemd (checkout/service separated, updates pulled from Gitea). Adds a reference auth route (register/login/me) as the canonical pattern for new routes. Updates all skills/rules/agents/docs to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMdBJfD9TT6XUU2NqGG1PN
31 lines
939 B
TypeScript
31 lines
939 B
TypeScript
import "dotenv/config";
|
|
import Fastify, { type FastifyRequest, type FastifyReply } from "fastify";
|
|
import cors from "@fastify/cors";
|
|
import jwt from "@fastify/jwt";
|
|
import rateLimit from "@fastify/rate-limit";
|
|
import { healthRoutes } from "./routes/health.js";
|
|
import { authRoutes } from "./routes/auth.js";
|
|
|
|
const app = Fastify({ logger: true });
|
|
|
|
await app.register(cors, { origin: true });
|
|
await app.register(rateLimit, { max: 100, timeWindow: "1 minute" });
|
|
await app.register(jwt, { secret: process.env.JWT_SECRET! });
|
|
|
|
app.decorate("authenticate", async (request: FastifyRequest, reply: FastifyReply) => {
|
|
try {
|
|
await request.jwtVerify();
|
|
} catch {
|
|
reply.code(401).send({ error: "Unauthorized" });
|
|
}
|
|
});
|
|
|
|
await app.register(healthRoutes);
|
|
await app.register(authRoutes);
|
|
|
|
const port = Number(process.env.PORT ?? 4000);
|
|
app.listen({ port, host: "0.0.0.0" }).catch((err) => {
|
|
app.log.error(err);
|
|
process.exit(1);
|
|
});
|