fix: fix bug in rebalanace, stale session values being read
All checks were successful
Deploy / deploy (push) Successful in 1m30s
Security Tests / security-non-db (push) Successful in 20s
Security Tests / security-db (push) Successful in 24s

This commit is contained in:
2026-03-12 13:51:46 -05:00
parent a03fbea5e7
commit 58545b2da7
3 changed files with 52 additions and 7 deletions

View File

@@ -3417,6 +3417,20 @@ async function ensureBudgetSession(app: any, userId: string, fallbackAvailableCe
});
}
async function ensureBudgetSessionAvailableSynced(
app: any,
userId: string,
availableCents: number
) {
const normalizedAvailableCents = BigInt(Math.max(0, Math.trunc(availableCents)));
const session = await ensureBudgetSession(app, userId, Number(normalizedAvailableCents));
if ((session.availableCents ?? 0n) === normalizedAvailableCents) return session;
return app.prisma.budgetSession.update({
where: { id: session.id },
data: { availableCents: normalizedAvailableCents },
});
}
app.post("/variable-categories", mutationRateLimit, async (req, reply) => {
const parsed = CatBody.safeParse(req.body);
if (!parsed.success) {
@@ -3555,11 +3569,11 @@ app.get("/variable-categories/manual-rebalance", async (req, reply) => {
select: { id: true, name: true, percent: true, isSavings: true, balanceCents: true },
});
const totalBalance = cats.reduce((s, c) => s + Number(c.balanceCents ?? 0n), 0);
const session = await ensureBudgetSession(app, userId, totalBalance);
await ensureBudgetSessionAvailableSynced(app, userId, totalBalance);
return reply.send({
ok: true,
availableCents: Number(session.availableCents ?? 0n),
availableCents: totalBalance,
categories: cats.map((c) => ({ ...c, balanceCents: Number(c.balanceCents ?? 0n) })),
});
});
@@ -3579,8 +3593,8 @@ app.post("/variable-categories/manual-rebalance", async (req, reply) => {
if (cats.length === 0) return reply.code(400).send({ ok: false, code: "NO_CATEGORIES" });
const totalBalance = cats.reduce((s, c) => s + Number(c.balanceCents ?? 0n), 0);
const session = await ensureBudgetSession(app, userId, totalBalance);
const availableCents = Number(session.availableCents ?? 0n);
const availableCents = totalBalance;
await ensureBudgetSessionAvailableSynced(app, userId, availableCents);
const targetMap = new Map<string, number>();
for (const t of parsed.data.targets) {