mirror of
https://gitbruv.vercel.app/api/git/bruv/gitbruv.git
synced 2025-12-20 23:24:09 +01:00
suspense
This commit is contained in:
parent
fa297f7b7a
commit
dd107b53de
2 changed files with 134 additions and 37 deletions
|
|
@ -560,21 +560,19 @@ export type FileEntry = {
|
|||
lastCommit: { message: string; timestamp: number } | null;
|
||||
};
|
||||
|
||||
async function fetchGitData(userId: string, repoName: string, defaultBranch: string) {
|
||||
async function fetchFileTree(userId: string, repoName: string, defaultBranch: string) {
|
||||
const repoPrefix = getRepoPrefix(userId, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
let files: FileEntry[] = [];
|
||||
let isEmpty = true;
|
||||
let readmeContent: string | null = null;
|
||||
let branches: string[] = [];
|
||||
let commitCount = 0;
|
||||
let readmeOid: string | null = null;
|
||||
|
||||
try {
|
||||
const [branchList, commits] = await Promise.all([git.listBranches({ fs, gitdir: "/" }), git.log({ fs, gitdir: "/", ref: defaultBranch })]);
|
||||
const [branchList, commits] = await Promise.all([git.listBranches({ fs, gitdir: "/" }), git.log({ fs, gitdir: "/", ref: defaultBranch, depth: 1 })]);
|
||||
|
||||
branches = branchList;
|
||||
commitCount = commits.length;
|
||||
|
||||
if (commits.length > 0) {
|
||||
isEmpty = false;
|
||||
|
|
@ -614,22 +612,57 @@ async function fetchGitData(userId: string, repoName: string, defaultBranch: str
|
|||
|
||||
const readmeEntry = tree.find((e) => e.path.toLowerCase() === "readme.md" && e.type === "blob");
|
||||
if (readmeEntry) {
|
||||
const { blob } = await git.readBlob({ fs, gitdir: "/", oid: readmeEntry.oid });
|
||||
readmeContent = new TextDecoder("utf-8").decode(blob);
|
||||
readmeOid = readmeEntry.oid;
|
||||
}
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const error = err as { code?: string };
|
||||
if (error.code !== "NotFoundError") {
|
||||
console.error("fetchGitData error:", err);
|
||||
console.error("fetchFileTree error:", err);
|
||||
}
|
||||
}
|
||||
|
||||
return { files, isEmpty, readmeContent, branches, commitCount };
|
||||
return { files, isEmpty, branches, readmeOid };
|
||||
}
|
||||
|
||||
const getCachedGitData = (owner: string, repoName: string, userId: string, defaultBranch: string) =>
|
||||
unstable_cache(() => fetchGitData(userId, repoName, defaultBranch), [`git-data`, owner, repoName], {
|
||||
async function fetchReadme(userId: string, repoName: string, readmeOid: string) {
|
||||
const repoPrefix = getRepoPrefix(userId, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
try {
|
||||
const { blob } = await git.readBlob({ fs, gitdir: "/", oid: readmeOid });
|
||||
return new TextDecoder("utf-8").decode(blob);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCommitCount(userId: string, repoName: string, defaultBranch: string) {
|
||||
const repoPrefix = getRepoPrefix(userId, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
try {
|
||||
const commits = await git.log({ fs, gitdir: "/", ref: defaultBranch });
|
||||
return commits.length;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const getCachedFileTree = (owner: string, repoName: string, userId: string, defaultBranch: string) =>
|
||||
unstable_cache(() => fetchFileTree(userId, repoName, defaultBranch), [`file-tree`, owner, repoName], {
|
||||
tags: [`repo:${owner}/${repoName}`],
|
||||
revalidate: 3600,
|
||||
})();
|
||||
|
||||
const getCachedReadme = (owner: string, repoName: string, userId: string, readmeOid: string) =>
|
||||
unstable_cache(() => fetchReadme(userId, repoName, readmeOid), [`readme`, owner, repoName, readmeOid], {
|
||||
tags: [`repo:${owner}/${repoName}`],
|
||||
revalidate: 3600,
|
||||
})();
|
||||
|
||||
const getCachedCommitCount = (owner: string, repoName: string, userId: string, defaultBranch: string) =>
|
||||
unstable_cache(() => fetchCommitCount(userId, repoName, defaultBranch), [`commit-count`, owner, repoName], {
|
||||
tags: [`repo:${owner}/${repoName}`],
|
||||
revalidate: 3600,
|
||||
})();
|
||||
|
|
@ -649,10 +682,10 @@ export async function getRepoPageData(owner: string, repoName: string) {
|
|||
return null;
|
||||
}
|
||||
|
||||
const [starCountResult, starredResult, gitData] = await Promise.all([
|
||||
const [starCountResult, starredResult, fileTreeData] = await Promise.all([
|
||||
db.select({ count: count() }).from(stars).where(eq(stars.repositoryId, repo.id)),
|
||||
session?.user ? db.query.stars.findFirst({ where: and(eq(stars.userId, session.user.id), eq(stars.repositoryId, repo.id)) }) : Promise.resolve(null),
|
||||
getCachedGitData(owner, repoName, user.id, repo.defaultBranch),
|
||||
getCachedFileTree(owner, repoName, user.id, repo.defaultBranch),
|
||||
]);
|
||||
|
||||
const starCount = starCountResult[0]?.count ?? 0;
|
||||
|
|
@ -666,7 +699,25 @@ export async function getRepoPageData(owner: string, repoName: string) {
|
|||
starCount,
|
||||
starred,
|
||||
},
|
||||
...gitData,
|
||||
...fileTreeData,
|
||||
isOwner,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getRepoReadme(owner: string, repoName: string, readmeOid: string) {
|
||||
const user = await db.query.users.findFirst({ where: eq(users.username, owner) });
|
||||
if (!user) return null;
|
||||
return getCachedReadme(owner, repoName, user.id, readmeOid);
|
||||
}
|
||||
|
||||
export async function getRepoCommitCountCached(owner: string, repoName: string) {
|
||||
const user = await db.query.users.findFirst({ where: eq(users.username, owner) });
|
||||
if (!user) return 0;
|
||||
|
||||
const repo = await db.query.repositories.findFirst({
|
||||
where: and(eq(repositories.ownerId, user.id), eq(repositories.name, repoName)),
|
||||
});
|
||||
if (!repo) return 0;
|
||||
|
||||
return getCachedCommitCount(owner, repoName, user.id, repo.defaultBranch);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Suspense } from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getRepoPageData } from "@/actions/repositories";
|
||||
import { getRepoPageData, getRepoReadme, getRepoCommitCountCached } from "@/actions/repositories";
|
||||
import { FileTree } from "@/components/file-tree";
|
||||
import { CodeViewer } from "@/components/code-viewer";
|
||||
import { CloneUrl } from "@/components/clone-url";
|
||||
|
|
@ -7,10 +8,68 @@ import { StarButton } from "@/components/star-button";
|
|||
import { BranchSelector } from "@/components/branch-selector";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Lock, Globe, FileCode, Settings, GitCommit, GitBranch } from "lucide-react";
|
||||
import { Lock, Globe, FileCode, Settings, GitCommit, GitBranch, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getPublicServerUrl } from "@/lib/utils";
|
||||
|
||||
async function CommitCount({ username, repoName, branch }: { username: string; repoName: string; branch: string }) {
|
||||
const commitCount = await getRepoCommitCountCached(username, repoName);
|
||||
|
||||
if (commitCount === 0) return null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`/${username}/${repoName}/commits/${branch}`}
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<GitCommit className="h-4 w-4" />
|
||||
<span className="font-medium">{commitCount}</span>
|
||||
<span className="hidden sm:inline">commits</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
async function ReadmeSection({ username, repoName, readmeOid }: { username: string; repoName: string; readmeOid: string }) {
|
||||
const content = await getRepoReadme(username, repoName, readmeOid);
|
||||
|
||||
if (!content) return null;
|
||||
|
||||
return (
|
||||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-card border-b border-border">
|
||||
<FileCode className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">README.md</span>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<CodeViewer content={content} language="markdown" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ReadmeSkeleton() {
|
||||
return (
|
||||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-card border-b border-border">
|
||||
<FileCode className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">README.md</span>
|
||||
</div>
|
||||
<div className="p-6 flex items-center justify-center min-h-[200px]">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CommitCountSkeleton() {
|
||||
return (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<GitCommit className="h-4 w-4" />
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function RepoPage({ params }: { params: Promise<{ username: string; repo: string }> }) {
|
||||
const { username, repo: repoName } = await params;
|
||||
|
||||
|
|
@ -20,7 +79,7 @@ export default async function RepoPage({ params }: { params: Promise<{ username:
|
|||
notFound();
|
||||
}
|
||||
|
||||
const { repo, files, isEmpty, readmeContent, branches, commitCount, isOwner } = data;
|
||||
const { repo, files, isEmpty, branches, readmeOid, isOwner } = data;
|
||||
|
||||
return (
|
||||
<div className="container px-4 py-6">
|
||||
|
|
@ -67,16 +126,9 @@ export default async function RepoPage({ params }: { params: Promise<{ username:
|
|||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<div className="flex items-center justify-between gap-4 px-4 py-3 bg-card border-b border-border">
|
||||
<BranchSelector branches={branches} currentBranch={repo.defaultBranch} username={username} repoName={repo.name} />
|
||||
{commitCount > 0 && (
|
||||
<Link
|
||||
href={`/${username}/${repo.name}/commits/${repo.defaultBranch}`}
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<GitCommit className="h-4 w-4" />
|
||||
<span className="font-medium">{commitCount}</span>
|
||||
<span className="hidden sm:inline">commits</span>
|
||||
</Link>
|
||||
)}
|
||||
<Suspense fallback={<CommitCountSkeleton />}>
|
||||
<CommitCount username={username} repoName={repoName} branch={repo.defaultBranch} />
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
{isEmpty ? (
|
||||
|
|
@ -86,16 +138,10 @@ export default async function RepoPage({ params }: { params: Promise<{ username:
|
|||
)}
|
||||
</div>
|
||||
|
||||
{readmeContent && (
|
||||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-card border-b border-border">
|
||||
<FileCode className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">README.md</span>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<CodeViewer content={readmeContent} language="markdown" />
|
||||
</div>
|
||||
</div>
|
||||
{readmeOid && (
|
||||
<Suspense fallback={<ReadmeSkeleton />}>
|
||||
<ReadmeSection username={username} repoName={repoName} readmeOid={readmeOid} />
|
||||
</Suspense>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue