@@ -2,11 +2,20 @@ import { Request, Response } from 'express';
22import { config } from '../config/config' ;
33import fs from 'fs' ;
44import path from 'path' ;
5- import { scoreResume , streamScoreResume , parseResumeFields } from '../services/resume_service' ;
5+ import { scoreResume , streamScoreResume , parseResumeFields ,
6+ saveParsedResume , getResumeByOwner , updateResume } from '../services/resume_service' ;
67import multer from 'multer' ;
8+ import { getResumeBuffer , resumeExists } from '../services/resources_service' ;
79import { CustomRequest } from "types/customRequest" ;
810import { handleError } from "../utils/handle_error" ;
911
12+ // Simple in-memory cache: { [key: string]: { scoreAndFeedback, timestamp } }
13+ const resumeScoreCache : Record < string , { data : any , timestamp : number } > = { } ;
14+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000 ; // 24 hour
15+
16+ const getCacheKey = ( filename : string , jobDescription ?: string ) =>
17+ `${ filename } ::${ jobDescription || '' } ` ;
18+
1019const getResumeScore = async ( req : Request , res : Response ) => {
1120 try {
1221 const { filename } = req . params ;
@@ -17,7 +26,14 @@ const getResumeScore = async (req: Request, res: Response) => {
1726 return res . status ( 404 ) . send ( 'Resume not found' ) ;
1827 }
1928
29+ const cacheKey = getCacheKey ( filename , jobDescription ) ;
30+ const cached = resumeScoreCache [ cacheKey ] ;
31+ if ( cached && Date . now ( ) - cached . timestamp < CACHE_TTL_MS ) {
32+ return res . status ( 200 ) . send ( cached . data ) ;
33+ }
34+
2035 const scoreAndFeedback = await scoreResume ( resumePath , jobDescription ) ;
36+ resumeScoreCache [ cacheKey ] = { data : scoreAndFeedback , timestamp : Date . now ( ) } ;
2137 return res . status ( 200 ) . send ( scoreAndFeedback ) ;
2238 } catch ( error ) {
2339 if ( error instanceof TypeError ) {
@@ -28,7 +44,7 @@ const getResumeScore = async (req: Request, res: Response) => {
2844 }
2945} ;
3046
31- const getStreamResumeScore = async ( req : Request , res : Response ) => {
47+ const getStreamResumeScore = async ( req : CustomRequest , res : Response ) => {
3248 try {
3349 const { filename } = req . params ;
3450 const resumePath = path . resolve ( config . resources . resumesDirectoryPath ( ) , filename ) ;
@@ -38,6 +54,18 @@ const getStreamResumeScore = async (req: Request, res: Response) => {
3854 return res . status ( 404 ) . send ( 'Resume not found' ) ;
3955 }
4056
57+ const cacheKey = getCacheKey ( filename , jobDescription ) ;
58+ const cached = resumeScoreCache [ cacheKey ] ;
59+ if ( cached && Date . now ( ) - cached . timestamp < CACHE_TTL_MS ) {
60+ // Stream cached result as SSE
61+ res . setHeader ( 'Content-Type' , 'text/event-stream' ) ;
62+ res . setHeader ( 'Cache-Control' , 'no-cache' ) ;
63+ res . setHeader ( 'Connection' , 'keep-alive' ) ;
64+ res . write ( `data: ${ JSON . stringify ( { ...cached . data , done : true } ) } \n\n` ) ;
65+ res . end ( ) ;
66+ return ;
67+ }
68+
4169 // Set headers for SSE
4270 res . setHeader ( 'Content-Type' , 'text/event-stream' ) ;
4371 res . setHeader ( 'Cache-Control' , 'no-cache' ) ;
@@ -49,17 +77,24 @@ const getStreamResumeScore = async (req: Request, res: Response) => {
4977 } ) ;
5078
5179 // Stream the response
52- const score = await streamScoreResume (
80+ let fullChunk = '' ;
81+ const [ score , fullText ] = await streamScoreResume (
5382 resumePath ,
5483 jobDescription ,
5584 ( chunk ) => {
85+ fullChunk += chunk ;
5686 res . write ( `data: ${ JSON . stringify ( { chunk } ) } \n\n` ) ;
5787 }
5888 ) ;
5989
6090 // Send the final score
6191 res . write ( `data: ${ JSON . stringify ( { score, done : true } ) } \n\n` ) ;
6292 res . end ( ) ;
93+
94+ // Optionally cache the result (score and fullText)
95+ resumeScoreCache [ cacheKey ] = { data : { score, fullText } , timestamp : Date . now ( ) } ;
96+ await updateResume ( req . user . id , jobDescription , fullText , score ) ;
97+
6398 } catch ( error ) {
6499 if ( error instanceof TypeError ) {
65100 return res . status ( 400 ) . send ( error . message ) ;
@@ -69,17 +104,57 @@ const getStreamResumeScore = async (req: Request, res: Response) => {
69104 }
70105} ;
71106
72- const parseResume = async ( req : Request , res : Response ) => {
107+
108+ const parseResume = async ( req : CustomRequest , res : Response ) => {
73109 try {
74- if ( ! req . file ) {
110+ if ( ! req . body . resumefileName ) {
75111 return res . status ( 400 ) . json ( { error : 'No resume file uploaded' } ) ;
76112 }
77- const parsed = await parseResumeFields ( req . file . buffer , req . file . originalname ) ;
113+ else if ( ! resumeExists ( req . body . resumefileName ) ) {
114+ return res . status ( 400 ) . json ( { error : 'No resume file uploaded' } ) ;
115+ }
116+
117+ const resumeFilename = req . body . resumefileName ;
118+ const parsed = await parseResumeFields ( getResumeBuffer ( req . body . resumefileName ) , resumeFilename ) ;
119+ const resumeData = await saveParsedResume ( parsed , req . user . id , resumeFilename , req . body . originfilename ) ;
120+
78121 return res . status ( 200 ) . json ( parsed ) ;
79122 } catch ( err : any ) {
80123 console . error ( 'Error parsing resume:' , err ) ;
81124 return handleError ( err , res ) ;
82125 }
83126 } ;
84127
85- export default { parseResume, getResumeScore, getStreamResumeScore } ;
128+ const getResume = async ( req : CustomRequest , res : Response ) => {
129+ try {
130+ const ownerId = req . user . id ;
131+ const resume = await getResumeByOwner ( ownerId ) ;
132+
133+ if ( ! resume ) {
134+ return res . status ( 404 ) . json ( { error : 'Resume not found' } ) ;
135+ }
136+
137+ return res . status ( 200 ) . json ( resume ) ;
138+ } catch ( error ) {
139+ console . error ( 'Error retrieving resume:' , error ) ;
140+ return handleError ( error , res ) ;
141+ }
142+ }
143+
144+
145+ const getResumeData = async ( req : CustomRequest , res : Response ) => {
146+ try {
147+ const ownerId = req . user . id ;
148+ // Get the optional version parameter from query string
149+ const version = req . query . version ? parseInt ( req . query . version as string ) : undefined ;
150+ const resume = await getResumeByOwner ( ownerId , version ) ;
151+
152+ return res . status ( 200 ) . json ( resume ) ;
153+ } catch ( error ) {
154+ console . error ( 'Error retrieving resume data:' , error ) ;
155+ return handleError ( error , res ) ;
156+ }
157+ } ;
158+
159+ export default { parseResume, getResumeScore,
160+ getStreamResumeScore, getResumeData, getResume } ;
0 commit comments