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,89 @@
import {FastifyRequest, FastifyReply} from 'fastify';
import {DebtCategoryService} from '../services/DebtCategoryService';
import {getUserId} from '../middleware/auth';
import {z} from 'zod';
const createCategorySchema = z.object({
name: z.string().min(1).max(255),
description: z.string().optional(),
color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(),
});
const updateCategorySchema = z.object({
name: z.string().min(1).max(255).optional(),
description: z.string().optional(),
color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(),
});
/**
* Controller for DebtCategory endpoints
* Implements Single Responsibility Principle - handles only HTTP layer
*/
export class DebtCategoryController {
constructor(private categoryService: DebtCategoryService) {}
/**
* Create a new debt category
*/
async create(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
const data = createCategorySchema.parse(request.body);
const category = await this.categoryService.create(userId, data);
return reply.status(201).send({category});
}
/**
* Get all debt categories
*/
async getAll(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
const {withStats} = request.query as {withStats?: string};
if (withStats === 'true') {
const categories = await this.categoryService.getWithStats(userId);
return reply.send({categories});
}
const categories = await this.categoryService.getAllByUser(userId);
return reply.send({categories});
}
/**
* Get a single debt category
*/
async getOne(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
const {id} = request.params as {id: string};
const category = await this.categoryService.getById(id, userId);
return reply.send({category});
}
/**
* Update a debt category
*/
async update(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
const {id} = request.params as {id: string};
const data = updateCategorySchema.parse(request.body);
const category = await this.categoryService.update(id, userId, data);
return reply.send({category});
}
/**
* Delete a debt category
*/
async delete(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
const {id} = request.params as {id: string};
await this.categoryService.delete(id, userId);
return reply.status(204).send();
}
}