cpns / apps /server /src /index.ts
pramudya-yb
Minimalize landing page and remove rogasper
7bcfa1f
Raw
History Blame Contribute Delete
3.09 kB
import { trpcServer } from "@hono/trpc-server";
import { createContext } from "@pram/api/context";
import { appRouter } from "@pram/api/routers/index";
import { checkRateLimitAllowed } from "@pram/api/lib/rate-limit";
import { auth } from "@pram/auth";
import { env } from "@pram/env/server";
import { withRequestId } from "@pram/api/logger";
import { Hono } from "hono";
import { cors } from "hono/cors";
import "@pram/api/queue";
const app = new Hono();
app.use("/*", async (c, next) => {
const reqId = c.req.header("x-request-id") ?? crypto.randomUUID();
const reqLogger = withRequestId(reqId);
const start = Date.now();
c.res.headers.set("x-request-id", reqId);
reqLogger.info(`${c.req.method} ${c.req.path}`);
await next();
const duration = Date.now() - start;
reqLogger.info(`${c.req.method} ${c.req.path} ${c.res.status}`, { durationMs: duration });
});
app.use("/*", async (c, next) => {
await next();
c.res.headers.set("X-Content-Type-Options", "nosniff");
c.res.headers.set("X-Frame-Options", "DENY");
c.res.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
c.res.headers.set("X-XSS-Protection", "0");
c.res.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
});
app.use(
"/*",
cors({
origin: env.CORS_ORIGIN,
allowMethods: ["GET", "POST", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization"],
credentials: true,
}),
);
// Rate limit sign-up attempts per IP to mitigate mass account creation
app.use("/api/auth/sign-up/email", async (c, next) => {
if (c.req.method !== "POST") return next();
const cfIp = c.req.header("cf-connecting-ip");
const forwarded = c.req.header("x-forwarded-for");
const realIp = c.req.header("x-real-ip");
const firstForwardedIp = forwarded ? forwarded.split(",")[0]?.trim() : undefined;
const ip = cfIp ?? firstForwardedIp ?? realIp ?? "unknown";
const allowed = await checkRateLimitAllowed({ key: `signup:ip:${ip}`, limit: 5, windowMs: 3_600_000 });
if (!allowed) {
return c.json({ error: "Too many sign-up attempts. Please try again later." }, 429);
}
return next();
});
app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw));
app.use(
"/trpc/*",
trpcServer({
router: appRouter,
createContext: (_opts, context) => {
return createContext({ context });
},
}),
);
app.get("/sitemap.xml", (c) => {
const baseUrl = "https://pram-app.vercel.app";
const urls = [
{ loc: `${baseUrl}/`, changefreq: "weekly", priority: "1.0" },
];
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls
.map(
(url) => ` <url>
<loc>${url.loc}</loc>
<changefreq>${url.changefreq}</changefreq>
<priority>${url.priority}</priority>
</url>`,
)
.join("\n")}
</urlset>`;
return c.text(sitemap, 200, { "Content-Type": "application/xml" });
});
app.get("/", (c) => {
return c.text("OK");
});
export default {
port: process.env.PORT || 3000,
hostname: "0.0.0.0",
fetch: app.fetch,
};