feat: create shared errors and logger layer
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Base domain error class for all application-specific errors.
|
||||||
|
* Extends the built-in Error with a fixed name property for reliable
|
||||||
|
* instance checking across layers.
|
||||||
|
*/
|
||||||
|
export class DomainError extends Error {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'DomainError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a requested file cannot be found in storage.
|
||||||
|
*/
|
||||||
|
export class FileNotFoundError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'FileNotFoundError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a requested bucket does not exist.
|
||||||
|
*/
|
||||||
|
export class BucketNotFoundError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'BucketNotFoundError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a file exceeds the maximum allowed size for upload.
|
||||||
|
*/
|
||||||
|
export class FileTooLargeError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'FileTooLargeError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an attempt is made to upload a file that already exists
|
||||||
|
* (detected by content hash deduplication).
|
||||||
|
*/
|
||||||
|
export class DuplicateFileError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'DuplicateFileError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when authentication fails or a valid session is not present.
|
||||||
|
*/
|
||||||
|
export class AuthenticationError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'AuthenticationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when input validation fails (e.g. missing required fields,
|
||||||
|
* invalid format, or constraint violations).
|
||||||
|
*/
|
||||||
|
export class ValidationError extends DomainError {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import winston from 'winston';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application-wide logger singleton configured with Winston.
|
||||||
|
*
|
||||||
|
* Writes error-level logs to `logs/error.log`, all logs to
|
||||||
|
* `logs/combined.log`, and outputs to the console in both
|
||||||
|
* development (colorized, simple format) and production
|
||||||
|
* (JSON format) environments.
|
||||||
|
*/
|
||||||
|
const logger = winston.createLogger({
|
||||||
|
level: process.env.LOG_LEVEL || 'info',
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp(),
|
||||||
|
winston.format.errors({ stack: true }),
|
||||||
|
winston.format.json(),
|
||||||
|
),
|
||||||
|
defaultMeta: { service: 'filedrop' },
|
||||||
|
transports: [
|
||||||
|
// Write all logs including error logs to file
|
||||||
|
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
|
||||||
|
new winston.transports.File({ filename: 'logs/combined.log' }),
|
||||||
|
// Console transport for docker logs / CLI visibility
|
||||||
|
new winston.transports.Console({
|
||||||
|
format:
|
||||||
|
process.env.NODE_ENV !== 'production'
|
||||||
|
? winston.format.combine(winston.format.colorize(), winston.format.simple())
|
||||||
|
: winston.format.json(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default logger;
|
||||||
Reference in New Issue
Block a user