const express require(express);
const multer require(multer);
const admin require(../config/firebase);
const getStorage require(firebase-admin/storage);
const getFirestore require(firebase-admin/firestore);
const FieldValue require(firebase-admin/firestore);
const parseResumeDetails require(../flows/parse-resume-details-flow);
const generateSessionToken require(../middleware/session-token-auth);
const pdfParse require(pdf-parse);
const mammoth require(mammoth);
const axios require(axios);
const router ();
// reCAPTCHA verification function
const verifyRecaptcha async (token) >
try
if (!)
(RECAPTCHASECRETKEY not configured);
return false;
if (!token)
(No reCAPTCHA token provided);
return false;
const response await ( null
params:
secret:
response: token
);
return ;
catch (error)
(reCAPTCHA verification error: );
return false;
;
// Configure multer for file uploads
const upload multer(
storage: ()
limits:
fileSize: 5 * 1024 * 1024 // 5MB limit
fileFilter: (req file cb) >
const allowedTypes application/pdf application/ application/msword;
if (())
cb(null true);
else
cb(new Error(Invalid file type. Only PDF DOCX and DOC files are allowed.) false);
);
// Helper function to extract text from PDF
const parsePdf async (buffer) >
try
const data await pdfParse(buffer);
return
.replace(/s/g )
.replace(/ns*n/g n)
.trim();
catch (error)
(Error parsing PDF: error);
throw new Error(Failed to parse PDF file);
;
// Helper function to extract text from DOCX
const parseDocx async (buffer) >
const result await ( buffer );
return ;
;
// Helper function to validate resume content
const isResumeContent (text) >
const lowerText ();
const resumeKeywords
education experience skills projects objective summary
certifications linkedin bachelor intern developer work
employment job position role responsibilities achievements
university college degree graduated phone email contact
;
const forbiddenKeywords
solicitation contracting officer rfq gsa ebuy amendment
section phase 1 phase 2 government reserves vendor questions
usda task order
;
const emailPattern /bA-Z0-9.%/i;
const phonePattern /b(:d13)-.s()d3-.s()d34-.s()d4b/;
const keywordCount (k > (k)).length;
const hasContact (text) (text);
const hasForbidden (k > (k));
const hasResumeContent keywordCount > 2 hasContact;
return hasResumeContent && !hasForbidden;
;
// POST /api/upload-resume
(/ (resume) async (req res) >
try
if (!)
return (400).json( error: No file uploaded );
const recaptchaToken sessionId ;
if (!recaptchaToken)
return (400).json( error: reCAPTCHA token is required );
// Verify reCAPTCHA token
try
const recaptchaSecret ;
if (!recaptchaSecret)
(RECAPTCHASECRETKEY not configured skipping verification);
else
const verificationUrl
const response await (verificationUrl null
params:
secret: recaptchaSecret
response: recaptchaToken
remoteip:
);
const success score action ;
if (!success)
return (400).json(
error: reCAPTCHA verification failed
message: Please try again or contact support if the problem persists
);
// For v3 reCAPTCHA check the score (0.0 is very likely a bot 1.0 is very likely a human)
if (score ! undefined && score < 0.5)
return (400).json(
error: Suspicious activity detected
message: Please try again or contact support if you believe this is an error
);
catch (error)
(reCAPTCHA verification error: );
return (400).json(
error: reCAPTCHA verification failed
message: Please try again or contact support if the problem persists
);
// Note: reCAPTCHA verification is already handled by middleware
// Note: reCAPTCHA verification is already handled by middleware
// STEP 1: Upload file to Firebase Storage FIRST
const storage getStorage();
const db getFirestore();
// Create organized folder structure: uploads/resumes/YYYY/MM/DD/filename
const now new Date();
const year ();
const month String(() 1).padStart(2 0);
const day String(()).padStart(2 0);
const timestamp ();
const fileName $timestamp-$;
const filePath uploads/resumes/$year/$month/$day/$fileName;
try
const bucket ();
const file (filePath);
await (
metadata:
contentType:
metadata:
originalName:
uploadedAt: new Date().toISOString()
);
catch (error)
( Error uploading to Firebase Storage: error);
return (500).json( error: Failed to upload file to storage );
// STEP 2: Extract text from file
let rawExtractedText;
const fileExtension ((.)).toLowerCase();
try
if (fileExtension .pdf)
rawExtractedText await parsePdf();
else if (fileExtension .docx fileExtension .doc)
rawExtractedText await parseDocx();
else
return (400).json( error: Unsupported file type );
catch (error)
( Error extracting text: error);
return (500).json( error: Failed to extract text from file );
// STEP 3: Validate resume content
if (!isResumeContent(rawExtractedText))
return (400).json(
error: Uploaded file does not appear to be a resume. Please ensure the file contains resume content with contact information work experience education or skills.
);
// STEP 4: Parse resume details using local parser
let parsedDetails;
try
parsedDetails await parseResumeDetails( resumeText: rawExtractedText );
catch (error)
( Error parsing resume details: error);
return (500).json( error: Failed to parse resume details );
// STEP 5: Validate parsed details
// Helper function to check for invalid placeholder values
const isValidValue (value) >
if (!value typeof value ! string) return false;
const trimmed ();
if (!trimmed) return false;
// Check for common placeholder patterns
const placeholderPatterns
/^(000)$/ //etc.
/^(N/ANot providedplaceholdernullundefined)$/i
/^(unknownnot foundnot available)$/i
/^0s$/ // Only zeros and spaces
/^xs$/i // Only Xs and spaces
;
return !(pattern > (trimmed));
;
const hasName && isValidValue();
const hasExperience && > 0;
const hasEducation && > 0;
const hasSkills ( && > 0)
( && > 0)
( && > 0);
const hasSummary && isValidValue();
if (!hasName && !hasExperience && !hasEducation && !hasSkills && !hasSummary)
return (400).json(
error: Resume appears to be missing essential details. Please ensure your resume contains at least one of: name work experience education skills or summary.
);
// Save data to Firestore
const maskedResumeText ...detailsToStore parsedDetails;
const resumeDoc
...detailsToStore
resumeStoragePath: filePath
rawTextLength:
fileName:
parsedAt: ()
analysis:
sessionId: sessionId unknown
captchaTokenUsed: recaptchaToken
uploadedAt: ()
uploadDate:
year: year
month: month
day: day
timestamp: timestamp
;
// Create session if not provided
if (!sessionId sessionId unknown)
const sessionId upload$()$().toString(36).substr(2 9);
sessionId;
let docRef;
try
const db getFirestore();
docRef await (parsedresumes).add(resumeDoc);
catch (error)
(Error saving to Firestore: error);
return (500).json( error: Failed to save resume data );
// Generate session token after successful upload
let sessionToken null;
try
sessionToken await generateSessionToken(sessionId unknown
success: true
timestamp: new Date().toISOString()
);
catch (error)
(Failed to generate session token: error);
// Continue without token - user will need reCAPTCHA for analysis
// Return success response
(
success: true
resumeId:
fileName:
parsedDetails:
...parsedDetails
maskedResumeText: null
sessionToken: sessionToken // Include session token in response
message: Resume uploaded and parsed successfully
);
catch (error)
(Error in upload-resume: error);
(500).json( error: Internal server error );
);
router;
Location: Toronto Ontario Canada
Responsibilities:
- Lead organizational development strategy development business planning and funding requests.
- Gather and develop requirements to create and maintain detailed project schedules and integrated plans.
- Monitor and forecast project costs providing reporting and input to ensure targets are met.
- Develop and manage project schedules deliverables and scope.
- Promote project management best practices and adherence to standard methodologies.
- Direct project teams to ensure project deliverables are delivered on time and adhere to standards.
- Coordinate and monitor project processes and develop/communicate guidelines and procedures.
- Resolve resourcing and interpersonal conflicts negotiate changes to resourcing and ensure knowledge is shared among team members.
- Develop complex project budgets based on multiple funding channels and cross-ministry dependencies.
- Manage large project budgets and ensure a high level of fiscal control and accountability.
- Proactively identify potential risk events and issues developing mitigating strategies.
- Articulate and prioritize issues and risks at senior executive levels recommending mitigation strategies.
- Establish and participate in steering committee and stakeholder forums.
- Provide project program and/or portfolio reporting to multi-stakeholders at senior executive levels.
- Use appropriate strategies to overcome resistance to change and capitalize on forces in support of change.
- Promote standards and best practices for project management.
- Provide project management on large-scale complex projects.
- Ensure project deliverables meet business requirements on time scope and budget.
Required Skills & Certifications:
Preferred Skills & Certifications:
Special Considerations:
Scheduling: