diff --git a/src/shared/errors/index.ts b/src/shared/errors/index.ts new file mode 100644 index 0000000..ad6fb05 --- /dev/null +++ b/src/shared/errors/index.ts @@ -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'; + } +} \ No newline at end of file diff --git a/src/shared/logger/index.ts b/src/shared/logger/index.ts new file mode 100644 index 0000000..36eda38 --- /dev/null +++ b/src/shared/logger/index.ts @@ -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; \ No newline at end of file