monacousa-portal/server/api/members/[id]/create-portal-account.post.ts

207 lines
7.8 KiB
TypeScript

export default defineEventHandler(async (event) => {
console.log('[api/members/[id]/create-portal-account.post] =========================');
console.log('[api/members/[id]/create-portal-account.post] POST /api/members/:id/create-portal-account - Create portal account for member');
try {
// Validate session and require board/admin privileges
const sessionManager = createSessionManager();
const cookieHeader = getCookie(event, 'monacousa-session') ? getHeader(event, 'cookie') : undefined;
const session = sessionManager.getSession(cookieHeader);
if (!session?.user) {
throw createError({
statusCode: 401,
statusMessage: 'Authentication required'
});
}
// Require board or admin privileges
if (session.user.tier !== 'admin' && session.user.tier !== 'board') {
throw createError({
statusCode: 403,
statusMessage: 'Board or Admin privileges required'
});
}
console.log('[api/members/[id]/create-portal-account.post] Authorized user:', session.user.email, 'tier:', session.user.tier);
// Get member ID from route parameter
const memberId = getRouterParam(event, 'id');
if (!memberId) {
throw createError({
statusCode: 400,
statusMessage: 'Member ID is required'
});
}
console.log('[api/members/[id]/create-portal-account.post] Processing member ID:', memberId);
// 1. Get member data
const { getMemberById } = await import('~/server/utils/nocodb');
const member = await getMemberById(memberId);
if (!member) {
throw createError({
statusCode: 404,
statusMessage: 'Member not found'
});
}
console.log('[api/members/[id]/create-portal-account.post] Found member:', member.email);
// 2. Check if member already has portal account
if (member.keycloak_id) {
console.log('[api/members/[id]/create-portal-account.post] Member already has portal account:', member.keycloak_id);
throw createError({
statusCode: 409,
statusMessage: 'Member already has a portal account'
});
}
// 3. Validate member data
if (!member.email || !member.first_name || !member.last_name) {
throw createError({
statusCode: 400,
statusMessage: 'Member must have email, first name, and last name to create portal account'
});
}
// 4. Check if user already exists in Keycloak (by email)
const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin');
const keycloakAdmin = createKeycloakAdminClient();
console.log('[api/members/[id]/create-portal-account.post] Checking for existing Keycloak user...');
const existingUsers = await keycloakAdmin.findUserByEmail(member.email);
if (existingUsers.length > 0) {
console.log('[api/members/[id]/create-portal-account.post] User already exists in Keycloak');
throw createError({
statusCode: 409,
statusMessage: 'A user with this email already exists in the system'
});
}
// 5. Determine membership tier based on member data
const membershipTier = determineMembershipTier(member);
console.log('[api/members/[id]/create-portal-account.post] Determined membership tier:', membershipTier);
// 6. Prepare membership data for Keycloak sync
const membershipData = {
membershipStatus: member.membership_status || 'Active',
duesStatus: member.current_year_dues_paid === 'true' ? 'paid' as const : 'unpaid' as const,
memberSince: member.member_since || new Date().getFullYear().toString(),
nationality: member.nationality || '',
phone: member.phone || '',
address: member.address || '',
registrationDate: member.registration_date || new Date().toISOString(),
paymentDueDate: member.payment_due_date || '',
membershipTier,
nocodbMemberId: memberId
};
// 7. Create Keycloak user with role-based registration
console.log('[api/members/[id]/create-portal-account.post] Creating Keycloak user with role-based system...');
const keycloakId = await keycloakAdmin.createUserWithRoleRegistration({
email: member.email,
firstName: member.first_name,
lastName: member.last_name,
membershipTier,
membershipData
});
console.log('[api/members/[id]/create-portal-account.post] Created Keycloak user with ID:', keycloakId);
// 8. Update member record with keycloak_id
console.log('[api/members/[id]/create-portal-account.post] Updating member record with keycloak_id...');
const { updateMember } = await import('~/server/utils/nocodb');
await updateMember(memberId, { keycloak_id: keycloakId });
// 9. Send welcome/verification email using our custom email system
console.log('[api/members/[id]/create-portal-account.post] Attempting to send welcome/verification email...');
let emailSent = false;
try {
const { getEmailService } = await import('~/server/utils/email');
const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens');
const emailService = await getEmailService();
const verificationToken = await generateEmailVerificationToken(keycloakId, member.email);
const config = useRuntimeConfig();
const verificationLink = `${config.public.domain}/api/auth/verify-email?token=${verificationToken}`;
await emailService.sendWelcomeEmail(member.email, {
firstName: member.first_name,
lastName: member.last_name,
verificationLink,
memberId: memberId
});
emailSent = true;
console.log('[api/members/[id]/create-portal-account.post] Welcome email sent successfully');
} catch (emailError: any) {
console.error('[api/members/[id]/create-portal-account.post] Failed to send welcome email:', emailError.message);
// Don't fail the account creation if email fails - user can resend verification email later
}
console.log('[api/members/[id]/create-portal-account.post] ✅ Portal account creation successful');
return {
success: true,
message: emailSent
? 'Portal account created successfully. The member will receive an email to verify their account and set their password.'
: 'Portal account created successfully. Email sending is not configured - the member will need to request a password reset to access their account.',
data: {
keycloak_id: keycloakId,
member_id: memberId,
email: member.email,
name: `${member.first_name} ${member.last_name}`,
email_sent: emailSent
}
};
} catch (error: any) {
console.error('[api/members/[id]/create-portal-account.post] ❌ Portal account creation failed:', error);
// If it's already an HTTP error, re-throw it
if (error.statusCode) {
throw error;
}
// Otherwise, wrap it in a generic error
throw createError({
statusCode: 500,
statusMessage: error.message || 'Failed to create portal account'
});
}
});
/**
* Determine membership tier based on member data
* This function analyzes member information to assign appropriate portal roles
*/
function determineMembershipTier(member: any): 'user' | 'board' | 'admin' {
// Check for explicit tier indicators in member data
// This could be based on membership type, special flags, or other criteria
// For now, default all members to 'user' tier
// In the future, you might want to check specific fields like:
// - member.membership_type
// - member.is_board_member
// - member.is_admin
// - specific email domains for admins
// - etc.
// Example logic (uncomment and modify as needed):
/*
if (member.email && member.email.includes('@admin.monacousa.org')) {
return 'admin';
}
if (member.membership_type === 'Board' || member.is_board_member === 'true') {
return 'board';
}
*/
// Default to user tier for all members
return 'user';
}