Files
gregorlohaus.com/src/server/routers/project.ts

98 lines
2.0 KiB
TypeScript

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),
};
}),
);
}),
});