refactor(db): migrate staff and app actions from Prisma facade to Drizzle

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-31 21:35:05 +02:00
1 parent 9854719cfd
commit ed9c23c702
85 files changed
+2612 -1832

No files matched your search

+52 -30
View File
@@ -1,11 +1,12 @@
"use server";
import { and, eq, sql } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { db, WebsiteShopVouchers, WebsiteUsedShopVouchers } from "@/lib/db";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import { rcon } from "@/lib/services/rcon";
import { sendCurrency } from "@/lib/services/send-currency";
import { currencyDb, sendCurrency } from "@/lib/services/send-currency";
/** Feedback returned to the <RedeemForm/> client component via useActionState. */
export type RedeemState = { ok: boolean; message: string } | null;
@@ -20,7 +21,7 @@ export type RedeemState = { ok: boolean; message: string } | null;
* - on success the reward `amount` is granted, the used-row is inserted,
* use_count is incremented, and the voucher is expired once max_uses is hit.
*
* The reward is delivered through sendCurrency({ rcon, db: prisma }); the
* The reward is delivered through sendCurrency({ rcon, db: currencyDb }); the
* voucher schema carries a single `amount`, granted as the website credits
* wallet currency.
*/
@@ -65,16 +66,18 @@ export async function redeem(
expiresAt: Date | null;
} | null;
try {
voucher = await prisma.websiteShopVouchers.findUnique({
where: { code },
select: {
id: true,
amount: true,
maxUses: true,
useCount: true,
expiresAt: true,
},
});
const [row] = await db
.select({
id: WebsiteShopVouchers.id,
amount: WebsiteShopVouchers.amount,
maxUses: WebsiteShopVouchers.maxUses,
useCount: WebsiteShopVouchers.useCount,
expiresAt: WebsiteShopVouchers.expiresAt,
})
.from(WebsiteShopVouchers)
.where(eq(WebsiteShopVouchers.code, code))
.limit(1);
voucher = row ?? null;
} catch {
return {
ok: false,
@@ -95,10 +98,16 @@ export async function redeem(
// One redemption per user.
try {
const already = await prisma.websiteUsedShopVouchers.findFirst({
where: { userId, voucherId: voucher.id },
select: { id: true },
});
const [already] = await db
.select({ id: WebsiteUsedShopVouchers.id })
.from(WebsiteUsedShopVouchers)
.where(
and(
eq(WebsiteUsedShopVouchers.userId, userId),
eq(WebsiteUsedShopVouchers.voucherId, voucher.id),
),
)
.limit(1);
if (already) {
return { ok: false, message: "You can only use each shop voucher once." };
}
@@ -111,8 +120,9 @@ export async function redeem(
// Record the redemption first so a successful grant can never be double-claimed.
try {
await prisma.websiteUsedShopVouchers.create({
data: { userId, voucherId: voucher.id },
await db.insert(WebsiteUsedShopVouchers).values({
userId,
voucherId: voucher.id,
});
} catch {
// Most likely a race (another tab redeemed it) — treat as already used.
@@ -121,7 +131,12 @@ export async function redeem(
// Grant the reward. The voucher carries a single amount, delivered as credits.
try {
await sendCurrency({ rcon, db: prisma }, userId, "credits", voucher.amount);
await sendCurrency(
{ rcon, db: currencyDb },
userId,
"credits",
voucher.amount,
);
} catch {
// sendCurrency already falls back to a direct DB write; if it still throws,
// the used-row stands and the balance simply wasn't credited — surface that.
@@ -134,16 +149,23 @@ export async function redeem(
// Bump use_count and expire the voucher once the cap is reached.
try {
const updated = await prisma.websiteShopVouchers.update({
where: { id: voucher.id },
data: { useCount: { increment: 1 } },
select: { maxUses: true, useCount: true },
});
if (updated.maxUses && updated.useCount >= updated.maxUses) {
await prisma.websiteShopVouchers.update({
where: { id: voucher.id },
data: { expiresAt: new Date() },
});
await db
.update(WebsiteShopVouchers)
.set({ useCount: sql`${WebsiteShopVouchers.useCount} + 1` })
.where(eq(WebsiteShopVouchers.id, voucher.id));
const [updated] = await db
.select({
maxUses: WebsiteShopVouchers.maxUses,
useCount: WebsiteShopVouchers.useCount,
})
.from(WebsiteShopVouchers)
.where(eq(WebsiteShopVouchers.id, voucher.id))
.limit(1);
if (updated?.maxUses && updated.useCount >= updated.maxUses) {
await db
.update(WebsiteShopVouchers)
.set({ expiresAt: new Date() })
.where(eq(WebsiteShopVouchers.id, voucher.id));
}
} catch {
// Reward already delivered; the counter bump is best-effort.