feat: add Redis caching to cospend API endpoints
All checks were successful
CI / update (push) Successful in 1m21s
All checks were successful
CI / update (push) Successful in 1m21s
Implement comprehensive caching for all cospend routes to improve performance: Cache Implementation: - Balance API: 30-minute TTL for user balances and global balances - Debts API: 15-minute TTL for debt breakdown calculations - Payments List: 10-minute TTL with pagination support - Individual Payment: 30-minute TTL for payment details Cache Invalidation: - Created invalidateCospendCaches() helper function - Invalidates user balances, debts, and payment lists on mutations - Applied to payment create, update, and delete operations - Applied to recurring payment execution (manual and cron)
This commit is contained in:
@@ -302,10 +302,44 @@ export async function invalidateRecipeCaches(): Promise<void> {
|
|||||||
cache.delPattern('recipes:category:*'),
|
cache.delPattern('recipes:category:*'),
|
||||||
cache.delPattern('recipes:icon:*'),
|
cache.delPattern('recipes:icon:*'),
|
||||||
]);
|
]);
|
||||||
console.log('[Cache] Invalidated all recipe caches');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Cache] Error invalidating recipe caches:', err);
|
console.error('[Cache] Error invalidating recipe caches:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to invalidate cospend caches for specific users and/or payments
|
||||||
|
* Call this after payment create/update/delete operations
|
||||||
|
* @param usernames - Array of usernames whose caches should be invalidated
|
||||||
|
* @param paymentId - Optional payment ID to invalidate specific payment cache
|
||||||
|
*/
|
||||||
|
export async function invalidateCospendCaches(usernames: string[], paymentId?: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const invalidations: Promise<any>[] = [];
|
||||||
|
|
||||||
|
// Invalidate balance and debts caches for all affected users
|
||||||
|
for (const username of usernames) {
|
||||||
|
invalidations.push(
|
||||||
|
cache.del(`cospend:balance:${username}`),
|
||||||
|
cache.del(`cospend:debts:${username}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate global balance cache
|
||||||
|
invalidations.push(cache.del('cospend:balance:all'));
|
||||||
|
|
||||||
|
// Invalidate payment list caches (all pagination variants)
|
||||||
|
invalidations.push(cache.delPattern('cospend:payments:list:*'));
|
||||||
|
|
||||||
|
// If specific payment ID provided, invalidate its cache
|
||||||
|
if (paymentId) {
|
||||||
|
invalidations.push(cache.del(`cospend:payment:${paymentId}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(invalidations);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Cache] Error invalidating cospend caches:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default cache;
|
export default cache;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PaymentSplit } from '../../../../models/PaymentSplit';
|
|||||||
import { Payment } from '../../../../models/Payment'; // Need to import Payment for populate to work
|
import { Payment } from '../../../../models/Payment'; // Need to import Payment for populate to work
|
||||||
import { dbConnect } from '../../../../utils/db';
|
import { dbConnect } from '../../../../utils/db';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
|
import cache from '$lib/server/cache';
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ locals, url }) => {
|
export const GET: RequestHandler = async ({ locals, url }) => {
|
||||||
const auth = await locals.auth();
|
const auth = await locals.auth();
|
||||||
@@ -14,9 +15,17 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
const includeAll = url.searchParams.get('all') === 'true';
|
const includeAll = url.searchParams.get('all') === 'true';
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (includeAll) {
|
if (includeAll) {
|
||||||
|
// Try cache first for all balances
|
||||||
|
const cacheKey = 'cospend:balance:all';
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
return json(JSON.parse(cached));
|
||||||
|
}
|
||||||
|
|
||||||
const allSplits = await PaymentSplit.aggregate([
|
const allSplits = await PaymentSplit.aggregate([
|
||||||
{
|
{
|
||||||
$group: {
|
$group: {
|
||||||
@@ -44,14 +53,27 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
netBalance: 0
|
netBalance: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return json({
|
const result = {
|
||||||
currentUser: currentUserBalance,
|
currentUser: currentUserBalance,
|
||||||
allBalances: allSplits
|
allBalances: allSplits
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Cache for 30 minutes
|
||||||
|
await cache.set(cacheKey, JSON.stringify(result), 1800);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Try cache first for individual user balance
|
||||||
|
const cacheKey = `cospend:balance:${username}`;
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
return json(JSON.parse(cached));
|
||||||
|
}
|
||||||
|
|
||||||
const userSplits = await PaymentSplit.find({ username }).lean();
|
const userSplits = await PaymentSplit.find({ username }).lean();
|
||||||
|
|
||||||
// Calculate net balance: negative = you are owed money, positive = you owe money
|
// Calculate net balance: negative = you are owed money, positive = you owe money
|
||||||
const netBalance = userSplits.reduce((sum, split) => sum + split.amount, 0);
|
const netBalance = userSplits.reduce((sum, split) => sum + split.amount, 0);
|
||||||
|
|
||||||
@@ -78,17 +100,22 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
paymentId: split.paymentId._id,
|
paymentId: split.paymentId._id,
|
||||||
username: { $ne: username }
|
username: { $ne: username }
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
if (otherSplit) {
|
if (otherSplit) {
|
||||||
split.otherUser = otherSplit.username;
|
split.otherUser = otherSplit.username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return json({
|
const result = {
|
||||||
netBalance,
|
netBalance,
|
||||||
recentSplits
|
recentSplits
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Cache for 30 minutes
|
||||||
|
await cache.set(cacheKey, JSON.stringify(result), 1800);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PaymentSplit } from '../../../../models/PaymentSplit';
|
|||||||
import { Payment } from '../../../../models/Payment';
|
import { Payment } from '../../../../models/Payment';
|
||||||
import { dbConnect } from '../../../../utils/db';
|
import { dbConnect } from '../../../../utils/db';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
|
import cache from '$lib/server/cache';
|
||||||
|
|
||||||
interface DebtSummary {
|
interface DebtSummary {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -25,8 +26,16 @@ export const GET: RequestHandler = async ({ locals }) => {
|
|||||||
const currentUser = auth.user.nickname;
|
const currentUser = auth.user.nickname;
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Try cache first
|
||||||
|
const cacheKey = `cospend:debts:${currentUser}`;
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
return json(JSON.parse(cached));
|
||||||
|
}
|
||||||
|
|
||||||
// Get all splits for the current user
|
// Get all splits for the current user
|
||||||
const userSplits = await PaymentSplit.find({ username: currentUser })
|
const userSplits = await PaymentSplit.find({ username: currentUser })
|
||||||
.populate('paymentId')
|
.populate('paymentId')
|
||||||
@@ -50,13 +59,13 @@ export const GET: RequestHandler = async ({ locals }) => {
|
|||||||
if (!payment) continue;
|
if (!payment) continue;
|
||||||
|
|
||||||
// Find other participants in this payment
|
// Find other participants in this payment
|
||||||
const otherSplits = allRelatedSplits.filter(s =>
|
const otherSplits = allRelatedSplits.filter(s =>
|
||||||
s.paymentId._id.toString() === split.paymentId._id.toString()
|
s.paymentId._id.toString() === split.paymentId._id.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const otherSplit of otherSplits) {
|
for (const otherSplit of otherSplits) {
|
||||||
const otherUser = otherSplit.username;
|
const otherUser = otherSplit.username;
|
||||||
|
|
||||||
if (!debtsByUser.has(otherUser)) {
|
if (!debtsByUser.has(otherUser)) {
|
||||||
debtsByUser.set(otherUser, {
|
debtsByUser.set(otherUser, {
|
||||||
username: otherUser,
|
username: otherUser,
|
||||||
@@ -66,11 +75,11 @@ export const GET: RequestHandler = async ({ locals }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const debt = debtsByUser.get(otherUser)!;
|
const debt = debtsByUser.get(otherUser)!;
|
||||||
|
|
||||||
// Current user's amount: positive = they owe, negative = they are owed
|
// Current user's amount: positive = they owe, negative = they are owed
|
||||||
// We want to show net between the two users
|
// We want to show net between the two users
|
||||||
debt.netAmount += split.amount;
|
debt.netAmount += split.amount;
|
||||||
|
|
||||||
debt.transactions.push({
|
debt.transactions.push({
|
||||||
paymentId: payment._id.toString(),
|
paymentId: payment._id.toString(),
|
||||||
title: payment.title,
|
title: payment.title,
|
||||||
@@ -97,12 +106,17 @@ export const GET: RequestHandler = async ({ locals }) => {
|
|||||||
netAmount: Math.round(debt.netAmount * 100) / 100 // Round to 2 decimal places
|
netAmount: Math.round(debt.netAmount * 100) / 100 // Round to 2 decimal places
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return json({
|
const result = {
|
||||||
whoOwesMe,
|
whoOwesMe,
|
||||||
whoIOwe,
|
whoIOwe,
|
||||||
totalOwedToMe: whoOwesMe.reduce((sum, debt) => sum + debt.netAmount, 0),
|
totalOwedToMe: whoOwesMe.reduce((sum, debt) => sum + debt.netAmount, 0),
|
||||||
totalIOwe: whoIOwe.reduce((sum, debt) => sum + debt.netAmount, 0)
|
totalIOwe: whoIOwe.reduce((sum, debt) => sum + debt.netAmount, 0)
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Cache for 15 minutes (as suggested in plan for debt breakdown)
|
||||||
|
await cache.set(cacheKey, JSON.stringify(result), 900);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error calculating debt breakdown:', e);
|
console.error('Error calculating debt breakdown:', e);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { PaymentSplit } from '../../../../models/PaymentSplit';
|
|||||||
import { dbConnect } from '../../../../utils/db';
|
import { dbConnect } from '../../../../utils/db';
|
||||||
import { convertToCHF, isValidCurrencyCode } from '../../../../lib/utils/currency';
|
import { convertToCHF, isValidCurrencyCode } from '../../../../lib/utils/currency';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
|
import cache, { invalidateCospendCaches } from '$lib/server/cache';
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ locals, url }) => {
|
export const GET: RequestHandler = async ({ locals, url }) => {
|
||||||
const auth = await locals.auth();
|
const auth = await locals.auth();
|
||||||
@@ -15,8 +16,16 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
const offset = parseInt(url.searchParams.get('offset') || '0');
|
const offset = parseInt(url.searchParams.get('offset') || '0');
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Try cache first (include pagination params in key)
|
||||||
|
const cacheKey = `cospend:payments:list:${limit}:${offset}`;
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
return json(JSON.parse(cached));
|
||||||
|
}
|
||||||
|
|
||||||
const payments = await Payment.find()
|
const payments = await Payment.find()
|
||||||
.populate('splits')
|
.populate('splits')
|
||||||
.sort({ date: -1, createdAt: -1 })
|
.sort({ date: -1, createdAt: -1 })
|
||||||
@@ -24,7 +33,12 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
.skip(offset)
|
.skip(offset)
|
||||||
.lean();
|
.lean();
|
||||||
|
|
||||||
return json({ payments });
|
const result = { payments };
|
||||||
|
|
||||||
|
// Cache for 10 minutes (shorter TTL since this changes frequently)
|
||||||
|
await cache.set(cacheKey, JSON.stringify(result), 600);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw error(500, 'Failed to fetch payments');
|
throw error(500, 'Failed to fetch payments');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -138,9 +152,13 @@ export const POST: RequestHandler = async ({ request, locals }) => {
|
|||||||
|
|
||||||
await Promise.all(splitPromises);
|
await Promise.all(splitPromises);
|
||||||
|
|
||||||
return json({
|
// Invalidate caches for all affected users
|
||||||
success: true,
|
const affectedUsernames = splits.map((split: any) => split.username);
|
||||||
payment: payment._id
|
await invalidateCospendCaches(affectedUsernames, payment._id.toString());
|
||||||
|
|
||||||
|
return json({
|
||||||
|
success: true,
|
||||||
|
payment: payment._id
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Payment } from '../../../../../models/Payment';
|
|||||||
import { PaymentSplit } from '../../../../../models/PaymentSplit';
|
import { PaymentSplit } from '../../../../../models/PaymentSplit';
|
||||||
import { dbConnect } from '../../../../../utils/db';
|
import { dbConnect } from '../../../../../utils/db';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
|
import cache, { invalidateCospendCaches } from '$lib/server/cache';
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ params, locals }) => {
|
export const GET: RequestHandler = async ({ params, locals }) => {
|
||||||
const auth = await locals.auth();
|
const auth = await locals.auth();
|
||||||
@@ -13,15 +14,28 @@ export const GET: RequestHandler = async ({ params, locals }) => {
|
|||||||
const { id } = params;
|
const { id } = params;
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Try cache first
|
||||||
|
const cacheKey = `cospend:payment:${id}`;
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
return json(JSON.parse(cached));
|
||||||
|
}
|
||||||
|
|
||||||
const payment = await Payment.findById(id).populate('splits').lean();
|
const payment = await Payment.findById(id).populate('splits').lean();
|
||||||
|
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
throw error(404, 'Payment not found');
|
throw error(404, 'Payment not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return json({ payment });
|
const result = { payment };
|
||||||
|
|
||||||
|
// Cache for 30 minutes
|
||||||
|
await cache.set(cacheKey, JSON.stringify(result), 1800);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.status === 404) throw e;
|
if (e.status === 404) throw e;
|
||||||
throw error(500, 'Failed to fetch payment');
|
throw error(500, 'Failed to fetch payment');
|
||||||
@@ -40,10 +54,10 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
const data = await request.json();
|
const data = await request.json();
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payment = await Payment.findById(id);
|
const payment = await Payment.findById(id);
|
||||||
|
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
throw error(404, 'Payment not found');
|
throw error(404, 'Payment not found');
|
||||||
}
|
}
|
||||||
@@ -52,9 +66,13 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
throw error(403, 'Not authorized to edit this payment');
|
throw error(403, 'Not authorized to edit this payment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get old splits to invalidate caches for users who were in the original payment
|
||||||
|
const oldSplits = await PaymentSplit.find({ paymentId: id }).lean();
|
||||||
|
const oldUsernames = oldSplits.map(split => split.username);
|
||||||
|
|
||||||
const updatedPayment = await Payment.findByIdAndUpdate(
|
const updatedPayment = await Payment.findByIdAndUpdate(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
amount: data.amount,
|
amount: data.amount,
|
||||||
@@ -67,9 +85,10 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
{ new: true }
|
{ new: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let newUsernames: string[] = [];
|
||||||
if (data.splits) {
|
if (data.splits) {
|
||||||
await PaymentSplit.deleteMany({ paymentId: id });
|
await PaymentSplit.deleteMany({ paymentId: id });
|
||||||
|
|
||||||
const splitPromises = data.splits.map((split: any) => {
|
const splitPromises = data.splits.map((split: any) => {
|
||||||
return PaymentSplit.create({
|
return PaymentSplit.create({
|
||||||
paymentId: id,
|
paymentId: id,
|
||||||
@@ -81,8 +100,13 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(splitPromises);
|
await Promise.all(splitPromises);
|
||||||
|
newUsernames = data.splits.map((split: any) => split.username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invalidate caches for all users (old and new)
|
||||||
|
const allAffectedUsers = [...new Set([...oldUsernames, ...newUsernames])];
|
||||||
|
await invalidateCospendCaches(allAffectedUsers, id);
|
||||||
|
|
||||||
return json({ success: true, payment: updatedPayment });
|
return json({ success: true, payment: updatedPayment });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.status) throw e;
|
if (e.status) throw e;
|
||||||
@@ -101,10 +125,10 @@ export const DELETE: RequestHandler = async ({ params, locals }) => {
|
|||||||
const { id } = params;
|
const { id } = params;
|
||||||
|
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payment = await Payment.findById(id);
|
const payment = await Payment.findById(id);
|
||||||
|
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
throw error(404, 'Payment not found');
|
throw error(404, 'Payment not found');
|
||||||
}
|
}
|
||||||
@@ -113,9 +137,16 @@ export const DELETE: RequestHandler = async ({ params, locals }) => {
|
|||||||
throw error(403, 'Not authorized to delete this payment');
|
throw error(403, 'Not authorized to delete this payment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get splits to invalidate caches for affected users
|
||||||
|
const splits = await PaymentSplit.find({ paymentId: id }).lean();
|
||||||
|
const affectedUsernames = splits.map(split => split.username);
|
||||||
|
|
||||||
await PaymentSplit.deleteMany({ paymentId: id });
|
await PaymentSplit.deleteMany({ paymentId: id });
|
||||||
await Payment.findByIdAndDelete(id);
|
await Payment.findByIdAndDelete(id);
|
||||||
|
|
||||||
|
// Invalidate caches for all affected users
|
||||||
|
await invalidateCospendCaches(affectedUsernames, id);
|
||||||
|
|
||||||
return json({ success: true });
|
return json({ success: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.status) throw e;
|
if (e.status) throw e;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { PaymentSplit } from '../../../../../models/PaymentSplit';
|
|||||||
import { dbConnect } from '../../../../../utils/db';
|
import { dbConnect } from '../../../../../utils/db';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import { calculateNextExecutionDate } from '../../../../../lib/utils/recurring';
|
import { calculateNextExecutionDate } from '../../../../../lib/utils/recurring';
|
||||||
|
import { invalidateCospendCaches } from '$lib/server/cache';
|
||||||
|
|
||||||
// This endpoint is designed to be called by a cron job or external scheduler
|
// This endpoint is designed to be called by a cron job or external scheduler
|
||||||
// It processes all recurring payments that are due for execution
|
// It processes all recurring payments that are due for execution
|
||||||
@@ -65,6 +66,10 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
|
|
||||||
await Promise.all(splitPromises);
|
await Promise.all(splitPromises);
|
||||||
|
|
||||||
|
// Invalidate caches for all affected users
|
||||||
|
const affectedUsernames = recurringPayment.splits.map((split) => split.username);
|
||||||
|
await invalidateCospendCaches(affectedUsernames, payment._id.toString());
|
||||||
|
|
||||||
// Calculate next execution date
|
// Calculate next execution date
|
||||||
const nextExecutionDate = calculateNextExecutionDate(recurringPayment, now);
|
const nextExecutionDate = calculateNextExecutionDate(recurringPayment, now);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { dbConnect } from '../../../../../utils/db';
|
|||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import { calculateNextExecutionDate } from '../../../../../lib/utils/recurring';
|
import { calculateNextExecutionDate } from '../../../../../lib/utils/recurring';
|
||||||
import { convertToCHF } from '../../../../../lib/utils/currency';
|
import { convertToCHF } from '../../../../../lib/utils/currency';
|
||||||
|
import { invalidateCospendCaches } from '$lib/server/cache';
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ locals }) => {
|
export const POST: RequestHandler = async ({ locals }) => {
|
||||||
const auth = await locals.auth();
|
const auth = await locals.auth();
|
||||||
@@ -98,6 +99,10 @@ export const POST: RequestHandler = async ({ locals }) => {
|
|||||||
|
|
||||||
await Promise.all(splitPromises);
|
await Promise.all(splitPromises);
|
||||||
|
|
||||||
|
// Invalidate caches for all affected users
|
||||||
|
const affectedUsernames = recurringPayment.splits.map((split) => split.username);
|
||||||
|
await invalidateCospendCaches(affectedUsernames, payment._id.toString());
|
||||||
|
|
||||||
// Calculate next execution date
|
// Calculate next execution date
|
||||||
const nextExecutionDate = calculateNextExecutionDate(recurringPayment, now);
|
const nextExecutionDate = calculateNextExecutionDate(recurringPayment, now);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user