153 lines
4.9 KiB
TypeScript
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 }
|
|
)
|
|
}
|
|
}
|