+ <>
{entires.data == undefined ?
:
@@ -40,6 +36,6 @@ export default function CvPage() {
})}
>
}
-
+ >
)
}
diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx
index d50ec93..cc40a7d 100644
--- a/src/app/admin/layout.tsx
+++ b/src/app/admin/layout.tsx
@@ -1,14 +1,18 @@
+import { SidebarProvider } from "~/components/ui/sidebar";
import AdminSideBar from "./_components/AdminSideBar";
+import { ScrollArea } from "~/components/ui/scroll-area";
export const dynamic = 'force-dynamic';
export default function Admin({children}: Readonly<{children: React.ReactNode}>) {
return (
<>
+
+ <>
{
projects.data == undefined ?
<>> :
@@ -55,6 +55,6 @@ export default function ProjectList() {
>
}
-
+ >
)
}
diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx
index 80616d1..eb5309d 100644
--- a/src/app/projects/page.tsx
+++ b/src/app/projects/page.tsx
@@ -11,6 +11,7 @@ import AnimateTextIn from "../_components/Animated/AnimateIn";
import { useTimeLine } from "../_providers/GsapProvicer";
import AnimatePopUp from "../_components/Animated/AnimatePopUp";
import { Button } from "~/components/ui/button";
+import remarkGfm from "remark-gfm"
export default function ProjectsPage() {
const { data: projects, isLoading } = trpc.projectv2.listWithStack.useQuery();
@@ -61,8 +62,9 @@ export default function ProjectsPage() {
{project.description && (
-
- {project.description}
+
+ {project.description}
+
)}
diff --git a/src/server/routers/project.ts b/src/server/routers/project.ts
index 8f3604e..3bfaf15 100644
--- a/src/server/routers/project.ts
+++ b/src/server/routers/project.ts
@@ -1,12 +1,92 @@
import { publicProcedure, router } from "~/server/trpc";
import { db } from "~/server/db";
+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 () => {
- return db.query.project.findMany({
+ const projects = await db.query.project.findMany({
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),
+ };
+ }),
+ );
}),
});