import { db } from "~/server/db"; import { publicProcedure, router } from "~/server/trpc"; type ReadmeRequest = { url: string; }; function getReadmeRequest(sourceLink: string): ReadmeRequest | null { let url: URL; try { url = new URL(sourceLink); } catch { return null; } const pathParts = url.pathname.split("/").filter(Boolean); const [owner, repo] = pathParts; if (!owner || !repo) { return null; } const repoName = repo.replace(/\.git$/, ""); if (url.hostname === "github.com" || url.hostname === "www.github.com") { return { url: `https://raw.githubusercontent.com/${owner}/${repoName}/main/README.md`, }; } if (url.hostname.includes("gitea.")) { return { url: `${url.origin}/${owner}/${repoName}/raw/branch/main/README.md`, }; } return null; } async function fetchReadme(sourceLink: string) { const readmeRequest = getReadmeRequest(sourceLink); if (!readmeRequest) { return null; } const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); try { const response = await fetch(readmeRequest.url, { headers: { Accept: "text/plain", }, signal: controller.signal, }); if (!response.ok) { return null; } return await response.text(); } catch { return null; } finally { clearTimeout(timeout); } } export const projectRouter = router({ listWithStack: publicProcedure.query(async () => { const projects = await db.query.project.findMany({ orderBy: (project, { asc }) => [ asc(project.orderPos), asc(project.title), asc(project.id), ], with: { techStack: true, }, }); return Promise.all( projects.map(async (project) => { if (project.description?.length !== 0 || !project.sourceLink) { return project; } return { ...project, description: await fetchReadme(project.sourceLink), }; }), ); }), });