import { NextResponse } from "next/server" import { randomBytes } from "crypto" import { requireOwner } from "@/lib/authz" import { prisma } from "@/lib/prisma" import { publicOrigin } from "@/lib/origin" const RESET_TTL_HOURS = 24 /** * Issues a single-use password reset link for a member. * * There is no email infrastructure, so the owner delivers this out of band, the same * way an invite is delivered. Without it a member who forgets their password is * permanently locked out and the owner has no way to help. * * Any unused reset for the same user is invalidated first, so only the newest link works. */ export async function POST( request: Request, { params }: { params: { id: string } } ) { const session = await requireOwner() if (session instanceof NextResponse) return session const user = await prisma.user.findUnique({ where: { id: params.id }, select: { id: true, email: true, password: true }, }) if (!user) return NextResponse.json({ error: "Not found" }, { status: 404 }) if (!user.password) { return NextResponse.json( { error: "That account does not use a password" }, { status: 400 } ) } const token = randomBytes(32).toString("hex") await prisma.$transaction([ prisma.passwordReset.updateMany({ where: { userId: params.id, usedAt: null }, data: { usedAt: new Date() }, }), prisma.passwordReset.create({ data: { token, userId: params.id, expiresAt: new Date(Date.now() + RESET_TTL_HOURS * 60 * 60 * 1000), }, }), ]) return NextResponse.json({ url: `${publicOrigin(request)}/reset/${token}`, expiresInHours: RESET_TTL_HOURS, }) }