From 4877c4ffa34182885963dc53ed788c12eb2c4137 Mon Sep 17 00:00:00 2001 From: Yash Kumar Date: Wed, 2 Sep 2026 20:49:14 +0530 Subject: [PATCH] fix: address PR feedback (SVG helper & AddOrganization crash) --- .../application/icon/show-icon-settings.tsx | 11 +-------- .../organization/handle-organization.tsx | 23 +++++-------------- apps/dokploy/utils/sanitize-svg.ts | 18 +++++++++++++++ 3 files changed, 25 insertions(+), 27 deletions(-) create mode 100644 apps/dokploy/utils/sanitize-svg.ts diff --git a/apps/dokploy/components/dashboard/application/icon/show-icon-settings.tsx b/apps/dokploy/components/dashboard/application/icon/show-icon-settings.tsx index a51ab4902..4fb46a6d1 100644 --- a/apps/dokploy/components/dashboard/application/icon/show-icon-settings.tsx +++ b/apps/dokploy/components/dashboard/application/icon/show-icon-settings.tsx @@ -1,4 +1,3 @@ -import DOMPurify from "dompurify"; import { CircuitBoard, GlobeIcon, Pencil, Search, X } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; @@ -14,6 +13,7 @@ import { Dropzone } from "@/components/ui/dropzone"; import { Input } from "@/components/ui/input"; import { type BundledIcon, bundledIcons } from "@/lib/bundled-icons"; import { api } from "@/utils/api"; +import { sanitizeSvg } from "@/utils/sanitize-svg"; interface ShowIconSettingsProps { serviceId: string; @@ -89,15 +89,6 @@ export const ShowIconSettings = ({ } }; - const sanitizeSvg = (svgContent: string): string | null => { - const clean = DOMPurify.sanitize(svgContent, { - USE_PROFILES: { svg: true, svgFilters: true }, - ADD_TAGS: ["use"], - }); - if (!clean) return null; - return `data:image/svg+xml;base64,${btoa(clean)}`; - }; - const handleFileUpload = async (files: FileList | null) => { if (!files || files.length === 0) return; const file = files[0]; diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 5c7b20194..786efb61a 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -1,7 +1,7 @@ import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema"; -import DOMPurify from "dompurify"; + import { GlobeIcon, PenBoxIcon, Plus, X } from "lucide-react"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; @@ -26,6 +26,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { sanitizeSvg } from "@/utils/sanitize-svg"; const organizationSchema = z.object({ name: z.string().min(1, { @@ -43,20 +44,6 @@ interface Props { onOpenChange?: (open: boolean) => void; } -const sanitizeSvg = (svgContent: string): string | null => { - const clean = DOMPurify.sanitize(svgContent, { - USE_PROFILES: { svg: true, svgFilters: true }, - ADD_TAGS: ["use"], - }); - if (!clean) return null; - const bytes = new TextEncoder().encode(clean); - let binString = ""; - for (let i = 0; i < bytes.length; i++) { - binString += String.fromCharCode(bytes[i]!); - } - return `data:image/svg+xml;base64,${btoa(binString)}`; -}; - const resizeImage = (file: File, maxSize: number): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -106,7 +93,9 @@ export function AddOrganization({ const uploadCounter = useRef(0); const isControlled = controlledOpen !== undefined; const open = isControlled ? controlledOpen : internalOpen; - const setOpen = isControlled ? controlledOnOpenChange! : setInternalOpen; + const setOpen = isControlled + ? controlledOnOpenChange || (() => {}) + : setInternalOpen; const utils = api.useUtils(); const { data: organization } = api.organization.one.useQuery( { diff --git a/apps/dokploy/utils/sanitize-svg.ts b/apps/dokploy/utils/sanitize-svg.ts new file mode 100644 index 000000000..b3474e7bc --- /dev/null +++ b/apps/dokploy/utils/sanitize-svg.ts @@ -0,0 +1,18 @@ +import DOMPurify from "dompurify"; + +export const sanitizeSvg = (svgContent: string): string | null => { + const clean = DOMPurify.sanitize(svgContent, { + USE_PROFILES: { svg: true, svgFilters: true }, + }); + + if (!clean) return null; + + // Fix unicode base64 bug (TextEncoder byte-loop handles non-Latin1 chars) + const bytes = new TextEncoder().encode(clean); + let binString = ""; + for (let i = 0; i < bytes.length; i++) { + binString += String.fromCharCode(bytes[i]!); + } + + return `data:image/svg+xml;base64,${btoa(binString)}`; +};