41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
|
|
import type { CompetitionCategory, PrismaClient } from '@prisma/client'
|
||
|
|
import { signFinalistToken } from '@/lib/finalist-token'
|
||
|
|
|
||
|
|
type AnyPrisma = Pick<PrismaClient, 'finalistConfirmation'>
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a PENDING FinalistConfirmation row with a signed token. Caller is
|
||
|
|
* responsible for sending the notification email separately.
|
||
|
|
*/
|
||
|
|
export async function createPendingConfirmation(
|
||
|
|
prisma: AnyPrisma,
|
||
|
|
args: {
|
||
|
|
projectId: string
|
||
|
|
category: CompetitionCategory
|
||
|
|
windowHours: number
|
||
|
|
promotedFromWaitlistEntryId?: string
|
||
|
|
},
|
||
|
|
): Promise<{ id: string; token: string; deadline: Date }> {
|
||
|
|
const deadline = new Date(Date.now() + args.windowHours * 3_600_000)
|
||
|
|
// Generate the row ID up front so we can sign it into the token before
|
||
|
|
// writing the row (token is unique-indexed; embedding the ID gives the
|
||
|
|
// public verify path a stable lookup key).
|
||
|
|
const id = `cmfc_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`
|
||
|
|
const token = signFinalistToken({
|
||
|
|
confirmationId: id,
|
||
|
|
exp: Math.floor(deadline.getTime() / 1000),
|
||
|
|
})
|
||
|
|
await prisma.finalistConfirmation.create({
|
||
|
|
data: {
|
||
|
|
id,
|
||
|
|
projectId: args.projectId,
|
||
|
|
category: args.category,
|
||
|
|
status: 'PENDING',
|
||
|
|
deadline,
|
||
|
|
token,
|
||
|
|
promotedFromWaitlistEntryId: args.promotedFromWaitlistEntryId ?? null,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
return { id, token, deadline }
|
||
|
|
}
|