update:deploy
This commit is contained in:
@@ -1,49 +1,61 @@
|
|||||||
import Fastify from 'fastify'
|
import Fastify from "fastify";
|
||||||
import cors from '@fastify/cors'
|
import cors from "@fastify/cors";
|
||||||
import cookie from '@fastify/cookie'
|
import cookie from "@fastify/cookie";
|
||||||
import session from '@fastify/session'
|
import session from "@fastify/session";
|
||||||
import csrf from '@fastify/csrf-protection'
|
import csrf from "@fastify/csrf-protection";
|
||||||
|
|
||||||
import { authMiddleware } from './middleware/auth.js'
|
import { authMiddleware } from "./middleware/auth.js";
|
||||||
import { storageMiddleware } from './middleware/storage.js'
|
import { storageMiddleware } from "./middleware/storage.js";
|
||||||
import { ticketsRouter } from './routes/tickets.js'
|
import { ticketsRouter } from "./routes/tickets.js";
|
||||||
import { authRouter } from './routes/auth.js'
|
import { authRouter } from "./routes/auth.js";
|
||||||
import { SqliteSessionStore } from './db/sessionStore.js'
|
import { SqliteSessionStore } from "./db/sessionStore.js";
|
||||||
|
import { cloudflareMiddleware } from "./middleware/cloudflare.js";
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production'
|
const isProd = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
const app = Fastify({ logger: true })
|
const sessionSecret = process.env.SESSION_SECRET;
|
||||||
|
if (!sessionSecret) throw new Error("SESSION_SECRET env var is required");
|
||||||
|
|
||||||
|
const app = Fastify({
|
||||||
|
// In prod: warn-level only to reduce noise; in dev: full pretty logging
|
||||||
|
logger: isProd ? { level: "warn" } : true,
|
||||||
|
// Trust the nginx reverse proxy so secure cookies and req.ip work correctly
|
||||||
|
trustProxy: isProd,
|
||||||
|
});
|
||||||
|
|
||||||
await app.register(cors, {
|
await app.register(cors, {
|
||||||
methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],
|
methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
||||||
origin: process.env.FRONTEND_URL ?? 'http://localhost:5173',
|
origin: process.env.FRONTEND_URL ?? "http://localhost:5173",
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
await app.register(cookie)
|
await app.register(cookie);
|
||||||
|
|
||||||
await app.register(session, {
|
await app.register(session, {
|
||||||
secret: process.env.SESSION_SECRET!,
|
secret: sessionSecret,
|
||||||
store: new SqliteSessionStore(), // ← persistent SQLite store
|
store: new SqliteSessionStore(), // ← persistent SQLite store
|
||||||
cookie: {
|
cookie: {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProd, // HTTPS-only in production
|
secure: isProd, // HTTPS-only in production
|
||||||
sameSite: isProd ? 'strict' : 'lax', // strict in prod, lax in dev
|
sameSite: isProd ? "strict" : "lax", // strict in prod, lax in dev
|
||||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
|
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
|
||||||
},
|
},
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
if (isProd) {
|
if (isProd) {
|
||||||
await app.register(csrf, {
|
await app.register(csrf, {
|
||||||
sessionPlugin: '@fastify/session',
|
sessionPlugin: "@fastify/session",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await app.register(authMiddleware)
|
await app.register(authMiddleware);
|
||||||
await app.register(storageMiddleware)
|
await app.register(storageMiddleware);
|
||||||
|
if (isProd) {
|
||||||
|
await app.register(cloudflareMiddleware);
|
||||||
|
}
|
||||||
|
|
||||||
await app.register(authRouter, { prefix: '/api/auth' })
|
await app.register(authRouter, { prefix: "/api/auth" });
|
||||||
await app.register(ticketsRouter, { prefix: '/api/tickets' })
|
await app.register(ticketsRouter, { prefix: "/api/tickets" });
|
||||||
|
|
||||||
await app.listen({ port: 4500, host: 'localhost' })
|
await app.listen({ port: 4500, host: process.env.HOST ?? "localhost" });
|
||||||
|
|||||||
21
backend/src/middleware/cloudflare.ts
Normal file
21
backend/src/middleware/cloudflare.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import fp from "fastify-plugin";
|
||||||
|
import type { FastifyPluginAsync } from "fastify";
|
||||||
|
|
||||||
|
declare module "fastify" {
|
||||||
|
interface FastifyRequest {
|
||||||
|
// Real client IP resolved from CF-Connecting-IP in prod, req.ip in dev
|
||||||
|
clientIp: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cloudflareMiddleware: FastifyPluginAsync = fp(async (app) => {
|
||||||
|
app.decorateRequest("clientIp", "");
|
||||||
|
|
||||||
|
app.addHook("onRequest", async (req) => {
|
||||||
|
// CF-Connecting-IP is set by Cloudflare and cannot be spoofed by the client.
|
||||||
|
// X-Forwarded-For is not used here because it can be injected by anyone
|
||||||
|
// sending a request directly to the origin, bypassing Cloudflare.
|
||||||
|
const cfIp = req.headers["cf-connecting-ip"];
|
||||||
|
req.clientIp = (Array.isArray(cfIp) ? cfIp[0] : cfIp) ?? req.ip;
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user