🐛 add env to useDb and apply prettier
tinyedge/deploy Deployed by TinyEdge

This commit is contained in:
2026-08-07 13:28:26 +02:00
parent 67727f96ab
commit cbe1b42f6e
54 changed files with 5492 additions and 2406 deletions
+11 -8
View File
@@ -1,12 +1,15 @@
import { calculateBalances } from '~~/shared/utils/balances'
import { calculateBalances } from '~~/shared/utils/balances';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
const slug = getRouterParam(event, 'slug')!;
const pot = await getPotOrThrow(slug)
const participants = await getParticipants(slug)
const expenses = await getExpensesWithParticipants(slug)
const { netBalances, settlements } = calculateBalances(participants, expenses)
const pot = await getPotOrThrow(slug);
const participants = await getParticipants(slug);
const expenses = await getExpensesWithParticipants(slug);
const { netBalances, settlements } = calculateBalances(
participants,
expenses,
);
return { pot, participants, expenses, netBalances, settlements }
})
return { pot, participants, expenses, netBalances, settlements };
});
+48 -21
View File
@@ -1,35 +1,62 @@
import { useDb } from '~~/server/database/client'
import { expenseParticipants, expenses } from '~~/server/database/schema'
import { useDb } from '~~/server/database/client';
import { expenseParticipants, expenses } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
await getPotOrThrow(slug);
const body = await readBody(event)
const description = typeof body?.description === 'string' ? body.description.trim() : ''
const amountCents = Number(body?.amountCents)
const payerId = Number(body?.payerId)
const participantIds: number[] = Array.isArray(body?.participantIds) ? body.participantIds.map(Number) : []
const body = await readBody(event);
const description =
typeof body?.description === 'string' ? body.description.trim() : '';
const amountCents = Number(body?.amountCents);
const payerId = Number(body?.payerId);
const participantIds: number[] = Array.isArray(body?.participantIds)
? body.participantIds.map(Number)
: [];
if (!description) {
throw createError({ statusCode: 400, statusMessage: 'description is required' })
throw createError({
statusCode: 400,
statusMessage: 'description is required',
});
}
if (!Number.isInteger(amountCents) || amountCents <= 0) {
throw createError({ statusCode: 400, statusMessage: 'amountCents must be a positive integer' })
throw createError({
statusCode: 400,
statusMessage: 'amountCents must be a positive integer',
});
}
if (participantIds.length === 0) {
throw createError({ statusCode: 400, statusMessage: 'at least one participant must be selected' })
throw createError({
statusCode: 400,
statusMessage: 'at least one participant must be selected',
});
}
const potParticipants = await getParticipants(slug)
const validIds = new Set(potParticipants.map(p => p.id))
if (!validIds.has(payerId) || participantIds.some(id => !validIds.has(id))) {
throw createError({ statusCode: 400, statusMessage: 'payerId and participantIds must reference participants of this Pot' })
const potParticipants = await getParticipants(slug);
const validIds = new Set(potParticipants.map((p) => p.id));
if (
!validIds.has(payerId) ||
participantIds.some((id) => !validIds.has(id))
) {
throw createError({
statusCode: 400,
statusMessage:
'payerId and participantIds must reference participants of this Pot',
});
}
const db = useDb()
const [expense] = await db.insert(expenses).values({ potSlug: slug, description, amountCents, payerId }).returning()
await db.insert(expenseParticipants).values(participantIds.map(participantId => ({ expenseId: expense!.id, participantId })))
const db = useDb(event);
const [expense] = await db
.insert(expenses)
.values({ potSlug: slug, description, amountCents, payerId })
.returning();
await db.insert(expenseParticipants).values(
participantIds.map((participantId) => ({
expenseId: expense!.id,
participantId,
})),
);
return { ...expense, participantIds }
})
return { ...expense, participantIds };
});
+18 -13
View File
@@ -1,20 +1,25 @@
import { and, eq } from 'drizzle-orm'
import { useDb } from '~~/server/database/client'
import { expenseParticipants, expenses } from '~~/server/database/schema'
import { and, eq } from 'drizzle-orm';
import { useDb } from '~~/server/database/client';
import { expenseParticipants, expenses } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
const id = Number(getRouterParam(event, 'id'))
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
const id = Number(getRouterParam(event, 'id'));
await getPotOrThrow(slug);
const db = useDb()
const [existing] = await db.select().from(expenses).where(and(eq(expenses.id, id), eq(expenses.potSlug, slug)))
const db = useDb(event);
const [existing] = await db
.select()
.from(expenses)
.where(and(eq(expenses.id, id), eq(expenses.potSlug, slug)));
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Expense not found' })
throw createError({ statusCode: 404, statusMessage: 'Expense not found' });
}
await db.delete(expenseParticipants).where(eq(expenseParticipants.expenseId, id))
await db.delete(expenses).where(eq(expenses.id, id))
await db
.delete(expenseParticipants)
.where(eq(expenseParticipants.expenseId, id));
await db.delete(expenses).where(eq(expenses.id, id));
return { success: true }
})
return { success: true };
});
+57 -26
View File
@@ -1,43 +1,74 @@
import { and, eq } from 'drizzle-orm'
import { useDb } from '~~/server/database/client'
import { expenseParticipants, expenses } from '~~/server/database/schema'
import { and, eq } from 'drizzle-orm';
import { useDb } from '~~/server/database/client';
import { expenseParticipants, expenses } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
const id = Number(getRouterParam(event, 'id'))
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
const id = Number(getRouterParam(event, 'id'));
await getPotOrThrow(slug);
const body = await readBody(event)
const description = typeof body?.description === 'string' ? body.description.trim() : ''
const amountCents = Number(body?.amountCents)
const payerId = Number(body?.payerId)
const participantIds: number[] = Array.isArray(body?.participantIds) ? body.participantIds.map(Number) : []
const body = await readBody(event);
const description =
typeof body?.description === 'string' ? body.description.trim() : '';
const amountCents = Number(body?.amountCents);
const payerId = Number(body?.payerId);
const participantIds: number[] = Array.isArray(body?.participantIds)
? body.participantIds.map(Number)
: [];
if (!description) {
throw createError({ statusCode: 400, statusMessage: 'description is required' })
throw createError({
statusCode: 400,
statusMessage: 'description is required',
});
}
if (!Number.isInteger(amountCents) || amountCents <= 0) {
throw createError({ statusCode: 400, statusMessage: 'amountCents must be a positive integer' })
throw createError({
statusCode: 400,
statusMessage: 'amountCents must be a positive integer',
});
}
if (participantIds.length === 0) {
throw createError({ statusCode: 400, statusMessage: 'at least one participant must be selected' })
throw createError({
statusCode: 400,
statusMessage: 'at least one participant must be selected',
});
}
const potParticipants = await getParticipants(slug)
const validIds = new Set(potParticipants.map(p => p.id))
if (!validIds.has(payerId) || participantIds.some(pid => !validIds.has(pid))) {
throw createError({ statusCode: 400, statusMessage: 'payerId and participantIds must reference participants of this Pot' })
const potParticipants = await getParticipants(slug);
const validIds = new Set(potParticipants.map((p) => p.id));
if (
!validIds.has(payerId) ||
participantIds.some((pid) => !validIds.has(pid))
) {
throw createError({
statusCode: 400,
statusMessage:
'payerId and participantIds must reference participants of this Pot',
});
}
const db = useDb()
const [existing] = await db.select().from(expenses).where(and(eq(expenses.id, id), eq(expenses.potSlug, slug)))
const db = useDb(event);
const [existing] = await db
.select()
.from(expenses)
.where(and(eq(expenses.id, id), eq(expenses.potSlug, slug)));
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Expense not found' })
throw createError({ statusCode: 404, statusMessage: 'Expense not found' });
}
await db.update(expenses).set({ description, amountCents, payerId }).where(eq(expenses.id, id))
await db.delete(expenseParticipants).where(eq(expenseParticipants.expenseId, id))
await db.insert(expenseParticipants).values(participantIds.map(participantId => ({ expenseId: id, participantId })))
await db
.update(expenses)
.set({ description, amountCents, payerId })
.where(eq(expenses.id, id));
await db
.delete(expenseParticipants)
.where(eq(expenseParticipants.expenseId, id));
await db
.insert(expenseParticipants)
.values(
participantIds.map((participantId) => ({ expenseId: id, participantId })),
);
return { id, description, amountCents, payerId, participantIds }
})
return { id, description, amountCents, payerId, participantIds };
});
+14 -11
View File
@@ -1,17 +1,20 @@
import { useDb } from '~~/server/database/client'
import { participants } from '~~/server/database/schema'
import { useDb } from '~~/server/database/client';
import { participants } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
await getPotOrThrow(slug);
const body = await readBody(event)
const name = typeof body?.name === 'string' ? body.name.trim() : ''
const body = await readBody(event);
const name = typeof body?.name === 'string' ? body.name.trim() : '';
if (!name) {
throw createError({ statusCode: 400, statusMessage: 'name is required' })
throw createError({ statusCode: 400, statusMessage: 'name is required' });
}
const db = useDb()
const [participant] = await db.insert(participants).values({ potSlug: slug, name }).returning()
return participant
})
const db = useDb(event);
const [participant] = await db
.insert(participants)
.values({ potSlug: slug, name })
.returning();
return participant;
});
@@ -1,25 +1,46 @@
import { and, eq } from 'drizzle-orm'
import { useDb } from '~~/server/database/client'
import { expenseParticipants, expenses, participants } from '~~/server/database/schema'
import { and, eq } from 'drizzle-orm';
import { useDb } from '~~/server/database/client';
import {
expenseParticipants,
expenses,
participants,
} from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
const id = Number(getRouterParam(event, 'id'))
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
const id = Number(getRouterParam(event, 'id'));
await getPotOrThrow(slug);
const db = useDb()
const [existing] = await db.select().from(participants).where(and(eq(participants.id, id), eq(participants.potSlug, slug)))
const db = useDb(event);
const [existing] = await db
.select()
.from(participants)
.where(and(eq(participants.id, id), eq(participants.potSlug, slug)));
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Participant not found' })
throw createError({
statusCode: 404,
statusMessage: 'Participant not found',
});
}
const [asPayer] = await db.select().from(expenses).where(eq(expenses.payerId, id)).limit(1)
const [asSplit] = await db.select().from(expenseParticipants).where(eq(expenseParticipants.participantId, id)).limit(1)
const [asPayer] = await db
.select()
.from(expenses)
.where(eq(expenses.payerId, id))
.limit(1);
const [asSplit] = await db
.select()
.from(expenseParticipants)
.where(eq(expenseParticipants.participantId, id))
.limit(1);
if (asPayer || asSplit) {
throw createError({ statusCode: 409, statusMessage: 'Cannot remove a participant with expenses or payments' })
throw createError({
statusCode: 409,
statusMessage: 'Cannot remove a participant with expenses or payments',
});
}
await db.delete(participants).where(eq(participants.id, id))
await db.delete(participants).where(eq(participants.id, id));
return { success: true }
})
return { success: true };
});
@@ -1,29 +1,40 @@
import { and, eq } from 'drizzle-orm'
import { isValidPaymentLink } from '~~/shared/utils/paymentLink'
import { useDb } from '~~/server/database/client'
import { participants } from '~~/server/database/schema'
import { and, eq } from 'drizzle-orm';
import { isValidPaymentLink } from '~~/shared/utils/paymentLink';
import { useDb } from '~~/server/database/client';
import { participants } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
const id = Number(getRouterParam(event, 'id'))
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
const id = Number(getRouterParam(event, 'id'));
await getPotOrThrow(slug);
const body = await readBody(event)
const paymentLink = typeof body?.paymentLink === 'string' ? body.paymentLink.trim() : ''
const body = await readBody(event);
const paymentLink =
typeof body?.paymentLink === 'string' ? body.paymentLink.trim() : '';
if (paymentLink && !isValidPaymentLink(paymentLink)) {
throw createError({ statusCode: 400, statusMessage: 'paymentLink must be a valid http(s) URL' })
throw createError({
statusCode: 400,
statusMessage: 'paymentLink must be a valid http(s) URL',
});
}
const db = useDb()
const [existing] = await db.select().from(participants).where(and(eq(participants.id, id), eq(participants.potSlug, slug)))
const db = useDb(event);
const [existing] = await db
.select()
.from(participants)
.where(and(eq(participants.id, id), eq(participants.potSlug, slug)));
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Participant not found' })
throw createError({
statusCode: 404,
statusMessage: 'Participant not found',
});
}
const [updated] = await db.update(participants)
const [updated] = await db
.update(participants)
.set({ paymentLink: paymentLink || null })
.where(eq(participants.id, id))
.returning()
.returning();
return updated
})
return updated;
});
+38 -20
View File
@@ -1,33 +1,51 @@
import { useDb } from '~~/server/database/client'
import { expenseParticipants, expenses } from '~~/server/database/schema'
import { useDb } from '~~/server/database/client';
import { expenseParticipants, expenses } from '~~/server/database/schema';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')!
await getPotOrThrow(slug)
const slug = getRouterParam(event, 'slug')!;
await getPotOrThrow(slug);
const body = await readBody(event)
const fromId = Number(body?.fromId)
const toId = Number(body?.toId)
const amountCents = Number(body?.amountCents)
const body = await readBody(event);
const fromId = Number(body?.fromId);
const toId = Number(body?.toId);
const amountCents = Number(body?.amountCents);
if (!Number.isInteger(amountCents) || amountCents <= 0) {
throw createError({ statusCode: 400, statusMessage: 'amountCents must be a positive integer' })
throw createError({
statusCode: 400,
statusMessage: 'amountCents must be a positive integer',
});
}
if (fromId === toId) {
throw createError({ statusCode: 400, statusMessage: 'fromId and toId must differ' })
throw createError({
statusCode: 400,
statusMessage: 'fromId and toId must differ',
});
}
const potParticipants = await getParticipants(slug)
const validIds = new Set(potParticipants.map(p => p.id))
const potParticipants = await getParticipants(slug);
const validIds = new Set(potParticipants.map((p) => p.id));
if (!validIds.has(fromId) || !validIds.has(toId)) {
throw createError({ statusCode: 400, statusMessage: 'fromId and toId must reference participants of this Pot' })
throw createError({
statusCode: 400,
statusMessage: 'fromId and toId must reference participants of this Pot',
});
}
const db = useDb()
const [payment] = await db.insert(expenses)
.values({ potSlug: slug, type: 'payment', description: 'Settlement', amountCents, payerId: fromId })
.returning()
await db.insert(expenseParticipants).values({ expenseId: payment!.id, participantId: toId })
const db = useDb(event);
const [payment] = await db
.insert(expenses)
.values({
potSlug: slug,
type: 'payment',
description: 'Settlement',
amountCents,
payerId: fromId,
})
.returning();
await db
.insert(expenseParticipants)
.values({ expenseId: payment!.id, participantId: toId });
return { ...payment, participantIds: [toId] }
})
return { ...payment, participantIds: [toId] };
});