diff --git a/src/components/resume/store/resume.ts b/src/components/resume/store/resume.ts index 5673fafe8..5b1764494 100644 --- a/src/components/resume/store/resume.ts +++ b/src/components/resume/store/resume.ts @@ -8,7 +8,7 @@ import { create } from "zustand/react"; import { orpc, type RouterOutput } from "@/integrations/orpc/client"; import type { ResumeData } from "@/schema/resume/data"; -type Resume = Omit; +type Resume = Pick; type ResumeStoreState = { resume: Resume; diff --git a/src/integrations/orpc/router/printer.ts b/src/integrations/orpc/router/printer.ts index 71bc5dfa0..8a4a17255 100644 --- a/src/integrations/orpc/router/printer.ts +++ b/src/integrations/orpc/router/printer.ts @@ -15,8 +15,8 @@ export const printerRouter = { .input(z.object({ id: z.string() })) .output(z.object({ url: z.string() })) .handler(async ({ input, context }) => { - const resume = await resumeService.getByIdForPrinter({ id: input.id }); - const url = await printerService.printResumeAsPDF(resume); + const { id, data, userId } = await resumeService.getByIdForPrinter({ id: input.id }); + const url = await printerService.printResumeAsPDF({ id, data, userId }); if (!context.user) { await resumeService.statistics.increment({ id: input.id, downloads: true }); @@ -36,8 +36,8 @@ export const printerRouter = { .input(z.object({ id: z.string() })) .output(z.object({ url: z.string() })) .handler(async ({ input }) => { - const resume = await resumeService.getByIdForPrinter({ id: input.id }); - const url = await printerService.getResumeScreenshot(resume); + const { id, data, userId, updatedAt } = await resumeService.getByIdForPrinter({ id: input.id }); + const url = await printerService.getResumeScreenshot({ id, data, userId, updatedAt }); return { url }; }), diff --git a/src/integrations/orpc/services/printer.ts b/src/integrations/orpc/services/printer.ts index 173d6acf7..66c256923 100644 --- a/src/integrations/orpc/services/printer.ts +++ b/src/integrations/orpc/services/printer.ts @@ -64,9 +64,9 @@ export const printerService = { * 7. Upload to storage and return the URL */ printResumeAsPDF: async ( - input: Pick, "userId" | "id" | "data">, + input: Pick, "id" | "data" | "userId">, ): Promise => { - const { id, userId, data } = input; + const { id, data, userId } = input; // Step 1: Delete any existing PDF for this resume to ensure fresh generation const storageService = getStorageService(); @@ -190,15 +190,16 @@ export const printerService = { }, getResumeScreenshot: async ( - input: Pick, "userId" | "id" | "data">, + input: Pick, "userId" | "id" | "data" | "updatedAt">, ): Promise => { - const { id, userId, data } = input; + const { id, userId, data, updatedAt } = input; const storageService = getStorageService(); const screenshotPrefix = `uploads/${userId}/screenshots/${id}`; const existingScreenshots = await storageService.list(screenshotPrefix); const now = Date.now(); + const resumeUpdatedAt = updatedAt.getTime(); if (existingScreenshots.length > 0) { const sortedFiles = existingScreenshots @@ -214,8 +215,17 @@ export const printerService = { const latest = sortedFiles[0]; const age = now - latest.timestamp; + // Return existing screenshot if it's still fresh (within TTL) if (age < SCREENSHOT_TTL) return new URL(latest.path, env.APP_URL).toString(); + // Screenshot is stale (past TTL), but only regenerate if the resume + // was updated after the screenshot was taken. If the resume hasn't + // changed, keep using the existing screenshot to avoid unnecessary work. + if (resumeUpdatedAt <= latest.timestamp) { + return new URL(latest.path, env.APP_URL).toString(); + } + + // Resume was updated after the screenshot - delete old ones and regenerate await Promise.all(sortedFiles.map((file) => storageService.delete(file.path))); } } diff --git a/src/integrations/orpc/services/resume.ts b/src/integrations/orpc/services/resume.ts index c25f1d47f..bd2331a73 100644 --- a/src/integrations/orpc/services/resume.ts +++ b/src/integrations/orpc/services/resume.ts @@ -135,13 +135,13 @@ export const resumeService = { const [resume] = await db .select({ id: schema.resume.id, - userId: schema.resume.userId, name: schema.resume.name, slug: schema.resume.slug, tags: schema.resume.tags, data: schema.resume.data, - isPublic: schema.resume.isPublic, + userId: schema.resume.userId, isLocked: schema.resume.isLocked, + updatedAt: schema.resume.updatedAt, }) .from(schema.resume) .where(eq(schema.resume.id, input.id));