From 4c7ed8ea0d5c58493f5c9c93fe2483e7c09bed38 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 14:30:02 +0100 Subject: [PATCH 1/8] refacto(Resume Service): move view count increment outside of findOne --- apps/server/src/resume/resume.service.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/server/src/resume/resume.service.ts b/apps/server/src/resume/resume.service.ts index 53d4ab243..3a4669a7d 100644 --- a/apps/server/src/resume/resume.service.ts +++ b/apps/server/src/resume/resume.service.ts @@ -84,21 +84,19 @@ export class ResumeService { }; } - async findOneByUsernameSlug(username: string, slug: string, userId?: string) { - const resume = await this.prisma.resume.findFirstOrThrow({ + async findOneByUsernameSlug(username: string, slug: string) { + return await this.prisma.resume.findFirstOrThrow({ where: { user: { username }, slug, visibility: "public" }, }); + } + async incrementViewCountForOne(resumeId: string) { // Update statistics: increment the number of views by 1 - if (!userId) { - await this.prisma.statistics.upsert({ - where: { resumeId: resume.id }, - create: { views: 1, downloads: 0, resumeId: resume.id }, - update: { views: { increment: 1 } }, - }); - } - - return resume; + await this.prisma.statistics.upsert({ + where: { resumeId }, + create: { views: 1, downloads: 0, resumeId }, + update: { views: { increment: 1 } }, + }); } async update(userId: string, id: string, updateResumeDto: UpdateResumeDto) { From 589bd2c16205b3171d66a0699cec2f0e7c108047 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 14:34:57 +0100 Subject: [PATCH 2/8] refacto(Resume Controller): reflecting previous change in controller --- apps/server/src/resume/resume.controller.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/server/src/resume/resume.controller.ts b/apps/server/src/resume/resume.controller.ts index 29d93266f..ab717465d 100644 --- a/apps/server/src/resume/resume.controller.ts +++ b/apps/server/src/resume/resume.controller.ts @@ -99,11 +99,15 @@ export class ResumeController { @Param("slug") slug: string, @User("id") userId: string, ) { - const resume = await this.resumeService.findOneByUsernameSlug(username, slug, userId); + const resume = await this.resumeService.findOneByUsernameSlug(username, slug); // Hide private notes from public resume API responses set(resume.data as ResumeData, "metadata.notes", undefined); + if (!userId) { + await this.resumeService.incrementViewCountForOne(resume.id); + } + return resume; } From 62c09ea142452fb30cd4d3a4939803402ad5b373 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 14:35:24 +0100 Subject: [PATCH 3/8] feature(Resume Controller): add route to get Resume without incrementing view count --- apps/server/src/resume/resume.controller.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/server/src/resume/resume.controller.ts b/apps/server/src/resume/resume.controller.ts index ab717465d..33f7bf576 100644 --- a/apps/server/src/resume/resume.controller.ts +++ b/apps/server/src/resume/resume.controller.ts @@ -92,6 +92,20 @@ export class ResumeController { return this.resumeService.findOneStatistics(id); } + @Get("/public/:username/:slug/stealthily") + @UseGuards(OptionalGuard) + async findOneByUsernameSlugStealthily( + @Param("username") username: string, + @Param("slug") slug: string, + ) { + const resume = await this.resumeService.findOneByUsernameSlug(username, slug); + + // Hide private notes from public resume API responses + set(resume.data as ResumeData, "metadata.notes", undefined); + + return resume; + } + @Get("/public/:username/:slug") @UseGuards(OptionalGuard) async findOneByUsernameSlug( From db86f3275b7841740d0f3539952c9c98daaa2e06 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 14:37:37 +0100 Subject: [PATCH 4/8] feature(Sharing section): add Raw Json URL card to Sharing section --- .../sidebars/right/sections/sharing.tsx | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx b/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx index b048a5432..c19f28b28 100644 --- a/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx +++ b/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx @@ -4,6 +4,7 @@ import { Button, Input, Label, Switch, Tooltip } from "@reactive-resume/ui"; import { AnimatePresence, motion } from "framer-motion"; import { useToast } from "@/client/hooks/use-toast"; +import { axios } from "@/client/libs/axios" import { useUser } from "@/client/services/user"; import { useResumeStore } from "@/client/stores/resume"; @@ -20,6 +21,7 @@ export const SharingSection = () => { // Constants const url = `${window.location.origin}/${username}/${slug}`; + const rawJsonUrl = `${window.location.origin}${axios.defaults.baseURL}/resume/public/${username}/${slug}/stealthily`; const onCopy = async () => { await navigator.clipboard.writeText(url); @@ -31,6 +33,16 @@ export const SharingSection = () => { }); }; + const onRawJsonCopy = async () => { + await navigator.clipboard.writeText(rawJsonUrl); + + toast({ + variant: "success", + title: t`A link has been copied to your clipboard.`, + description: t`Anyone with this link can view and download the json data of the resume. This link will not count towards the resume view count.`, + }); + }; + return (
@@ -65,21 +77,50 @@ export const SharingSection = () => { {isPublic && ( - +
+ -
- +
+ - - - + + + +
+
+ +
+ + +
+ + + + + +
)} From 2e11e6488137452c1f16501a6d7d2b579427e547 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 22:42:48 +0100 Subject: [PATCH 5/8] fix(Resume Controller): address CodeRabbit nitpick nb1 --- apps/server/src/resume/resume.controller.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/server/src/resume/resume.controller.ts b/apps/server/src/resume/resume.controller.ts index 33f7bf576..d7d6c8e59 100644 --- a/apps/server/src/resume/resume.controller.ts +++ b/apps/server/src/resume/resume.controller.ts @@ -100,8 +100,7 @@ export class ResumeController { ) { const resume = await this.resumeService.findOneByUsernameSlug(username, slug); - // Hide private notes from public resume API responses - set(resume.data as ResumeData, "metadata.notes", undefined); + redactPrivateNotes(resume as ResumeDto); return resume; } @@ -115,8 +114,7 @@ export class ResumeController { ) { const resume = await this.resumeService.findOneByUsernameSlug(username, slug); - // Hide private notes from public resume API responses - set(resume.data as ResumeData, "metadata.notes", undefined); + redactPrivateNotes(resume as ResumeDto); if (!userId) { await this.resumeService.incrementViewCountForOne(resume.id); @@ -173,3 +171,7 @@ export class ResumeController { } } } + +function redactPrivateNotes(resume: ResumeDto) { + set(resume.data, "metadata.notes", undefined); +} From 1749f95086193584ae918a6d64617246499d1797 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Sun, 21 Dec 2025 22:43:08 +0100 Subject: [PATCH 6/8] fix(Sharing section): address CodeRabbit nitpick nb4 --- .../src/pages/builder/sidebars/right/sections/sharing.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx b/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx index c19f28b28..f8ebe472e 100644 --- a/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx +++ b/apps/client/src/pages/builder/sidebars/right/sections/sharing.tsx @@ -103,7 +103,7 @@ export const SharingSection = () => {
From d7409832abe6ac01c8916a8b8dcb2c9d174aa869 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Mon, 22 Dec 2025 10:26:36 +0100 Subject: [PATCH 7/8] fix(Resume Controller): address CI lint issue --- apps/server/src/resume/resume.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/resume/resume.controller.ts b/apps/server/src/resume/resume.controller.ts index d7d6c8e59..e962400dc 100644 --- a/apps/server/src/resume/resume.controller.ts +++ b/apps/server/src/resume/resume.controller.ts @@ -20,7 +20,7 @@ import { ResumeDto, UpdateResumeDto, } from "@reactive-resume/dto"; -import { ResumeData, resumeDataSchema } from "@reactive-resume/schema"; +import { resumeDataSchema } from "@reactive-resume/schema"; import { ErrorMessage } from "@reactive-resume/utils"; import set from "lodash.set"; import { zodToJsonSchema } from "zod-to-json-schema"; From 76b61d013530276cd8ce1262005a6882b286a304 Mon Sep 17 00:00:00 2001 From: y8a5y Date: Mon, 22 Dec 2025 10:40:40 +0100 Subject: [PATCH 8/8] fix(Contrib file): add steps to ensure successful CI --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3501deab4..d192d3d50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,4 +82,14 @@ git commit -m "fix(homepage): fix typo on homepage in the faq section" It helps to be as descriptive as possible in commit messages so that users can be aware of the changes made by you. +Then, you can run the basic checks, as to make sure the CI has the most chance of being successful: + +``` +pnpm run lint + +pnpm run test + +pnpm run build +``` + Finally, create a pull request to merge the changes on your forked repository to the original repository hosted on AmruthPillai/Reactive-Resume. I can take a look at the changes you've made when I have the time and have it merged onto the app.