feat : limit each session to 1 hours when checkout
This commit is contained in:
@@ -4,6 +4,7 @@ import { createHash, randomBytes } from "node:crypto";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { getDb } from "@/db";
|
||||
import { commissionCheckouts, commissionTickets } from "@/db/schema";
|
||||
import { checkoutExpired, checkoutExpiresAt } from "@/lib/commission/checkout-expiration";
|
||||
import { getRedisClient, redisEventChannel } from "@/lib/redis/client";
|
||||
import { HttpError } from "@/lib/security/http";
|
||||
|
||||
@@ -15,19 +16,22 @@ const errorKey = (digest: string) => `${prefix()}:error:${digest}`;
|
||||
const digestToken = (token: string) => createHash("sha256").update(token).digest("hex");
|
||||
|
||||
export async function createMobileSlipLink(checkoutId: string, userId: string) {
|
||||
const [checkout] = await getDb().select({ id: commissionCheckouts.id }).from(commissionCheckouts)
|
||||
const [checkout] = await getDb().select({ id: commissionCheckouts.id, createdAt: commissionCheckouts.createdAt }).from(commissionCheckouts)
|
||||
.where(and(eq(commissionCheckouts.id, checkoutId), eq(commissionCheckouts.userId, userId))).limit(1);
|
||||
if (!checkout) throw new HttpError(404, "checkout-not-found");
|
||||
const [ticket] = await getDb().select({ id: commissionTickets.id }).from(commissionTickets)
|
||||
.where(eq(commissionTickets.checkoutId, checkoutId)).limit(1);
|
||||
if (ticket) throw new HttpError(409, "checkout-already-paid");
|
||||
if (checkoutExpired(checkout.createdAt)) throw new HttpError(410, "checkout-expired");
|
||||
|
||||
const token = randomBytes(32).toString("base64url");
|
||||
const digest = digestToken(token);
|
||||
const redis = await getRedisClient();
|
||||
await redis.set(tokenKey(digest), JSON.stringify({ checkoutId, userId }), "EX", TTL_SECONDS);
|
||||
await redis.set(activeKey(checkoutId), digest, "EX", TTL_SECONDS);
|
||||
return { token, digest, expiresAt: Date.now() + TTL_SECONDS * 1000 };
|
||||
const expiresAt = Math.min(Date.now() + TTL_SECONDS * 1000, checkoutExpiresAt(checkout.createdAt));
|
||||
const ttl = Math.max(1, Math.ceil((expiresAt - Date.now()) / 1000));
|
||||
await redis.set(tokenKey(digest), JSON.stringify({ checkoutId, userId }), "EX", ttl);
|
||||
await redis.set(activeKey(checkoutId), digest, "EX", ttl);
|
||||
return { token, digest, expiresAt };
|
||||
}
|
||||
|
||||
export async function authorizeMobileSlip(token: string) {
|
||||
@@ -44,16 +48,18 @@ export async function authorizeMobileSlip(token: string) {
|
||||
if (!checkout) throw new HttpError(404, "checkout-not-found");
|
||||
const [ticket] = await getDb().select({ id: commissionTickets.id }).from(commissionTickets)
|
||||
.where(eq(commissionTickets.checkoutId, checkoutId)).limit(1);
|
||||
if (!ticket && checkoutExpired(checkout.createdAt)) throw new HttpError(410, "checkout-expired");
|
||||
return { checkout, ticketId: ticket?.id ?? null, digest };
|
||||
}
|
||||
|
||||
export async function mobileSlipStatus(checkoutId: string, userId: string, digest: string) {
|
||||
const [checkout] = await getDb().select({ id: commissionCheckouts.id }).from(commissionCheckouts)
|
||||
const [checkout] = await getDb().select({ id: commissionCheckouts.id, createdAt: commissionCheckouts.createdAt }).from(commissionCheckouts)
|
||||
.where(and(eq(commissionCheckouts.id, checkoutId), eq(commissionCheckouts.userId, userId))).limit(1);
|
||||
if (!checkout) throw new HttpError(404, "checkout-not-found");
|
||||
const [ticket] = await getDb().select({ id: commissionTickets.id }).from(commissionTickets)
|
||||
.where(eq(commissionTickets.checkoutId, checkoutId)).limit(1);
|
||||
if (ticket) return { state: "complete", ticketId: ticket.id };
|
||||
if (checkoutExpired(checkout.createdAt)) return { state: "expired" };
|
||||
if (!/^[a-f0-9]{64}$/.test(digest)) throw new HttpError(400, "invalid-upload-link");
|
||||
const redis = await getRedisClient();
|
||||
if (await redis.get(activeKey(checkoutId)) !== digest) return { state: "expired" };
|
||||
|
||||
Reference in New Issue
Block a user