Skip to content

Commit

Permalink
fixes #2082, fixes #2066 - fallback to cuid2 if filename contains non…
Browse files Browse the repository at this point in the history
…-latin characters
  • Loading branch information
AmruthPillai committed Jan 12, 2025
1 parent 2d62504 commit 6335ad1
Show file tree
Hide file tree
Showing 11 changed files with 54 additions and 36 deletions.
9 changes: 5 additions & 4 deletions apps/client/src/pages/dashboard/resumes/_dialogs/resume.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import {
Input,
Tooltip,
} from "@reactive-resume/ui";
import { cn, generateRandomName, kebabCase } from "@reactive-resume/utils";
import { cn, generateRandomName } from "@reactive-resume/utils";
import slugify from "@sindresorhus/slugify";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
Expand Down Expand Up @@ -71,7 +72,7 @@ export const ResumeDialog = () => {
}, [isOpen, payload]);

useEffect(() => {
const slug = kebabCase(form.watch("title"));
const slug = slugify(form.watch("title"));
form.setValue("slug", slug);
}, [form.watch("title")]);

Expand Down Expand Up @@ -122,15 +123,15 @@ export const ResumeDialog = () => {
const onGenerateRandomName = () => {
const name = generateRandomName();
form.setValue("title", name);
form.setValue("slug", kebabCase(name));
form.setValue("slug", slugify(name));
};

const onCreateSample = async () => {
const randomName = generateRandomName();

await duplicateResume({
title: randomName,
slug: kebabCase(randomName),
slug: slugify(randomName),
data: sampleResume,
});

Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/printer/printer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ export class PrinterService {

return resumeUrl;
} catch (error) {
this.logger.error(error);

throw new InternalServerErrorException(
ErrorMessage.ResumePrinterError,
(error as Error).message,
Expand Down
7 changes: 4 additions & 3 deletions apps/server/src/resume/resume.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { Prisma } from "@prisma/client";
import { CreateResumeDto, ImportResumeDto, ResumeDto, UpdateResumeDto } from "@reactive-resume/dto";
import { defaultResumeData, ResumeData } from "@reactive-resume/schema";
import type { DeepPartial } from "@reactive-resume/utils";
import { ErrorMessage, generateRandomName, kebabCase } from "@reactive-resume/utils";
import { ErrorMessage, generateRandomName } from "@reactive-resume/utils";
import slugify from "@sindresorhus/slugify";
import deepmerge from "deepmerge";
import { PrismaService } from "nestjs-prisma";

Expand Down Expand Up @@ -40,7 +41,7 @@ export class ResumeService {
userId,
title: createResumeDto.title,
visibility: createResumeDto.visibility,
slug: createResumeDto.slug ?? kebabCase(createResumeDto.title),
slug: createResumeDto.slug ?? slugify(createResumeDto.title),
},
});
}
Expand All @@ -54,7 +55,7 @@ export class ResumeService {
visibility: "private",
data: importResumeDto.data,
title: importResumeDto.title ?? randomTitle,
slug: importResumeDto.slug ?? kebabCase(randomTitle),
slug: importResumeDto.slug ?? slugify(randomTitle),
},
});
}
Expand Down
10 changes: 8 additions & 2 deletions apps/server/src/storage/storage.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, InternalServerErrorException, Logger, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { createId } from "@paralleldrive/cuid2";
import slugify from "@sindresorhus/slugify";
import { MinioClient, MinioService } from "nestjs-minio-client";
import sharp from "sharp";

Expand Down Expand Up @@ -116,14 +117,19 @@ export class StorageService implements OnModuleInit {
) {
const extension = type === "resumes" ? "pdf" : "jpg";
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
const filepath = `${userId}/${type}/${filename}.${extension}`;

let normalizedFilename = slugify(filename);
if (!normalizedFilename) normalizedFilename = createId();

const filepath = `${userId}/${type}/${normalizedFilename}.${extension}`;
const url = `${storageUrl}/${filepath}`;

const metadata =
extension === "jpg"
? { "Content-Type": "image/jpeg" }
: {
"Content-Type": "application/pdf",
"Content-Disposition": `attachment; filename=${filename}.${extension}`,
"Content-Disposition": `attachment; filename=${normalizedFilename}.${extension}`,
};

try {
Expand Down
1 change: 1 addition & 0 deletions libs/dto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@reactive-resume/utils": "*",
"@reactive-resume/schema": "*",
"@sindresorhus/slugify": "^2.2.1",
"nestjs-zod": "^3.0.0",
"@swc/helpers": "~0.5.11",
"zod": "^3.24.1"
Expand Down
8 changes: 6 additions & 2 deletions libs/dto/src/resume/create.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { kebabCase } from "@reactive-resume/utils";
import slugify from "@sindresorhus/slugify";
import { createZodDto } from "nestjs-zod/dto";
import { z } from "zod";

export const createResumeSchema = z.object({
title: z.string().min(1),
slug: z.string().min(1).transform(kebabCase).optional(),
slug: z
.string()
.min(1)
.transform((value) => slugify(value))
.optional(),
visibility: z.enum(["public", "private"]).default("private"),
});

Expand Down
8 changes: 6 additions & 2 deletions libs/dto/src/resume/import.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { resumeDataSchema } from "@reactive-resume/schema";
import { kebabCase } from "@reactive-resume/utils";
import slugify from "@sindresorhus/slugify";
import { createZodDto } from "nestjs-zod/dto";
import { z } from "zod";

export const importResumeSchema = z.object({
title: z.string().optional(),
slug: z.string().min(1).transform(kebabCase).optional(),
slug: z
.string()
.min(1)
.transform((value) => slugify(value))
.optional(),
visibility: z.enum(["public", "private"]).default("private").optional(),
data: resumeDataSchema,
});
Expand Down
11 changes: 0 additions & 11 deletions libs/utils/src/namespaces/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,6 @@ export const extractUrl = (string: string) => {
return result ? result[0] : null;
};

export const kebabCase = (string?: string | null) => {
if (!string) return "";

return (
string
.match(/[A-Z]{2,}(?=[A-Z][a-z]+\d*|\b)|[A-Z]?[a-z]+\d*|[A-Z]|\d+/gu)
?.join("-")
.toLowerCase() ?? ""
);
};

export const generateRandomName = () => {
return uniqueNamesGenerator({
dictionaries: [adjectives, adjectives, animals],
Expand Down
11 changes: 0 additions & 11 deletions libs/utils/src/namespaces/tests/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
getInitials,
isEmptyString,
isUrl,
kebabCase,
processUsername,
} from "../string";

Expand Down Expand Up @@ -40,16 +39,6 @@ describe("extractUrl", () => {
});
});

describe("kebabCase", () => {
it("converts a string to kebab-case", () => {
expect(kebabCase("fooBar")).toBe("foo-bar");
expect(kebabCase("Foo Bar")).toBe("foo-bar");
expect(kebabCase("foo_bar")).toBe("foo-bar");
expect(kebabCase("")).toBe("");
expect(kebabCase(null)).toBe("");
});
});

describe("generateRandomName", () => {
it("generates a random name", () => {
const name = generateRandomName();
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@reactive-resume/source",
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
"version": "4.3.3",
"version": "4.3.4",
"license": "MIT",
"private": true,
"author": {
Expand Down Expand Up @@ -168,6 +168,7 @@
"@radix-ui/react-toggle-group": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.6",
"@radix-ui/react-visually-hidden": "^1.1.1",
"@sindresorhus/slugify": "^2.2.1",
"@swc/helpers": "^0.5.15",
"@tanstack/react-query": "^5.64.0",
"@tiptap/extension-highlight": "^2.11.2",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6335ad1

Please sign in to comment.