refactor(db): migrate app pages and APIs from Prisma facade to Drizzle (4)
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
580972c0a0
commit
9aa4f331bf
40 files changed
+1259
-927
No files matched your search
@@ -1,9 +1,11 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import type { ResultSetHeader } from "mysql2";
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { sessionUserId } from "@/lib/auth/session-user";
|
||||
import { db, WebsitePaypalTransactions } from "@/lib/db";
|
||||
import { resolveHotelName } from "@/lib/hotel-name";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { logServerError } from "@/lib/server-log";
|
||||
import {
|
||||
type CaptureResult,
|
||||
@@ -18,7 +20,7 @@ import {
|
||||
TopupCaptureError,
|
||||
} from "@/lib/services/paypal-topup";
|
||||
import { rcon } from "@/lib/services/rcon";
|
||||
import { sendCurrency } from "@/lib/services/send-currency";
|
||||
import { currencyDb, sendCurrency } from "@/lib/services/send-currency";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -79,11 +81,18 @@ export async function POST(req: Request): Promise<Response> {
|
||||
authorized = await authorizeTopupCapture(
|
||||
userId,
|
||||
orderId,
|
||||
async (transactionId) =>
|
||||
prisma.websitePaypalTransactions.findFirst({
|
||||
where: { transactionId },
|
||||
select: { userId: true, status: true, amount: true },
|
||||
}),
|
||||
async (transactionId) => {
|
||||
const [row] = await db
|
||||
.select({
|
||||
userId: WebsitePaypalTransactions.userId,
|
||||
status: WebsitePaypalTransactions.status,
|
||||
amount: WebsitePaypalTransactions.amount,
|
||||
})
|
||||
.from(WebsitePaypalTransactions)
|
||||
.where(eq(WebsitePaypalTransactions.transactionId, transactionId))
|
||||
.limit(1);
|
||||
return row ?? null;
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof TopupCaptureError) {
|
||||
@@ -103,21 +112,30 @@ export async function POST(req: Request): Promise<Response> {
|
||||
const amount = authorized.amount ?? 0;
|
||||
const credits = Math.floor(amount * creditsPerUnit());
|
||||
try {
|
||||
await claimTopupDelivery(() =>
|
||||
prisma.websitePaypalTransactions.updateMany({
|
||||
where: {
|
||||
userId,
|
||||
transactionId: orderId,
|
||||
status: "CAPTURED_PENDING_CREDIT",
|
||||
},
|
||||
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
|
||||
}),
|
||||
);
|
||||
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
|
||||
await prisma.websitePaypalTransactions.updateMany({
|
||||
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
|
||||
data: { status: "COMPLETED", updatedAt: new Date() },
|
||||
await claimTopupDelivery(async () => {
|
||||
const [result] = (await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({ status: "CREDIT_DELIVERING", updatedAt: new Date() })
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CAPTURED_PENDING_CREDIT"),
|
||||
),
|
||||
)) as unknown as [ResultSetHeader];
|
||||
return { count: result.affectedRows };
|
||||
});
|
||||
await sendCurrency({ rcon, db: currencyDb }, userId, "credits", credits);
|
||||
await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({ status: "COMPLETED", updatedAt: new Date() })
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CREDIT_DELIVERING"),
|
||||
),
|
||||
);
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
recovered: true,
|
||||
@@ -162,17 +180,23 @@ export async function POST(req: Request): Promise<Response> {
|
||||
if (result.status !== "COMPLETED") {
|
||||
// Record the non-completed attempt so support can trace it.
|
||||
try {
|
||||
await prisma.websitePaypalTransactions.updateMany({
|
||||
where: { userId, transactionId: orderId, status: "CREATED" },
|
||||
data: {
|
||||
await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({
|
||||
status: result.status,
|
||||
description: `${hotelName} top-up (not completed)`,
|
||||
amount: result.amount,
|
||||
currency: result.currency,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CREATED"),
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
/* best-effort logging */
|
||||
}
|
||||
@@ -187,18 +211,24 @@ export async function POST(req: Request): Promise<Response> {
|
||||
// Record the transaction BEFORE crediting so a crash mid-grant can't be
|
||||
// reprocessed into a double credit (the idempotency check above keys on this).
|
||||
try {
|
||||
const claimed = await prisma.websitePaypalTransactions.updateMany({
|
||||
where: { userId, transactionId: orderId, status: "CREATED" },
|
||||
data: {
|
||||
const [claimed] = (await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({
|
||||
status: "CAPTURED_PENDING_CREDIT",
|
||||
description: `${hotelName} top-up: ${credits} credits`,
|
||||
amount: result.amount,
|
||||
currency: result.currency,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
if (claimed.count !== 1)
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CREATED"),
|
||||
),
|
||||
)) as unknown as [ResultSetHeader];
|
||||
if (claimed.affectedRows !== 1)
|
||||
throw new Error("Top-up order was already claimed");
|
||||
} catch (e) {
|
||||
logger.error("PayPal capture record failed", {
|
||||
@@ -218,21 +248,30 @@ export async function POST(req: Request): Promise<Response> {
|
||||
// external delivery outcome is ambiguous, CREDIT_DELIVERING remains visible
|
||||
// for staff reconciliation instead of automatically risking a second grant.
|
||||
try {
|
||||
await claimTopupDelivery(() =>
|
||||
prisma.websitePaypalTransactions.updateMany({
|
||||
where: {
|
||||
userId,
|
||||
transactionId: orderId,
|
||||
status: "CAPTURED_PENDING_CREDIT",
|
||||
},
|
||||
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
|
||||
}),
|
||||
);
|
||||
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
|
||||
await prisma.websitePaypalTransactions.updateMany({
|
||||
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
|
||||
data: { status: "COMPLETED", updatedAt: new Date() },
|
||||
await claimTopupDelivery(async () => {
|
||||
const [claimResult] = (await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({ status: "CREDIT_DELIVERING", updatedAt: new Date() })
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CAPTURED_PENDING_CREDIT"),
|
||||
),
|
||||
)) as unknown as [ResultSetHeader];
|
||||
return { count: claimResult.affectedRows };
|
||||
});
|
||||
await sendCurrency({ rcon, db: currencyDb }, userId, "credits", credits);
|
||||
await db
|
||||
.update(WebsitePaypalTransactions)
|
||||
.set({ status: "COMPLETED", updatedAt: new Date() })
|
||||
.where(
|
||||
and(
|
||||
eq(WebsitePaypalTransactions.userId, userId),
|
||||
eq(WebsitePaypalTransactions.transactionId, orderId),
|
||||
eq(WebsitePaypalTransactions.status, "CREDIT_DELIVERING"),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.error("PayPal capture credit failed", {
|
||||
module: "paypal/capture",
|
||||
|
||||
@@ -2,9 +2,9 @@ import { NextResponse } from "next/server";
|
||||
import { env } from "@/env";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { sessionUserId } from "@/lib/auth/session-user";
|
||||
import { db, WebsitePaypalTransactions } from "@/lib/db";
|
||||
import { resolveHotelName } from "@/lib/hotel-name";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import {
|
||||
createOrder,
|
||||
creditsPerUnit,
|
||||
@@ -87,7 +87,7 @@ export async function POST(req: Request): Promise<Response> {
|
||||
|
||||
await recordCreatedTopup(
|
||||
{ userId, orderId: order.id, amount, currency: PAYPAL_CURRENCY, credits },
|
||||
(data) => prisma.websitePaypalTransactions.create({ data }),
|
||||
(data) => db.insert(WebsitePaypalTransactions).values(data),
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
|
||||
Reference in new issue
Block a user