add production stuff
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
PORT=8000
|
PORT=8000
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
DATABASE_URL=file:/app/prisma/dev.db
|
DATABASE_URL=file:/app/prisma/dev.db
|
||||||
FRONTEND_URL=http://localhost:6767
|
FRONTEND_URL=https://draw.louiscreates.com
|
||||||
|
API_BASE_PATH=/api
|
||||||
# Keep disabled unless traffic always comes through a trusted reverse proxy.
|
# Keep disabled unless traffic always comes through a trusted reverse proxy.
|
||||||
TRUST_PROXY=false
|
TRUST_PROXY=false
|
||||||
AUTH_MODE=local
|
AUTH_MODE=local
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface Config {
|
|||||||
nodeEnv: string;
|
nodeEnv: string;
|
||||||
databaseUrl?: string;
|
databaseUrl?: string;
|
||||||
frontendUrl?: string;
|
frontendUrl?: string;
|
||||||
|
apiBasePath: string;
|
||||||
authMode: AuthMode;
|
authMode: AuthMode;
|
||||||
jwtSecret: string;
|
jwtSecret: string;
|
||||||
jwtAccessExpiresIn: string;
|
jwtAccessExpiresIn: string;
|
||||||
@@ -82,6 +83,22 @@ const parseFrontendUrl = (raw: string | undefined): string | undefined => {
|
|||||||
return normalized.length > 0 ? normalized : undefined;
|
return normalized.length > 0 ? normalized : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseApiBasePath = (raw: string | undefined): string => {
|
||||||
|
const fallback = "/api";
|
||||||
|
if (!raw || raw.trim().length === 0) return fallback;
|
||||||
|
|
||||||
|
const trimmed = raw.trim();
|
||||||
|
if (trimmed === "/") return "/";
|
||||||
|
|
||||||
|
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
||||||
|
const withoutTrailingSlash =
|
||||||
|
withLeadingSlash.length > 1 && withLeadingSlash.endsWith("/")
|
||||||
|
? withLeadingSlash.slice(0, -1)
|
||||||
|
: withLeadingSlash;
|
||||||
|
|
||||||
|
return withoutTrailingSlash.length > 0 ? withoutTrailingSlash : fallback;
|
||||||
|
};
|
||||||
|
|
||||||
const resolveDatabaseUrl = (rawUrl?: string) => {
|
const resolveDatabaseUrl = (rawUrl?: string) => {
|
||||||
const backendRoot = path.resolve(__dirname, "../");
|
const backendRoot = path.resolve(__dirname, "../");
|
||||||
const defaultDbPath = path.resolve(backendRoot, "prisma/dev.db");
|
const defaultDbPath = path.resolve(backendRoot, "prisma/dev.db");
|
||||||
@@ -189,6 +206,7 @@ export const config: Config = {
|
|||||||
nodeEnv: getOptionalEnv("NODE_ENV", "development"),
|
nodeEnv: getOptionalEnv("NODE_ENV", "development"),
|
||||||
databaseUrl: process.env.DATABASE_URL,
|
databaseUrl: process.env.DATABASE_URL,
|
||||||
frontendUrl: parseFrontendUrl(process.env.FRONTEND_URL),
|
frontendUrl: parseFrontendUrl(process.env.FRONTEND_URL),
|
||||||
|
apiBasePath: parseApiBasePath(process.env.API_BASE_PATH),
|
||||||
authMode: resolvedAuthMode,
|
authMode: resolvedAuthMode,
|
||||||
jwtSecret: resolveJwtSecret(getOptionalEnv("NODE_ENV", "development")),
|
jwtSecret: resolveJwtSecret(getOptionalEnv("NODE_ENV", "development")),
|
||||||
jwtAccessExpiresIn: getOptionalEnv("JWT_ACCESS_EXPIRES_IN", "15m"),
|
jwtAccessExpiresIn: getOptionalEnv("JWT_ACCESS_EXPIRES_IN", "15m"),
|
||||||
|
|||||||
+16
-5
@@ -59,6 +59,7 @@ const normalizeOrigins = (rawOrigins?: string | null): string[] => {
|
|||||||
|
|
||||||
const allowedOrigins = normalizeOrigins(config.frontendUrl);
|
const allowedOrigins = normalizeOrigins(config.frontendUrl);
|
||||||
console.log("Allowed origins:", allowedOrigins);
|
console.log("Allowed origins:", allowedOrigins);
|
||||||
|
console.log("API base path:", config.apiBasePath);
|
||||||
|
|
||||||
const isDev = (process.env.NODE_ENV || "development") !== "production";
|
const isDev = (process.env.NODE_ENV || "development") !== "production";
|
||||||
const isLocalDevOrigin = (origin: string): boolean => {
|
const isLocalDevOrigin = (origin: string): boolean => {
|
||||||
@@ -132,6 +133,10 @@ if (trustProxyValue === true) {
|
|||||||
|
|
||||||
const httpServer = createServer(app);
|
const httpServer = createServer(app);
|
||||||
const io = new Server(httpServer, {
|
const io = new Server(httpServer, {
|
||||||
|
path:
|
||||||
|
config.apiBasePath === "/"
|
||||||
|
? "/socket.io"
|
||||||
|
: `${config.apiBasePath}/socket.io`,
|
||||||
cors: {
|
cors: {
|
||||||
origin: (origin, cb) => cb(null, isAllowedOrigin(origin ?? undefined)),
|
origin: (origin, cb) => cb(null, isAllowedOrigin(origin ?? undefined)),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
@@ -329,15 +334,18 @@ const generalRateLimiter = rateLimit({
|
|||||||
|
|
||||||
app.use(generalRateLimiter);
|
app.use(generalRateLimiter);
|
||||||
|
|
||||||
|
const apiApp = express();
|
||||||
|
app.use(config.apiBasePath, apiApp);
|
||||||
|
|
||||||
registerCsrfProtection({
|
registerCsrfProtection({
|
||||||
app,
|
app: apiApp,
|
||||||
isAllowedOrigin,
|
isAllowedOrigin,
|
||||||
maxRequestsPerWindow: config.csrfMaxRequests,
|
maxRequestsPerWindow: config.csrfMaxRequests,
|
||||||
enableDebugLogging: process.env.DEBUG_CSRF === "true",
|
enableDebugLogging: process.env.DEBUG_CSRF === "true",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Authentication routes (no CSRF required, uses JWT)
|
// Authentication routes (no CSRF required, uses JWT)
|
||||||
app.use("/auth", authRouter);
|
apiApp.use("/auth", authRouter);
|
||||||
|
|
||||||
// Files field can contain arbitrary file metadata, so we use unknown and validate structure
|
// Files field can contain arbitrary file metadata, so we use unknown and validate structure
|
||||||
const filesFieldSchema = z
|
const filesFieldSchema = z
|
||||||
@@ -556,13 +564,13 @@ registerSocketHandlers({
|
|||||||
jwtSecret: config.jwtSecret,
|
jwtSecret: config.jwtSecret,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/health", (req, res) => {
|
apiApp.get("/health", (req, res) => {
|
||||||
res.status(200).json({ status: "ok" });
|
res.status(200).json({ status: "ok" });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Health check endpoint doesn't require auth
|
// Health check endpoint doesn't require auth
|
||||||
|
|
||||||
registerDashboardRoutes(app, {
|
registerDashboardRoutes(apiApp, {
|
||||||
prisma,
|
prisma,
|
||||||
requireAuth,
|
requireAuth,
|
||||||
asyncHandler,
|
asyncHandler,
|
||||||
@@ -584,7 +592,7 @@ registerDashboardRoutes(app, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
registerImportExportRoutes({
|
registerImportExportRoutes({
|
||||||
app,
|
app: apiApp,
|
||||||
prisma,
|
prisma,
|
||||||
requireAuth,
|
requireAuth,
|
||||||
asyncHandler,
|
asyncHandler,
|
||||||
@@ -622,5 +630,8 @@ if (isMain) {
|
|||||||
console.log(`Server running on port ${PORT}`);
|
console.log(`Server running on port ${PORT}`);
|
||||||
console.log(`Environment: ${config.nodeEnv}`);
|
console.log(`Environment: ${config.nodeEnv}`);
|
||||||
console.log(`Frontend URL: ${config.frontendUrl}`);
|
console.log(`Frontend URL: ${config.frontendUrl}`);
|
||||||
|
console.log(
|
||||||
|
`API endpoints: ${config.apiBasePath === "/" ? "/" : `${config.apiBasePath}/`}*`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user