Files
hwinkel b22e5baa5c
Build and Push Docker Image / build (push) Successful in 1m23s
feat: add client-side validation utilities and debugging tools
- Implemented client-side validation functions for tax ID, VAT ID, IBAN, BIC, and website URL.
- Added debug logging functionality to assist in development.
- Created a comprehensive validation function for company form data.

feat: initialize database with Prisma migrations

- Added a server-side script to run Prisma migrations and check database health.
- Ensured safe initialization of the database to prevent concurrent migrations.

feat: comprehensive server-side error logging

- Developed an error logging system that captures detailed error context, including request details and stack traces.
- Implemented logging functions for different error types (route, action, database, API, startup).

fix: validate user ID existence in audit logs

- Updated the logging function to validate that the user ID exists in the database before logging actions.

fix: update schemas for optional fields and validation

- Modified schemas to allow for nullable fields and refined validation logic for tax ID, VAT ID, IBAN, and BIC.

feat: enhance error boundary for better debugging

- Improved error boundary to log detailed error information in development mode.
- Added a debug panel to the main application layout for real-time error tracking.

feat: implement company deletion functionality in admin routes

- Added a new API route for deleting companies with appropriate logging.
- Integrated delete confirmation in the admin interface for better user experience.

fix: handle API errors gracefully

- Wrapped API actions in try-catch blocks to log errors and return appropriate responses.

feat: generate and save invoice PDFs

- Implemented functionality to generate and save invoice PDFs upon status updates.
- Added a new column in the database for storing the URL of the generated PDF.

chore: update Docker image reference

- Changed the Docker image reference to point to the new Git repository.

chore: update package dependencies

- Added @radix-ui/react-tooltip for enhanced UI components.
- Updated package-lock.json to reflect new dependencies.
2026-05-03 08:46:58 +02:00

228 lines
5.2 KiB
TypeScript

/**
* Comprehensive server-side error logging for debugging
* Captures errors with full context: stack traces, request details, environment
*/
export interface ErrorContext {
request?: Request;
userId?: string | null;
route?: string;
metadata?: Record<string, unknown>;
}
export interface ErrorLogEntry {
timestamp: string;
type: string;
message: string;
stack?: string;
context?: {
method?: string;
url?: string;
route?: string;
userId?: string | null;
ipAddress?: string | null;
userAgent?: string;
metadata?: Record<string, unknown>;
};
environment: string;
}
/**
* Extract error message and stack from any error type
*/
function extractErrorInfo(error: unknown): { message: string; stack?: string } {
if (error instanceof Error) {
return {
message: error.message,
stack: error.stack,
};
}
if (typeof error === "string") {
return { message: error };
}
if (error && typeof error === "object") {
const err = error as Record<string, unknown>;
return {
message: (err.message as string) || String(error),
stack: (err.stack as string) || undefined,
};
}
return { message: String(error) };
}
/**
* Extract request context
*/
function extractRequestContext(
request?: Request
): ErrorLogEntry["context"] {
if (!request) return {};
const url = new URL(request.url);
const ipAddress =
request.headers.get("x-forwarded-for") ??
request.headers.get("x-real-ip") ??
null;
const userAgent = request.headers.get("user-agent");
return {
method: request.method,
url: url.pathname + url.search,
ipAddress,
userAgent: userAgent ?? undefined,
};
}
/**
* Build comprehensive error log entry
*/
function buildErrorLogEntry(
type: string,
error: unknown,
context?: ErrorContext
): ErrorLogEntry {
const { message, stack } = extractErrorInfo(error);
const requestContext = extractRequestContext(context?.request);
return {
timestamp: new Date().toISOString(),
type,
message,
stack,
context: {
...requestContext,
route: context?.route,
userId: context?.userId,
metadata: context?.metadata,
},
environment: process.env.NODE_ENV || "development",
};
}
/**
* Format error log for console output
*/
function formatErrorLog(entry: ErrorLogEntry): string {
const parts = [
`[${entry.type}]`,
`${entry.timestamp}`,
entry.message,
];
if (entry.context?.route) parts.push(`route: ${entry.context.route}`);
if (entry.context?.method && entry.context?.url) {
parts.push(`${entry.context.method} ${entry.context.url}`);
}
if (entry.context?.userId) parts.push(`user: ${entry.context.userId}`);
if (entry.context?.ipAddress) parts.push(`ip: ${entry.context.ipAddress}`);
let output = parts.join(" | ");
if (entry.stack) {
output += "\n" + entry.stack;
}
if (entry.context?.metadata) {
output += "\nMetadata: " + JSON.stringify(entry.context.metadata, null, 2);
}
return output;
}
/**
* Log a route/loader error
*/
export function logRouteError(
error: unknown,
context: ErrorContext & { route: string }
): void {
const entry = buildErrorLogEntry("ROUTE_ERROR", error, context);
console.error("\n" + "=".repeat(80));
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}
/**
* Log an action/mutation error
*/
export function logActionError(
error: unknown,
context: ErrorContext & { action: string }
): void {
const entry = buildErrorLogEntry("ACTION_ERROR", error, {
...context,
metadata: {
action: context.action,
...context.metadata,
},
});
console.error("\n" + "=".repeat(80));
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}
/**
* Log a database error
*/
export function logDatabaseError(
error: unknown,
operation: string,
context?: Omit<ErrorContext, "metadata">
): void {
const entry = buildErrorLogEntry("DATABASE_ERROR", error, {
...context,
metadata: { operation },
});
console.error("\n" + "=".repeat(80));
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}
/**
* Log an API error
*/
export function logApiError(
error: unknown,
context: ErrorContext & { endpoint: string; statusCode?: number }
): void {
const entry = buildErrorLogEntry("API_ERROR", error, {
...context,
metadata: {
endpoint: context.endpoint,
statusCode: context.statusCode || 500,
...context.metadata,
},
});
console.error("\n" + "=".repeat(80));
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}
/**
* Log a server startup error
*/
export function logStartupError(error: unknown): void {
const entry = buildErrorLogEntry("STARTUP_ERROR", error);
console.error("\n" + "=".repeat(80));
console.error("🚨 CRITICAL: Server failed to start");
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}
/**
* Log a generic error with type
*/
export function logError(
type: string,
error: unknown,
context?: ErrorContext
): void {
const entry = buildErrorLogEntry(type, error, context);
console.error("\n" + "=".repeat(80));
console.error(formatErrorLog(entry));
console.error("=".repeat(80) + "\n");
}