Files
medassist-ng/backend/src/utils/server-config.ts
T
Daniel Volz 2a84a43654 fix: unify data directory for dev and prod environments (#116)
Add DATA_DIR env var support to configure the data directory path.
All hardcoded resolve(cwd, 'data') paths now use a central getDataDir()
function from db-utils.ts that checks DATA_DIR first, falling back to
resolve(cwd, 'data').

This prevents local dev (cd backend && npm run dev) from creating a
separate backend/data/ directory instead of using the root data/ folder.

Changes:
- Add getDataDir() to db-utils.ts as single source of truth
- Update all 8 source files that reference the data directory
- Add dotenv fallback to ../.env for local dev from backend/
- Add DATA_DIR documentation to .env.example
- Add 7 new tests for getDataDir and getDbPaths with DATA_DIR
- 493 tests pass, TypeScript clean
2026-02-08 11:20:55 +01:00

114 lines
2.7 KiB
TypeScript

/**
* Utility functions for server configuration.
* Exported separately to allow testing without triggering server start.
*/
import { existsSync, mkdirSync } from "node:fs";
import { resolve } from "node:path";
import type { CookieSerializeOptions } from "@fastify/cookie";
import { getDataDir } from "../db/db-utils.js";
/**
* Parse comma-separated CORS origins string
*/
export function parseCorsOrigins(originsStr: string): string[] {
return originsStr
.split(",")
.map((o) => o.trim())
.filter((o) => o.length > 0);
}
/**
* Build base cookie options for access token
*/
export function buildBaseCookieOptions(accessTtlMinutes: number, isProduction: boolean): CookieSerializeOptions {
return {
httpOnly: true,
secure: isProduction,
sameSite: "lax",
path: "/",
maxAge: accessTtlMinutes * 60, // Convert minutes to seconds
};
}
/**
* Build refresh cookie options (extends base with longer TTL)
*/
export function buildRefreshCookieOptions(
baseCookieOptions: CookieSerializeOptions,
refreshTtlDays: number
): CookieSerializeOptions {
return {
...baseCookieOptions,
maxAge: refreshTtlDays * 24 * 60 * 60, // Convert days to seconds
};
}
/**
* Build complete app configuration object
*/
export interface AppConfigOptions {
jwtSecret?: string;
refreshSecret?: string;
accessTtlMinutes: number;
refreshTtlDays: number;
isProduction: boolean;
}
export interface AppConfig {
accessSecret: string;
refreshSecret: string;
accessTtl: number;
refreshTtl: number;
cookieOptions: CookieSerializeOptions;
refreshCookieOptions: CookieSerializeOptions;
}
export function buildAppConfig(options: AppConfigOptions): AppConfig {
const cookieOptions = buildBaseCookieOptions(options.accessTtlMinutes, options.isProduction);
const refreshCookieOptions = buildRefreshCookieOptions(cookieOptions, options.refreshTtlDays);
return {
accessSecret: options.jwtSecret || "",
refreshSecret: options.refreshSecret || "",
accessTtl: options.accessTtlMinutes,
refreshTtl: options.refreshTtlDays,
cookieOptions,
refreshCookieOptions,
};
}
/**
* Ensure images directory exists
*/
export function ensureImagesDirectory(cwd?: string): string {
const imagesDir = resolve(getDataDir(cwd), "images");
if (!existsSync(imagesDir)) {
mkdirSync(imagesDir, { recursive: true });
}
return imagesDir;
}
/**
* Get JWT configuration based on auth enabled status
*/
export interface JwtConfig {
secret: string;
cookie: {
cookieName: string;
signed: boolean;
};
}
export function getJwtConfig(authEnabled: boolean, jwtSecret?: string): JwtConfig {
const effectiveSecret = authEnabled && jwtSecret ? jwtSecret : "auth-disabled-no-secret-needed";
return {
secret: effectiveSecret,
cookie: {
cookieName: "access_token",
signed: false,
},
};
}