Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export default [

'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'warn',

// Enforce use of the project logger (src/utils/logger.ts) instead
// of direct console calls, which bypass structured logging and
// environment-aware log levels.
'no-console': 'error',
},
},
];
7 changes: 4 additions & 3 deletions src/components/v1/distribution/distribution.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import AppDataSource from "../../../config/persistence/data-source"
import { DistributionEntity } from "./distribution.entity"
import { DistributionService } from "./distribution.service"
import type { ApiResponse, DistributionResponseDto, CreateDistributionDto, UpdateDistributionDto } from "./distribution.dto"
import { logError } from "../../../utils/logger"

const getDistributionService = () => {
if (!AppDataSource.isInitialized) {
Expand All @@ -29,7 +30,7 @@ export const createDistribution = async (req: Request, res: Response): Promise<v

res.status(201).json(response)
} catch (error) {
console.error("Error in createDistribution:", error)
logError("Error in createDistribution", error)

const errorResponse: ApiResponse<null> = {
data: null,
Expand Down Expand Up @@ -57,7 +58,7 @@ export const updateDistribution = async (req: Request, res: Response): Promise<v

res.status(200).json(response)
} catch (error) {
console.error("Error in updateDistribution:", error)
logError("Error in updateDistribution", error)

const isNotFound = error instanceof Error && error.message === "Distribution not found"
const status = isNotFound ? 404 : 500
Expand All @@ -83,7 +84,7 @@ export const listDistributions = async (_req: Request, res: Response): Promise<v

res.status(200).json(response)
} catch (error) {
console.error("Error in listDistributions:", error)
logError("Error in listDistributions", error)

const errorResponse: ApiResponse<null> = {
data: null,
Expand Down
9 changes: 5 additions & 4 deletions src/components/v1/distribution/distribution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Repository } from "typeorm"
import type { DistributionEntity } from "./distribution.entity"
import type { CreateDistributionDto, DistributionResponseDto, UpdateDistributionDto } from "./distribution.dto"
import { DistributionStatus, Network } from "../../../types/enums"
import { logError, logWarn } from "../../../utils/logger"

export class DistributionService {
constructor(private readonly distributionRepository: Repository<DistributionEntity>) {}
Expand All @@ -16,7 +17,7 @@ export class DistributionService {

return this.formatDistributionResponse(savedDistribution)
} catch (error) {
console.error("Error creating distribution:", error)
logError("Error creating distribution", error)
throw new Error("Failed to create distribution")
}
}
Expand Down Expand Up @@ -57,7 +58,7 @@ export class DistributionService {

return this.formatDistributionResponse(savedDistribution)
} catch (error) {
console.error("Error updating distribution:", error)
logError("Error updating distribution", error)
throw error instanceof Error ? error : new Error("Failed to update distribution")
}
}
Expand All @@ -71,7 +72,7 @@ export class DistributionService {

return distributions.map((d) => this.formatDistributionResponse(d))
} catch (error) {
console.error("Error listing distributions:", error)
logError("Error listing distributions", error)
throw new Error("Failed to list distributions")
}
}
Expand Down Expand Up @@ -115,7 +116,7 @@ export class DistributionService {
const rate = new Decimal(usdRate)
return amount.mul(rate).toString()
} catch (error) {
console.warn("Error calculating total USD amount:", error)
logWarn("Error calculating total USD amount", { error: String(error) })
return "0"
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/config/persistence/data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const resetDatabase = async () => {
await queryRunner.clearDatabase();
logger.info('All tables dropped. Database reset successfully!');
} catch (error) {
console.error('Error resetting the database:', error);
logger.error('Error resetting the database:', { error: String(error) });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Don’t stringify the reset failure here.

String(error) drops the structured fields this PR just standardized (name, message, stack), which makes DB reset failures much harder to diagnose. Route this through logError("Error resetting the database", error) or pass equivalent structured metadata.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/persistence/data-source.ts` at line 78, The database reset error
logging in data-source should preserve structured error details instead of
converting the exception to a string. Update the reset-failure handling to use
logError("Error resetting the database", error) or an equivalent structured
logger call so the existing name, message, and stack fields are retained; adjust
the error path near the logger.error call in the reset logic accordingly.

} finally {
await AppDataSource.destroy();
}
Expand Down
4 changes: 2 additions & 2 deletions src/scripts/generate-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const execAsync = promisify(exec);
const migrationName = process.argv[2];

if (!migrationName) {
console.error('Please provide a migration name.');
logger.error('Please provide a migration name.');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

This breaks the script’s terminal feedback.

These messages are part of the CLI contract, but the shared logger shown in src/utils/logger.ts only writes to error.log/combined.log. After this change, callers no longer see “Please provide a migration name” or “No schema changes detected” in stderr/stdout. Keep direct console output for CLI-only scripts, or add a Console transport before routing script UX through the shared logger.

Also applies to: 23-23

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { exec } from 'child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/generate-migration.ts` at line 11, The CLI messages in
generate-migration.ts are now being sent through the shared logger, so users no
longer see them in terminal output. Update the script’s migration-name and
no-schema-changes paths to write directly to stderr/stdout for CLI feedback, or
add a Console transport in src/utils/logger.ts if you want these messages to
remain visible while still using logger.error. Keep the script-specific UX on
the terminal side, and leave file logging behavior unchanged.

process.exit(1);
}

Expand All @@ -20,7 +20,7 @@ const command = `pnpm typeorm migration:generate --outputJs src/migrations/${mig
const { stdout, stderr } = await execAsync(command);

if (stdout.includes('No changes in database schema were found')) {
console.warn(
logger.warn(
'No schema changes detected. Skipping migration generation.'
);
return;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class ErrorHandler {
let { message, httpCode, type } = error as AppError;
const { name, extra } = error as AppError;

console.info(message, name);
logger.info(`${name}: ${message}`);

switch (name) {
case 'ValidationError':
Expand Down
3 changes: 2 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod';
import type { EntityManager } from 'typeorm';

import { IRequest } from '../types/global';
import { logError } from './logger';

export const isValidUuid = (uuid: string): boolean => {
const result = z.string().uuid().safeParse(uuid);
Expand All @@ -27,7 +28,7 @@ export async function executeTransaction<T>(
try {
return await transactionCallback(transactionManager);
} catch (error) {
console.error('Transaction failed:', error);
logError('Transaction failed', error);
throw error;
}
});
Expand Down
58 changes: 58 additions & 0 deletions src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,62 @@ if (appConfigs.isDev || appConfigs.isTestMode || appConfigs.isLocalDev) {
);
}

/**
* Log an informational message with optional context metadata.
*
* @param message - Human-readable message
* @param context - Optional key/value metadata (service name, IDs, etc.)
*/
export const logInfo = (
message: string,
context?: Record<string, unknown>
): void => {
logger.info(message, context);
};

/**
* Log a warning with optional context metadata.
*/
export const logWarn = (
message: string,
context?: Record<string, unknown>
): void => {
logger.warn(message, context);
};

/**
* Log an error. Accepts an Error instance, a plain message, or both.
*
* @param message - Human-readable description of what went wrong
* @param error - The caught error/exception (optional)
* @param context - Additional key/value metadata (optional)
*/
export const logError = (
message: string,
error?: unknown,
context?: Record<string, unknown>
): void => {
const meta: Record<string, unknown> = { ...context };

if (error instanceof Error) {
meta.errorName = error.name;
meta.errorMessage = error.message;
meta.stack = error.stack;
} else if (error !== undefined) {
meta.error = error;
}

logger.error(message, meta);
};

/**
* Log a debug message (only emitted when log level is set to 'debug').
*/
export const logDebug = (
message: string,
context?: Record<string, unknown>
): void => {
logger.debug(message, context);
Comment on lines +76 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

logDebug is unreachable with the current logger configuration.

Line 6 hard-codes the logger level to info, so every call through this helper is discarded in all environments. If this helper is meant to be usable, drive the base level from config/env instead of a fixed value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/logger.ts` around lines 76 - 80, The logDebug helper is currently
unreachable because the shared logger is initialized with a fixed info level, so
debug calls are always dropped. Update the logger setup in logger.ts so the base
level is driven by configuration or environment instead of being hard-coded, and
keep logDebug delegating through the existing logger.debug path so it becomes
effective when debug logging is enabled.

};

export default logger;