feat: create centralized path utilities for improved path resolution and environment awareness

This commit is contained in:
Tudor Stanciu 2025-10-05 15:54:21 +03:00
parent dca221384c
commit ee20b85c9e
5 changed files with 58 additions and 25 deletions

View File

@ -29,6 +29,9 @@
"Removed all `.js` extensions from backend imports (handlers, middleware, routes, services)",
"Added `tsup.config.ts` with optimized ESM output configuration",
"Updated build script from `tsc` to `tsup` in package.json",
"Created centralized path utilities in `src/backend/utils/paths.ts`",
"Replaced duplicated `__dirname` logic with `resolveFromRoot()` function",
"Path resolution now environment-aware (dev vs production)",
"Maintained development mode with nodemon for hot-reload support"
]
},

View File

@ -3,8 +3,6 @@ import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { setTimeout } from 'timers';
import apiRoutes from './routes/api';
import apiKeyAuth from './middleware/auth';
@ -13,9 +11,7 @@ import config from './services/config';
import logger from './services/logger';
import { generateRuntimeConfig } from './services/runtimeConfig';
import { healthCheckHandler } from './handlers/healthHandler';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import { paths } from './utils/paths';
const app = express();
@ -72,7 +68,7 @@ const basePath = config.basePath.endsWith('/')
: config.basePath;
// Serve static frontend files
const frontendPath = path.join(__dirname, '../frontend');
const frontendPath = paths.frontendDir;
// Generate runtime configuration (env.js) at startup
generateRuntimeConfig(frontendPath, basePath);

View File

@ -1,8 +1,7 @@
import { Router, Request, Response } from 'express';
import Joi from 'joi';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import paths from '../utils/paths';
import geoIPService from '../services/geoip';
import logger from '../services/logger';
import config from '../services/config';
@ -13,9 +12,6 @@ import {
ErrorResponse,
} from '../types/index';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const router = Router();
// Validation schemas
@ -276,10 +272,8 @@ router.get('/version', (_req: Request, res: Response): void => {
// Get release notes
router.get('/release-notes', (_req: Request, res: Response): void => {
try {
const releaseNotesPath = join(__dirname, '../../../ReleaseNotes.json');
const releaseNotesContent = readFileSync(releaseNotesPath, 'utf-8');
const releaseNotesContent = readFileSync(paths.releaseNotesFile, 'utf-8');
const releaseNotes = JSON.parse(releaseNotesContent);
logger.debug('Release notes retrieved successfully');
res.json(releaseNotes);
} catch (error) {
@ -294,10 +288,8 @@ router.get('/release-notes', (_req: Request, res: Response): void => {
// Get overview
router.get('/overview', (_req: Request, res: Response): void => {
try {
const overviewPath = join(__dirname, '../../../Overview.json');
const overviewContent = readFileSync(overviewPath, 'utf-8');
const overviewContent = readFileSync(paths.overviewFile, 'utf-8');
const overview = JSON.parse(overviewContent);
logger.debug('Overview retrieved successfully');
res.json(overview);
} catch (error) {

View File

@ -1,14 +1,8 @@
import { Config, LogLevel, LOG_LEVELS } from '../types/index';
import path from 'path';
import { config as dotenvConfig } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { paths } from '../utils/paths';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Load .env file from root directory
dotenvConfig({ path: path.join(__dirname, '../../../.env') });
dotenvConfig({ path: paths.envFile });
// Parse log level from environment variable
const parseLogLevel = (level?: string): LogLevel => {

View File

@ -0,0 +1,48 @@
import path from 'path';
import { fileURLToPath } from 'url';
const isProduction = process.env.NODE_ENV === 'production';
const findProjectRoot = (): string => {
// In production (Docker), process.cwd() is /app (project root)
if (isProduction) {
return process.cwd();
}
if (import.meta.url) {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootPath = path.resolve(__dirname, '../../..');
return rootPath;
}
throw new Error(
'import.meta.url is required in development to resolve project root'
);
};
const projectRootPath = findProjectRoot();
console.log('[paths] Project root resolved:', projectRootPath);
const resolveFilePath = (...relativePath: string[]): string => {
return path.join(projectRootPath, ...relativePath);
};
const envPath = resolveFilePath('.env');
const releaseNotesPath = resolveFilePath('ReleaseNotes.json');
const overviewPath = resolveFilePath('Overview.json');
const frontendPrefix = isProduction ? 'dist' : 'src';
const frontendPath = resolveFilePath(frontendPrefix, 'frontend');
const paths = {
projectRoot: projectRootPath,
envFile: envPath,
releaseNotesFile: releaseNotesPath,
overviewFile: overviewPath,
frontendDir: frontendPath,
};
console.log('[paths] App paths resolved:', paths);
export { paths };
export default paths;