ironGym/webapp/src/app/api/member-lookup/route.ts

145 lines
5.0 KiB
TypeScript

/**
* @description API route for encrypted member lookup
* This endpoint receives an encrypted member ID and returns member information
* for the Expo Android app
*/
import dbConnect from "@/database/dbConnect";
import { NextResponse } from "next/server";
import memberModel from "@/database/models/memberModel";
import { decryptMemberId } from "@/utils/encryption";
// GET METHOD - Lookup member by encrypted ID
export async function GET(req: Request) {
try {
// Connect to the database
await dbConnect();
// Get the encrypted member ID from query parameters
const { searchParams } = new URL(req.url);
const encryptedId = searchParams.get('encryptedId');
// Validate the encrypted ID parameter
if (!encryptedId) {
return NextResponse.json({
success: false,
message: "Missing encrypted member ID",
}, {
status: 400,
headers: {
"content-type": "application/json"
}
});
}
// Decrypt the member ID
let memberId: string;
try {
memberId = decryptMemberId(encryptedId);
} catch (error) {
return NextResponse.json({
success: false,
message: "Invalid encrypted member ID",
}, {
status: 400,
headers: {
"content-type": "application/json"
}
});
}
// Find the member by ID
const member = await memberModel.findById(memberId);
if (!member) {
return NextResponse.json({
success: false,
message: "Member not found",
}, {
status: 404,
headers: {
"content-type": "application/json"
}
});
}
// Calculate plan status based on individual services (active/expired)
const currentTime = Math.floor(Date.now() / 1000);
const activeServices = member.services?.filter(service =>
service.active && service.planExpAt_unix > currentTime
) || [];
const expiredServices = member.services?.filter(service =>
!service.active || service.planExpAt_unix <= currentTime
) || [];
const planStatus = activeServices.length > 0 ? 'active' : 'expired';
// Get the latest expiration date from active services
const latestExpiration = activeServices.length > 0
? Math.max(...activeServices.map(s => s.planExpAt_unix))
: member.planExpAt_unix;
// Prepare detailed services information
const servicesInfo = member.services?.map(service => ({
serviceID: service.serviceID,
serviceName: service.serviceName,
registeredAt: service.registeredAt,
registeredAt_unix: service.registeredAt_unix,
planDelay: service.planDelay,
planDelay_unix: service.planDelay_unix,
planExpAt: service.planExpAt,
planExpAt_unix: service.planExpAt_unix,
planUpdatedAt: service.planUpdatedAt,
planUpdatedAt_unix: service.planUpdatedAt_unix,
active: service.active,
status: (service.active && service.planExpAt_unix > currentTime) ? 'active' : 'expired',
daysRemaining: service.planExpAt_unix > currentTime
? Math.ceil((service.planExpAt_unix - currentTime) / (24 * 60 * 60))
: 0
})) || [];
// Prepare the response data with detailed services information
const memberInfo = {
name: `${member.firstName} ${member.lastName}`,
gender: member.gendre,
planDelay: member.planDelay,
planStart: member.planUpdatedAt,
planStatus: planStatus,
planExpAt: new Date(latestExpiration * 1000).toUTCString(),
activeServicesCount: activeServices.length,
expiredServicesCount: expiredServices.length,
totalServicesCount: member.services?.length || 0,
services: servicesInfo
};
// Return the member information
return NextResponse.json({
success: true,
message: "Member information retrieved successfully",
data: memberInfo,
}, {
status: 200,
headers: {
"content-type": "application/json"
}
});
} catch (error) {
console.error('Error in member lookup API:', error);
// Return server error response
return NextResponse.json({
success: false,
message: "Server error",
}, {
status: 500,
headers: {
"content-type": "application/json"
}
});
}
}
// Set revalidation time
export const revalidate = 5;