Import source: backend/src/app
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import Fastify from "fastify";
|
||||
import cors from "@fastify/cors";
|
||||
import helmet from "@fastify/helmet";
|
||||
import jwt from "@fastify/jwt";
|
||||
import {
|
||||
serializerCompiler,
|
||||
validatorCompiler,
|
||||
ZodTypeProvider,
|
||||
} from "fastify-type-provider-zod";
|
||||
|
||||
import { env } from "./config/env.js";
|
||||
import { prismaPlugin } from "./plugins/prisma.js";
|
||||
import { errorHandler } from "./plugins/error-handler.js";
|
||||
import { fail } from "./lib/envelope.js";
|
||||
import { authRoutes } from "./routes/auth.js";
|
||||
import { websiteRoutes } from "./routes/websites.js";
|
||||
import { domainRoutes } from "./routes/domains.js";
|
||||
import { panelRoutes } from "./routes/panel.js";
|
||||
import { moduleRoutes } from "./routes/modules.js";
|
||||
|
||||
export function buildApp() {
|
||||
const app = Fastify({
|
||||
logger:
|
||||
env.NODE_ENV === "development"
|
||||
? {
|
||||
transport: { target: "pino-pretty" },
|
||||
}
|
||||
: false,
|
||||
}).withTypeProvider<ZodTypeProvider>();
|
||||
|
||||
// Register JSON schemas compilers for Zod
|
||||
app.setValidatorCompiler(validatorCompiler);
|
||||
app.setSerializerCompiler(serializerCompiler);
|
||||
app.removeContentTypeParser("application/json");
|
||||
app.addContentTypeParser(
|
||||
"application/json",
|
||||
{ parseAs: "string" },
|
||||
(_request, body, done) => {
|
||||
const text = typeof body === "string" ? body : body.toString("utf8");
|
||||
if (text.trim() === "") {
|
||||
done(null, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
done(null, JSON.parse(text));
|
||||
} catch (error) {
|
||||
done(error as Error, undefined);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Security Plugins
|
||||
app.register(helmet);
|
||||
app.register(cors, { origin: true, credentials: true });
|
||||
app.register(jwt, { secret: env.JWT_SECRET });
|
||||
|
||||
// Custom Plugins
|
||||
app.register(prismaPlugin);
|
||||
app.setErrorHandler(errorHandler);
|
||||
|
||||
app.get("/api/rate-limited", async (request, reply) =>
|
||||
fail(
|
||||
request,
|
||||
reply,
|
||||
429,
|
||||
{
|
||||
code: "RATE_LIMIT_ERROR",
|
||||
message: "Too many requests. Please try again later.",
|
||||
},
|
||||
{ rateLimit: { limit: 5, remaining: 0, reset: 100 } },
|
||||
),
|
||||
);
|
||||
|
||||
app.get("/api/simulate-500", async () => {
|
||||
throw new Error("Internal server error simulated successfully.");
|
||||
});
|
||||
|
||||
app.get("/api/auth/login", async (request, reply) =>
|
||||
fail(request, reply, 405, {
|
||||
code: "METHOD_NOT_ALLOWED",
|
||||
message: "GET is not allowed for this endpoint.",
|
||||
}),
|
||||
);
|
||||
|
||||
app.register(authRoutes);
|
||||
app.register(websiteRoutes);
|
||||
app.register(domainRoutes);
|
||||
app.register(panelRoutes);
|
||||
app.register(moduleRoutes);
|
||||
|
||||
// Health check route
|
||||
app.get("/health", async (request, reply) => {
|
||||
try {
|
||||
// Verify database connection using Prisma
|
||||
await app.prisma.$queryRaw`SELECT 1`;
|
||||
return {
|
||||
status: "ok",
|
||||
database: "connected",
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
app.log.error(err);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
return reply.status(500).send({
|
||||
status: "error",
|
||||
database: "disconnected",
|
||||
message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.setNotFoundHandler((request, reply) =>
|
||||
fail(request, reply, 404, {
|
||||
code: "NOT_FOUND",
|
||||
message: `Route ${request.method} ${request.url} was not found.`,
|
||||
}),
|
||||
);
|
||||
|
||||
return app;
|
||||
}
|
||||
Reference in New Issue
Block a user