JUNOHUB-test/src/app/api/supplier/invitations/accept/route.ts
2026-04-11 21:13:20 +05:00

153 lines
4.9 KiB
TypeScript

import { NextResponse } from "next/server"
import { auth } from "@/auth"
import { prisma } from "@/lib/prisma"
import { notifyInvitationAccepted } from "@/lib/notifications"
import { supplierInvitationAcceptSchema, validationErrorResponse } from "@/lib/api-schemas"
/**
* POST /api/supplier/invitations/accept
* Accept an invitation using token
*/
export async function POST(req: Request) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== "SUPPLIER") {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 })
}
const raw = await req.json().catch(() => ({}))
const parsed = supplierInvitationAcceptSchema.safeParse(raw)
if (!parsed.success) {
return validationErrorResponse(parsed.error)
}
const { token } = parsed.data
// Get supplier profile
const supplierProfile = await prisma.supplierProfile.findUnique({
where: { userId: session.user.id }
})
if (!supplierProfile) {
return NextResponse.json({ message: "Supplier profile not found" }, { status: 404 })
}
// Find invitation by token
// TypeScript may not recognize invitation model due to cache, but it exists at runtime
const invitation = await prisma.invitation.findUnique({
where: { token: token.toUpperCase() },
include: {
store: {
select: {
id: true,
businessName: true,
userId: true
}
}
}
})
if (!invitation) {
return NextResponse.json(
{ message: "Invalid invitation token" },
{ status: 404 }
)
}
// Security checks
if (invitation.used) {
return NextResponse.json(
{ message: "This invitation has already been used" },
{ status: 400 }
)
}
if (new Date() > invitation.expiresAt) {
return NextResponse.json(
{ message: "This invitation has expired" },
{ status: 400 }
)
}
if (invitation.supplierId !== supplierProfile.id) {
return NextResponse.json(
{ message: "This invitation is not for your supplier account" },
{ status: 403 }
)
}
// Check if connection already exists
const existingConnection = await prisma.connection.findUnique({
where: {
storeId_supplierId: {
storeId: invitation.storeId,
supplierId: supplierProfile.id
}
}
})
if (existingConnection) {
// Mark invitation as used even if connection exists
// TypeScript may not recognize invitation model due to cache, but it exists at runtime
await prisma.invitation.update({
where: { id: invitation.id },
data: { used: true, usedAt: new Date() }
})
return NextResponse.json(
{ message: `Connection already exists with status: ${existingConnection.status}` },
{ status: 400 }
)
}
// Create connection
const connection = await prisma.connection.create({
data: {
storeId: invitation.storeId,
supplierId: supplierProfile.id,
status: "CONNECTED" // Auto-connect when supplier accepts via token
}
})
// Mark invitation as used
// TypeScript may not recognize invitation model due to cache, but it exists at runtime
await (prisma as any).invitation.update({
where: { id: invitation.id },
data: {
used: true,
usedAt: new Date()
}
})
// Create notification for vendor
try {
await notifyInvitationAccepted(
invitation.store.userId,
supplierProfile.companyName,
invitation.store.businessName,
connection.id
)
} catch (notifError) {
console.error("Error creating notification:", notifError)
// Don't fail the request if notification fails
}
return NextResponse.json({
message: "Invitation accepted successfully. You are now connected!",
connection: {
id: connection.id,
status: connection.status,
vendor: {
businessName: invitation.store.businessName
}
}
})
} catch (error) {
console.error("Error accepting invitation:", error)
return NextResponse.json(
{ message: "Failed to accept invitation" },
{ status: 500 }
)
}
}