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,112 @@
import {NetWorthSnapshot, Prisma} from '@prisma/client';
import {DatabaseConnection} from '../config/database';
import {IUserScopedRepository} from './interfaces/IRepository';
const prisma = DatabaseConnection.getInstance();
/**
* Repository for NetWorthSnapshot data access
* Implements Single Responsibility Principle - handles only database operations
*/
export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWorthSnapshot> {
async findById(id: string): Promise<NetWorthSnapshot | null> {
return prisma.netWorthSnapshot.findUnique({
where: {id},
});
}
async findAllByUser(userId: string): Promise<NetWorthSnapshot[]> {
return prisma.netWorthSnapshot.findMany({
where: {userId},
orderBy: {date: 'desc'},
});
}
async create(data: Prisma.NetWorthSnapshotCreateInput): Promise<NetWorthSnapshot> {
return prisma.netWorthSnapshot.create({
data,
});
}
async update(id: string, data: Prisma.NetWorthSnapshotUpdateInput): Promise<NetWorthSnapshot> {
return prisma.netWorthSnapshot.update({
where: {id},
data,
});
}
async delete(id: string): Promise<void> {
await prisma.netWorthSnapshot.delete({
where: {id},
});
}
/**
* Get the latest snapshot for a user
*/
async getLatest(userId: string): Promise<NetWorthSnapshot | null> {
return prisma.netWorthSnapshot.findFirst({
where: {userId},
orderBy: {date: 'desc'},
});
}
/**
* Get snapshots within a date range
*/
async getByDateRange(userId: string, startDate: Date, endDate: Date): Promise<NetWorthSnapshot[]> {
return prisma.netWorthSnapshot.findMany({
where: {
userId,
date: {
gte: startDate,
lte: endDate,
},
},
orderBy: {date: 'asc'},
});
}
/**
* Check if a snapshot exists for a specific date
*/
async existsForDate(userId: string, date: Date): Promise<boolean> {
const count = await prisma.netWorthSnapshot.count({
where: {
userId,
date,
},
});
return count > 0;
}
/**
* Get growth over time (percentage change between snapshots)
*/
async getGrowthStats(userId: string, limit: number = 12): Promise<any[]> {
const snapshots = await prisma.netWorthSnapshot.findMany({
where: {userId},
orderBy: {date: 'desc'},
take: limit,
});
const stats = [];
for (let i = 0; i < snapshots.length - 1; i++) {
const current = snapshots[i];
const previous = snapshots[i + 1];
const growthAmount = current.netWorth - previous.netWorth;
const growthPercent =
previous.netWorth !== 0 ? (growthAmount / previous.netWorth) * 100 : 0;
stats.push({
date: current.date,
netWorth: current.netWorth,
growthAmount,
growthPercent: parseFloat(growthPercent.toFixed(2)),
});
}
return stats;
}
}