Add lock files for package management and update architecture documentation

- Introduced bun.lock and package-lock.json to manage dependencies for the project.
- Enhanced backend API architecture documentation with additional security and documentation guidelines.
- Made minor formatting adjustments across various files for consistency and clarity.
This commit is contained in:
2025-12-11 02:11:43 -05:00
parent 4911b5d125
commit 40210c454e
74 changed files with 2599 additions and 1386 deletions

View File

@@ -12,14 +12,14 @@ export class AssetRepository {
async findByIdAndUser(id: string, userId: string): Promise<Asset | null> {
return prisma.asset.findFirst({
where: {id, userId},
where: {id, userId}
});
}
async findAllByUser(userId: string, filters?: Record<string, any>): Promise<Asset[]> {
return prisma.asset.findMany({
where: {userId, ...filters},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -30,7 +30,7 @@ export class AssetRepository {
async update(id: string, data: Prisma.AssetUpdateInput): Promise<Asset> {
return prisma.asset.update({
where: {id},
data,
data
});
}
@@ -41,7 +41,7 @@ export class AssetRepository {
async getTotalValue(userId: string): Promise<number> {
const result = await prisma.asset.aggregate({
where: {userId},
_sum: {value: true},
_sum: {value: true}
});
return result._sum.value || 0;
}

View File

@@ -18,7 +18,7 @@ export class IncomeSourceRepository {
async findAllByUser(userId: string): Promise<IncomeSource[]> {
return prisma.incomeSource.findMany({
where: {userId},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -37,7 +37,7 @@ export class IncomeSourceRepository {
async getTotalMonthlyIncome(userId: string): Promise<number> {
const result = await prisma.incomeSource.aggregate({
where: {userId},
_sum: {amount: true},
_sum: {amount: true}
});
return result._sum.amount || 0;
}
@@ -58,7 +58,7 @@ export class ExpenseRepository {
async findAllByUser(userId: string): Promise<Expense[]> {
return prisma.expense.findMany({
where: {userId},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -77,18 +77,21 @@ export class ExpenseRepository {
async getTotalMonthlyExpenses(userId: string): Promise<number> {
const result = await prisma.expense.aggregate({
where: {userId},
_sum: {amount: true},
_sum: {amount: true}
});
return result._sum.amount || 0;
}
async getByCategory(userId: string): Promise<Record<string, Expense[]>> {
const expenses = await this.findAllByUser(userId);
return expenses.reduce((acc, expense) => {
if (!acc[expense.category]) acc[expense.category] = [];
acc[expense.category].push(expense);
return acc;
}, {} as Record<string, Expense[]>);
return expenses.reduce(
(acc, expense) => {
if (!acc[expense.category]) acc[expense.category] = [];
acc[expense.category].push(expense);
return acc;
},
{} as Record<string, Expense[]>
);
}
}
@@ -107,7 +110,7 @@ export class TransactionRepository {
async findAllByUser(userId: string): Promise<Transaction[]> {
return prisma.transaction.findMany({
where: {userId},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
@@ -127,38 +130,38 @@ export class TransactionRepository {
return prisma.transaction.findMany({
where: {
userId,
date: {gte: startDate, lte: endDate},
date: {gte: startDate, lte: endDate}
},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
async getByType(userId: string, type: string): Promise<Transaction[]> {
return prisma.transaction.findMany({
where: {userId, type},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
async getCashflowSummary(userId: string, startDate: Date, endDate: Date): Promise<{
async getCashflowSummary(
userId: string,
startDate: Date,
endDate: Date
): Promise<{
totalIncome: number;
totalExpenses: number;
netCashflow: number;
}> {
const transactions = await this.getByDateRange(userId, startDate, endDate);
const totalIncome = transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const totalIncome = transactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0);
const totalExpenses = transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
const totalExpenses = transactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0);
return {
totalIncome,
totalExpenses,
netCashflow: totalIncome - totalExpenses,
netCashflow: totalIncome - totalExpenses
};
}
}

View File

@@ -12,8 +12,8 @@ export class ClientRepository {
return prisma.client.findUnique({
where: {id},
include: {
invoices: true,
},
invoices: true
}
});
}
@@ -21,8 +21,8 @@ export class ClientRepository {
return prisma.client.findFirst({
where: {id, userId},
include: {
invoices: true,
},
invoices: true
}
});
}
@@ -31,10 +31,10 @@ export class ClientRepository {
where: {userId},
include: {
invoices: {
orderBy: {createdAt: 'desc'},
},
orderBy: {createdAt: 'desc'}
}
},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -42,8 +42,8 @@ export class ClientRepository {
return prisma.client.create({
data,
include: {
invoices: true,
},
invoices: true
}
});
}
@@ -52,14 +52,14 @@ export class ClientRepository {
where: {id},
data,
include: {
invoices: true,
},
invoices: true
}
});
}
async delete(id: string): Promise<void> {
await prisma.client.delete({
where: {id},
where: {id}
});
}
@@ -70,8 +70,8 @@ export class ClientRepository {
return prisma.client.findFirst({
where: {
userId,
email,
},
email
}
});
}
@@ -82,13 +82,13 @@ export class ClientRepository {
const result = await prisma.invoice.aggregate({
where: {
client: {
userId,
userId
},
status: 'paid',
status: 'paid'
},
_sum: {
total: true,
},
total: true
}
});
return result._sum.total || 0;
@@ -105,11 +105,11 @@ export class ClientRepository {
select: {
id: true,
total: true,
status: true,
},
},
status: true
}
}
},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
return clients.map(client => ({
@@ -117,13 +117,9 @@ export class ClientRepository {
stats: {
totalInvoices: client.invoices.length,
paidInvoices: client.invoices.filter(inv => inv.status === 'paid').length,
totalRevenue: client.invoices
.filter(inv => inv.status === 'paid')
.reduce((sum, inv) => sum + inv.total, 0),
outstandingAmount: client.invoices
.filter(inv => inv.status !== 'paid')
.reduce((sum, inv) => sum + inv.total, 0),
},
totalRevenue: client.invoices.filter(inv => inv.status === 'paid').reduce((sum, inv) => sum + inv.total, 0),
outstandingAmount: client.invoices.filter(inv => inv.status !== 'paid').reduce((sum, inv) => sum + inv.total, 0)
}
}));
}
}

View File

@@ -14,9 +14,9 @@ export class DebtAccountRepository {
include: {
category: true,
payments: {
orderBy: {date: 'desc'},
},
},
orderBy: {date: 'desc'}
}
}
});
}
@@ -26,9 +26,9 @@ export class DebtAccountRepository {
include: {
category: true,
payments: {
orderBy: {date: 'desc'},
},
},
orderBy: {date: 'desc'}
}
}
});
}
@@ -38,10 +38,10 @@ export class DebtAccountRepository {
include: {
category: true,
payments: {
orderBy: {date: 'desc'},
},
orderBy: {date: 'desc'}
}
},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -50,10 +50,10 @@ export class DebtAccountRepository {
where: {categoryId},
include: {
payments: {
orderBy: {date: 'desc'},
},
orderBy: {date: 'desc'}
}
},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -62,8 +62,8 @@ export class DebtAccountRepository {
data,
include: {
category: true,
payments: true,
},
payments: true
}
});
}
@@ -73,14 +73,14 @@ export class DebtAccountRepository {
data,
include: {
category: true,
payments: true,
},
payments: true
}
});
}
async delete(id: string): Promise<void> {
await prisma.debtAccount.delete({
where: {id},
where: {id}
});
}
@@ -91,8 +91,8 @@ export class DebtAccountRepository {
const result = await prisma.debtAccount.aggregate({
where: {userId},
_sum: {
currentBalance: true,
},
currentBalance: true
}
});
return result._sum.currentBalance || 0;
@@ -107,9 +107,9 @@ export class DebtAccountRepository {
include: {
category: true,
payments: {
orderBy: {date: 'desc'},
},
},
orderBy: {date: 'desc'}
}
}
});
return accounts.map(account => {
@@ -122,8 +122,8 @@ export class DebtAccountRepository {
totalPaid,
numberOfPayments: account.payments.length,
lastPaymentDate: lastPayment?.date || null,
lastPaymentAmount: lastPayment?.amount || null,
},
lastPaymentAmount: lastPayment?.amount || null
}
};
});
}

View File

@@ -15,10 +15,10 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
include: {
accounts: {
include: {
payments: true,
},
},
},
payments: true
}
}
}
});
}
@@ -28,12 +28,12 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
include: {
accounts: {
include: {
payments: true,
payments: true
},
orderBy: {createdAt: 'desc'},
},
orderBy: {createdAt: 'desc'}
}
},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
@@ -41,8 +41,8 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
return prisma.debtCategory.create({
data,
include: {
accounts: true,
},
accounts: true
}
});
}
@@ -51,14 +51,14 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
where: {id},
data,
include: {
accounts: true,
},
accounts: true
}
});
}
async delete(id: string): Promise<void> {
await prisma.debtCategory.delete({
where: {id},
where: {id}
});
}
@@ -69,8 +69,8 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
return prisma.debtCategory.findFirst({
where: {
userId,
name,
},
name
}
});
}
@@ -81,8 +81,8 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
const result = await prisma.debtAccount.aggregate({
where: {categoryId},
_sum: {
currentBalance: true,
},
currentBalance: true
}
});
return result._sum.currentBalance || 0;
@@ -97,19 +97,15 @@ export class DebtCategoryRepository implements IUserScopedRepository<DebtCategor
return Promise.all(
categories.map(async category => {
const totalDebt = await this.getTotalDebt(category.id);
const totalPayments = category.accounts.reduce(
(sum, account) =>
sum + account.payments.reduce((pSum, payment) => pSum + payment.amount, 0),
0
);
const totalPayments = category.accounts.reduce((sum, account) => sum + account.payments.reduce((pSum, payment) => pSum + payment.amount, 0), 0);
return {
...category,
stats: {
totalAccounts: category.accounts.length,
totalDebt,
totalPayments,
},
totalPayments
}
};
})
);

View File

@@ -14,17 +14,17 @@ export class DebtPaymentRepository {
include: {
account: {
include: {
category: true,
},
},
},
category: true
}
}
}
});
}
async findByAccount(accountId: string): Promise<DebtPayment[]> {
return prisma.debtPayment.findMany({
where: {accountId},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
@@ -33,18 +33,18 @@ export class DebtPaymentRepository {
where: {
account: {
category: {
userId,
},
},
userId
}
}
},
include: {
account: {
include: {
category: true,
},
},
category: true
}
}
},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
@@ -54,16 +54,16 @@ export class DebtPaymentRepository {
include: {
account: {
include: {
category: true,
},
},
},
category: true
}
}
}
});
}
async delete(id: string): Promise<void> {
await prisma.debtPayment.delete({
where: {id},
where: {id}
});
}
@@ -74,8 +74,8 @@ export class DebtPaymentRepository {
const result = await prisma.debtPayment.aggregate({
where: {accountId},
_sum: {
amount: true,
},
amount: true
}
});
return result._sum.amount || 0;
@@ -89,13 +89,13 @@ export class DebtPaymentRepository {
where: {
account: {
category: {
userId,
},
},
userId
}
}
},
_sum: {
amount: true,
},
amount: true
}
});
return result._sum.amount || 0;
@@ -109,22 +109,22 @@ export class DebtPaymentRepository {
where: {
account: {
category: {
userId,
},
userId
}
},
date: {
gte: startDate,
lte: endDate,
},
lte: endDate
}
},
include: {
account: {
include: {
category: true,
},
},
category: true
}
}
},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
}

View File

@@ -15,14 +15,14 @@ export class InvoiceRepository implements IUserScopedRepository<Invoice> {
async findById(id: string): Promise<Invoice | null> {
return prisma.invoice.findUnique({
where: {id},
include: {lineItems: true, client: true},
include: {lineItems: true, client: true}
}) as unknown as Invoice;
}
async findByIdAndUser(id: string, userId: string): Promise<InvoiceWithLineItems | null> {
return prisma.invoice.findFirst({
where: {id, userId},
include: {lineItems: true, client: true},
include: {lineItems: true, client: true}
});
}
@@ -30,14 +30,14 @@ export class InvoiceRepository implements IUserScopedRepository<Invoice> {
return prisma.invoice.findMany({
where: {userId, ...filters},
include: {lineItems: true, client: true},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
async create(data: Prisma.InvoiceCreateInput): Promise<Invoice> {
return prisma.invoice.create({
data,
include: {lineItems: true, client: true},
include: {lineItems: true, client: true}
}) as unknown as Invoice;
}
@@ -45,7 +45,7 @@ export class InvoiceRepository implements IUserScopedRepository<Invoice> {
return prisma.invoice.update({
where: {id},
data,
include: {lineItems: true, client: true},
include: {lineItems: true, client: true}
}) as unknown as Invoice;
}
@@ -58,8 +58,8 @@ export class InvoiceRepository implements IUserScopedRepository<Invoice> {
where: {
userId,
invoiceNumber,
...(excludeId && {id: {not: excludeId}}),
},
...(excludeId && {id: {not: excludeId}})
}
});
return count > 0;
}
@@ -69,8 +69,8 @@ export class InvoiceRepository implements IUserScopedRepository<Invoice> {
const count = await prisma.invoice.count({
where: {
userId,
invoiceNumber: {startsWith: `INV-${year}-`},
},
invoiceNumber: {startsWith: `INV-${year}-`}
}
});
return `INV-${year}-${String(count + 1).padStart(3, '0')}`;
}

View File

@@ -10,39 +10,39 @@ const prisma = DatabaseConnection.getInstance();
export class LiabilityRepository {
async findById(id: string): Promise<Liability | null> {
return prisma.liability.findUnique({
where: {id},
where: {id}
});
}
async findByIdAndUser(id: string, userId: string): Promise<Liability | null> {
return prisma.liability.findFirst({
where: {id, userId},
where: {id, userId}
});
}
async findAllByUser(userId: string): Promise<Liability[]> {
return prisma.liability.findMany({
where: {userId},
orderBy: {createdAt: 'desc'},
orderBy: {createdAt: 'desc'}
});
}
async create(data: Prisma.LiabilityCreateInput): Promise<Liability> {
return prisma.liability.create({
data,
data
});
}
async update(id: string, data: Prisma.LiabilityUpdateInput): Promise<Liability> {
return prisma.liability.update({
where: {id},
data,
data
});
}
async delete(id: string): Promise<void> {
await prisma.liability.delete({
where: {id},
where: {id}
});
}
@@ -53,8 +53,8 @@ export class LiabilityRepository {
const result = await prisma.liability.aggregate({
where: {userId},
_sum: {
balance: true,
},
balance: true
}
});
return result._sum.balance || 0;
@@ -66,13 +66,16 @@ export class LiabilityRepository {
async getByType(userId: string): Promise<Record<string, Liability[]>> {
const liabilities = await this.findAllByUser(userId);
return liabilities.reduce((acc, liability) => {
const type = liability.type;
if (!acc[type]) {
acc[type] = [];
}
acc[type].push(liability);
return acc;
}, {} as Record<string, Liability[]>);
return liabilities.reduce(
(acc, liability) => {
const type = liability.type;
if (!acc[type]) {
acc[type] = [];
}
acc[type].push(liability);
return acc;
},
{} as Record<string, Liability[]>
);
}
}

View File

@@ -11,33 +11,33 @@ const prisma = DatabaseConnection.getInstance();
export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWorthSnapshot> {
async findById(id: string): Promise<NetWorthSnapshot | null> {
return prisma.netWorthSnapshot.findUnique({
where: {id},
where: {id}
});
}
async findAllByUser(userId: string): Promise<NetWorthSnapshot[]> {
return prisma.netWorthSnapshot.findMany({
where: {userId},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
async create(data: Prisma.NetWorthSnapshotCreateInput): Promise<NetWorthSnapshot> {
return prisma.netWorthSnapshot.create({
data,
data
});
}
async update(id: string, data: Prisma.NetWorthSnapshotUpdateInput): Promise<NetWorthSnapshot> {
return prisma.netWorthSnapshot.update({
where: {id},
data,
data
});
}
async delete(id: string): Promise<void> {
await prisma.netWorthSnapshot.delete({
where: {id},
where: {id}
});
}
@@ -47,7 +47,7 @@ export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWort
async getLatest(userId: string): Promise<NetWorthSnapshot | null> {
return prisma.netWorthSnapshot.findFirst({
where: {userId},
orderBy: {date: 'desc'},
orderBy: {date: 'desc'}
});
}
@@ -60,10 +60,10 @@ export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWort
userId,
date: {
gte: startDate,
lte: endDate,
},
lte: endDate
}
},
orderBy: {date: 'asc'},
orderBy: {date: 'asc'}
});
}
@@ -74,8 +74,8 @@ export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWort
const count = await prisma.netWorthSnapshot.count({
where: {
userId,
date,
},
date
}
});
return count > 0;
@@ -88,7 +88,7 @@ export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWort
const snapshots = await prisma.netWorthSnapshot.findMany({
where: {userId},
orderBy: {date: 'desc'},
take: limit,
take: limit
});
const stats = [];
@@ -96,14 +96,13 @@ export class NetWorthSnapshotRepository implements IUserScopedRepository<NetWort
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;
const growthPercent = previous.netWorth !== 0 ? (growthAmount / previous.netWorth) * 100 : 0;
stats.push({
date: current.date,
netWorth: current.netWorth,
growthAmount,
growthPercent: parseFloat(growthPercent.toFixed(2)),
growthPercent: parseFloat(growthPercent.toFixed(2))
});
}

View File

@@ -24,8 +24,8 @@ export class UserRepository implements IRepository<User> {
name: true,
createdAt: true,
updatedAt: true,
password: false, // Never return password
},
password: false // Never return password
}
}) as unknown as User[];
}
@@ -36,7 +36,7 @@ export class UserRepository implements IRepository<User> {
async update(id: string, data: Prisma.UserUpdateInput): Promise<User> {
return prisma.user.update({
where: {id},
data,
data
});
}

View File

@@ -15,8 +15,7 @@ export interface IRepository<T, CreateInput = unknown, UpdateInput = unknown> {
* User-scoped repository interface
* For entities that belong to a specific user
*/
export interface IUserScopedRepository<T, CreateInput = unknown, UpdateInput = unknown>
extends Omit<IRepository<T, CreateInput, UpdateInput>, 'findAll'> {
export interface IUserScopedRepository<T, CreateInput = unknown, UpdateInput = unknown> extends Omit<IRepository<T, CreateInput, UpdateInput>, 'findAll'> {
findAllByUser(userId: string, filters?: Record<string, unknown>): Promise<T[]>;
findByIdAndUser(id: string, userId: string): Promise<T | null>;
}