diff --git a/src/routes/image-proxy/index.ts b/src/routes/image-proxy/index.ts new file mode 100644 index 0000000..58b586b --- /dev/null +++ b/src/routes/image-proxy/index.ts @@ -0,0 +1,34 @@ +import type { RequestHandler } from "@builder.io/qwik-city"; + +export const onGet: RequestHandler = async ({ send, headers, url }) => { + const imageUrl = url.searchParams.get("url"); + + if (!imageUrl || !/^https?:\/\//i.test(imageUrl)) { + send(400, "Invalid or missing URL parameter"); + return; + } + + try { + const response = await fetch(imageUrl, { + signal: AbortSignal.timeout(3000), + }); + + if (!response.ok) { + throw new Error("Failed to fetch image"); + } + + const contentType = response.headers.get("Content-Type"); + if (!contentType || !contentType.startsWith("image/")) { + throw new Error("Invalid content type"); + } + const cacheControl = response.headers.get("Cache-Control"); + + headers.set("Content-Type", contentType); + headers.set("Cache-Control", cacheControl || "public, max-age=31536000"); + + const buffer = Buffer.from(await response.arrayBuffer()); + send(200, buffer); + } catch (error) { + send(400, "Error fetching image"); + } +}; diff --git a/src/routes/projects/[projectId]/index.tsx b/src/routes/projects/[projectId]/index.tsx index cae7020..8406300 100644 --- a/src/routes/projects/[projectId]/index.tsx +++ b/src/routes/projects/[projectId]/index.tsx @@ -9,6 +9,35 @@ import Socials from "~/components/blocks/Socials"; import { AppwriteException } from "appwrite"; import { useUpvotes } from "~/components/hooks/useUpvotes"; +const escape = (unsafe: string) => { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +}; + +const renderer: Partial< + Omit, "constructor" | "options"> +> = { + image(href: string, title: string, text: string) { + if (!href || !/^https?:\/\//i.test(href)) { + return text || ""; + } + + const searchParams = new URLSearchParams(); + searchParams.set("url", href); + + const escapedText = text ? `alt="${escape(text)}"` : ""; + const escapedTitle = title ? `title="${escape(title)}"` : ""; + + return ``; + }, +}; + +marked.use({ renderer }); + export const useProjectData = routeLoader$(async ({ params, status }) => { try { return {