1
0
Fork 0
mirror of https://gitbruv.vercel.app/api/git/bruv/gitbruv.git synced 2025-12-20 23:24:09 +01:00
This commit is contained in:
Ahmet Kilinc 2025-12-20 12:21:33 +00:00
parent 468781e311
commit 1055bd7f07
8 changed files with 416 additions and 85 deletions

View file

@ -0,0 +1,135 @@
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/db";
import { users, repositories } from "@/db/schema";
import { eq, and } from "drizzle-orm";
import { getSession } from "@/lib/session";
import git from "isomorphic-git";
import { createR2Fs, getRepoPrefix } from "@/lib/r2-fs";
const CHUNK_SIZE = 64 * 1024;
export async function GET(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
const { path } = await params;
if (path.length < 4) {
return NextResponse.json({ error: "Invalid path" }, { status: 400 });
}
const [username, repoName, branch, ...fileParts] = path;
const filePath = fileParts.join("/");
const user = await db.query.users.findFirst({
where: eq(users.username, username),
});
if (!user) {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
const repo = await db.query.repositories.findFirst({
where: and(eq(repositories.ownerId, user.id), eq(repositories.name, repoName)),
});
if (!repo) {
return NextResponse.json({ error: "Repository not found" }, { status: 404 });
}
if (repo.visibility === "private") {
const session = await getSession();
if (!session?.user || session.user.id !== repo.ownerId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
}
}
const repoPrefix = getRepoPrefix(user.id, `${repoName}.git`);
const fs = createR2Fs(repoPrefix);
try {
const commits = await git.log({
fs,
gitdir: "/",
ref: branch,
depth: 1,
});
if (commits.length === 0) {
return NextResponse.json({ error: "Branch not found" }, { status: 404 });
}
const commitOid = commits[0].oid;
const parts = filePath.split("/").filter(Boolean);
const fileName = parts.pop()!;
let currentTree = (await git.readTree({ fs, gitdir: "/", oid: commitOid })).tree;
for (const part of parts) {
const entry = currentTree.find((e) => e.path === part && e.type === "tree");
if (!entry) {
return NextResponse.json({ error: "Path not found" }, { status: 404 });
}
currentTree = (await git.readTree({ fs, gitdir: "/", oid: entry.oid })).tree;
}
const fileEntry = currentTree.find((e) => e.path === fileName && e.type === "blob");
if (!fileEntry) {
return NextResponse.json({ error: "File not found" }, { status: 404 });
}
const { blob } = await git.readBlob({
fs,
gitdir: "/",
oid: fileEntry.oid,
});
const rangeHeader = request.headers.get("range");
if (rangeHeader) {
const match = rangeHeader.match(/bytes=(\d+)-(\d*)/);
if (match) {
const start = parseInt(match[1], 10);
const end = match[2] ? parseInt(match[2], 10) : Math.min(start + CHUNK_SIZE - 1, blob.length - 1);
const chunk = blob.slice(start, end + 1);
return new NextResponse(chunk, {
status: 206,
headers: {
"Content-Range": `bytes ${start}-${end}/${blob.length}`,
"Accept-Ranges": "bytes",
"Content-Length": chunk.length.toString(),
"Content-Type": "application/octet-stream",
},
});
}
}
const stream = new ReadableStream({
start(controller) {
let offset = 0;
const push = () => {
if (offset >= blob.length) {
controller.close();
return;
}
const chunk = blob.slice(offset, offset + CHUNK_SIZE);
controller.enqueue(chunk);
offset += CHUNK_SIZE;
setTimeout(push, 0);
};
push();
},
});
return new NextResponse(stream, {
headers: {
"Content-Type": "application/octet-stream",
"Content-Length": blob.length.toString(),
"Accept-Ranges": "bytes",
"X-Total-Size": blob.length.toString(),
},
});
} catch (err) {
console.error("File streaming error:", err);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}