monacousa-portal/server/utils/member-id.ts

124 lines
4.1 KiB
TypeScript

import { getMembers, updateMember } from './nocodb';
import type { Member } from '~/utils/types';
/**
* Generates a unique member ID in the format MUSA-{unique 6-digit number}
* Checks against existing member IDs to ensure uniqueness
* @returns Promise<string> - The unique member ID
*/
export async function generateMemberID(): Promise<string> {
console.log('[member-id] Generating new member ID...');
let memberID: string;
let isUnique = false;
let attempts = 0;
const maxAttempts = 100; // Prevent infinite loops
while (!isUnique && attempts < maxAttempts) {
attempts++;
// Generate a 6-digit number (100000 to 999999)
const uniqueNumber = Math.floor(Math.random() * 900000) + 100000;
memberID = `MUSA-${uniqueNumber}`;
console.log(`[member-id] Attempt ${attempts}: Checking uniqueness of ${memberID}`);
// Check if ID already exists in database
const existingMember = await checkMemberIDExists(memberID);
isUnique = !existingMember;
if (!isUnique) {
console.log(`[member-id] ID ${memberID} already exists, generating new one...`);
}
}
if (attempts >= maxAttempts) {
console.error('[member-id] Failed to generate unique member ID after maximum attempts');
throw new Error('Failed to generate unique member ID after maximum attempts');
}
console.log(`[member-id] ✅ Generated unique member ID: ${memberID!} (attempts: ${attempts})`);
return memberID!;
}
/**
* Checks if a member ID already exists in the database
* @param memberID - The member ID to check
* @returns Promise<boolean> - True if the member ID exists, false otherwise
*/
export async function checkMemberIDExists(memberID: string): Promise<boolean> {
try {
console.log(`[member-id] Checking if member ID exists: ${memberID}`);
// Get all members and check for duplicate member_id
const members = await getMembers();
const memberList = Array.isArray(members) ? members : members?.list || [];
const existingMember = memberList.find((member: Member) => member.member_id === memberID);
const exists = !!existingMember;
console.log(`[member-id] Member ID ${memberID} exists: ${exists}`);
return exists;
} catch (error: any) {
console.error('[member-id] Error checking member ID existence:', error);
// In case of error, assume it doesn't exist to allow generation to continue
// The actual creation will fail if there's a real database issue
return false;
}
}
/**
* Finds all members without a member_id field
* Used for migration purposes
* @returns Promise<Member[]> - Array of members without member IDs
*/
export async function findMembersWithoutMemberID(): Promise<Member[]> {
try {
console.log('[member-id] Finding members without member IDs for migration...');
const members = await getMembers();
const memberList = Array.isArray(members) ? members : members?.list || [];
const membersWithoutId = memberList.filter((member: Member) =>
!member.member_id || member.member_id.trim() === ''
);
console.log(`[member-id] Found ${membersWithoutId.length} members without member IDs`);
return membersWithoutId;
} catch (error: any) {
console.error('[member-id] Error finding members without member IDs:', error);
throw error;
}
}
/**
* Validates a member ID format
* @param memberID - The member ID to validate
* @returns boolean - True if valid format, false otherwise
*/
export function isValidMemberIDFormat(memberID: string): boolean {
if (!memberID || typeof memberID !== 'string') {
return false;
}
// Check format: MUSA-{6 digits}
const memberIDRegex = /^MUSA-\d{6}$/;
return memberIDRegex.test(memberID);
}
/**
* Extracts the numeric part from a member ID
* @param memberID - The member ID (e.g., "MUSA-123456")
* @returns number - The numeric part or null if invalid
*/
export function extractMemberIDNumber(memberID: string): number | null {
if (!isValidMemberIDFormat(memberID)) {
return null;
}
const numericPart = memberID.replace('MUSA-', '');
return parseInt(numericPart, 10);
}