Add backend API for personal finance management application

- Introduced a comprehensive backend API using TypeScript, Fastify, and PostgreSQL.
- Added essential files including architecture documentation, environment configuration, and Docker setup.
- Implemented RESTful routes for managing assets, liabilities, clients, invoices, and cashflow.
- Established a robust database schema with Prisma for data management.
- Integrated middleware for authentication and error handling.
- Created service and repository layers to adhere to SOLID principles and clean architecture.
- Included example environment variables for development, staging, and production setups.
This commit is contained in:
2025-12-07 12:59:09 -05:00
parent 9d493ba82f
commit cd93dcbfd2
70 changed files with 8649 additions and 6 deletions

View File

@@ -0,0 +1,38 @@
/**
* Custom error classes
* Implements Open/Closed Principle: Extensible for new error types
*/
export abstract class AppError extends Error {
abstract statusCode: number;
constructor(message: string) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
export class NotFoundError extends AppError {
statusCode = 404;
}
export class ValidationError extends AppError {
statusCode = 400;
}
export class UnauthorizedError extends AppError {
statusCode = 401;
}
export class ForbiddenError extends AppError {
statusCode = 403;
}
export class ConflictError extends AppError {
statusCode = 409;
}
export class InternalServerError extends AppError {
statusCode = 500;
}

View File

@@ -0,0 +1,39 @@
import bcrypt from 'bcryptjs';
/**
* Password hashing utilities
* Implements Single Responsibility: Only handles password operations
*/
export class PasswordService {
private static readonly SALT_ROUNDS = 10;
static async hash(password: string): Promise<string> {
return bcrypt.hash(password, this.SALT_ROUNDS);
}
static async compare(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
static validate(password: string): {valid: boolean; errors: string[]} {
const errors: string[] = [];
if (password.length < 8) {
errors.push('Password must be at least 8 characters');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain at least one uppercase letter');
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain at least one lowercase letter');
}
if (!/[0-9]/.test(password)) {
errors.push('Password must contain at least one number');
}
return {
valid: errors.length === 0,
errors,
};
}
}