120 lines
3.8 KiB
JavaScript
120 lines
3.8 KiB
JavaScript
import jwt from 'jsonwebtoken'
|
|
import PreRegistration from '../../models/preRegistration.js'
|
|
import Member from '../../models/member.js'
|
|
import { connectDB } from '../../utils/mongoose.js'
|
|
import { setAuthCookie } from '../../utils/auth.js'
|
|
import { assignMemberNumber } from '../../utils/memberNumber.js'
|
|
import { createHelcimCustomer } from '../../utils/helcim.js'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await validateBody(event, inviteAcceptSchema)
|
|
const config = useRuntimeConfig(event)
|
|
await connectDB()
|
|
|
|
// Re-verify the token is still valid (not expired)
|
|
let decoded
|
|
try {
|
|
decoded = jwt.verify(body.token, config.jwtSecret)
|
|
} catch {
|
|
throw createError({ statusCode: 401, statusMessage: 'Invalid or expired invitation link' })
|
|
}
|
|
|
|
if (decoded.type !== 'prereg-invite' || decoded.preRegistrationId !== body.preRegistrationId) {
|
|
throw createError({ statusCode: 401, statusMessage: 'Invalid or expired invitation link' })
|
|
}
|
|
|
|
const preReg = await PreRegistration.findById(body.preRegistrationId)
|
|
if (!preReg) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Pre-registration not found' })
|
|
}
|
|
|
|
if (preReg.status === 'accepted') {
|
|
throw createError({ statusCode: 400, statusMessage: 'This invitation has already been accepted' })
|
|
}
|
|
|
|
// Check no existing member with this email
|
|
const existingMember = await Member.findOne({ email: preReg.email })
|
|
if (existingMember) {
|
|
throw createError({ statusCode: 409, statusMessage: 'A member with this email already exists' })
|
|
}
|
|
|
|
// For paid invites, create the Helcim customer up front so we can store the ID
|
|
// on the Member at creation time. Done before Member.create so a Helcim failure
|
|
// doesn't leave us with an orphan Member doc.
|
|
let helcimCustomer = null
|
|
if (body.contributionAmount > 0) {
|
|
helcimCustomer = await createHelcimCustomer({
|
|
customerType: 'PERSON',
|
|
contactName: body.name,
|
|
email: preReg.email,
|
|
})
|
|
}
|
|
|
|
// Create the member
|
|
const member = await Member.create({
|
|
email: preReg.email,
|
|
name: body.name,
|
|
pronouns: body.pronouns || undefined,
|
|
location: body.location || undefined,
|
|
circle: body.circle,
|
|
contributionAmount: body.contributionAmount,
|
|
bio: body.motivation || undefined,
|
|
status: body.contributionAmount === 0 ? 'active' : 'pending_payment',
|
|
helcimCustomerId: helcimCustomer?.id,
|
|
helcimCustomerCode: helcimCustomer?.customerCode,
|
|
agreement: { acceptedAt: new Date() },
|
|
})
|
|
|
|
await assignMemberNumber(member._id)
|
|
|
|
// Update pre-registration
|
|
await PreRegistration.findByIdAndUpdate(preReg._id, {
|
|
$set: {
|
|
status: 'accepted',
|
|
acceptedAt: new Date(),
|
|
memberId: member._id,
|
|
}
|
|
})
|
|
|
|
logActivity(member._id, 'member_joined', {
|
|
source: 'pre-registration',
|
|
preRegistrationId: preReg._id,
|
|
})
|
|
|
|
// Issue session cookie so the member is authenticated for any follow-up calls
|
|
// (Helcim initialize-payment for paid flow, dashboard fetches for free flow).
|
|
setAuthCookie(event, member)
|
|
|
|
// For free tier, redirect to welcome
|
|
if (body.contributionAmount === 0) {
|
|
return {
|
|
success: true,
|
|
requiresPayment: false,
|
|
redirectUrl: '/member/dashboard',
|
|
member: {
|
|
id: member._id,
|
|
email: member.email,
|
|
name: member.name,
|
|
circle: member.circle,
|
|
contributionAmount: member.contributionAmount,
|
|
status: member.status,
|
|
}
|
|
}
|
|
}
|
|
|
|
// For paid tiers, return member + Helcim customer so frontend can proceed to payment
|
|
return {
|
|
success: true,
|
|
requiresPayment: true,
|
|
customerId: helcimCustomer.id,
|
|
customerCode: helcimCustomer.customerCode,
|
|
member: {
|
|
id: member._id,
|
|
email: member.email,
|
|
name: member.name,
|
|
circle: member.circle,
|
|
contributionAmount: member.contributionAmount,
|
|
status: member.status,
|
|
}
|
|
}
|
|
})
|