mirror of
https://gitbruv.vercel.app/api/git/bruv/gitbruv.git
synced 2025-12-20 23:24:09 +01:00
added
This commit is contained in:
parent
ba29d1ad56
commit
bbbf5f2a24
14 changed files with 1071 additions and 29 deletions
|
|
@ -1,9 +1,9 @@
|
|||
"use server";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { repositories, users } from "@/db/schema";
|
||||
import { repositories, users, stars } from "@/db/schema";
|
||||
import { getSession } from "@/lib/session";
|
||||
import { eq, and, desc } from "drizzle-orm";
|
||||
import { eq, and, desc, count, sql } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import git from "isomorphic-git";
|
||||
import { createR2Fs, getRepoPrefix } from "@/lib/r2-fs";
|
||||
|
|
@ -301,3 +301,273 @@ export async function getRepoFile(owner: string, repoName: string, branch: strin
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function toggleStar(repoId: string) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const existing = await db.query.stars.findFirst({
|
||||
where: and(eq(stars.userId, session.user.id), eq(stars.repositoryId, repoId)),
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await db.delete(stars).where(and(eq(stars.userId, session.user.id), eq(stars.repositoryId, repoId)));
|
||||
return { starred: false };
|
||||
} else {
|
||||
await db.insert(stars).values({
|
||||
userId: session.user.id,
|
||||
repositoryId: repoId,
|
||||
});
|
||||
return { starred: true };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStarCount(repoId: string) {
|
||||
const result = await db.select({ count: count() }).from(stars).where(eq(stars.repositoryId, repoId));
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
|
||||
export async function isStarredByUser(repoId: string) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const existing = await db.query.stars.findFirst({
|
||||
where: and(eq(stars.userId, session.user.id), eq(stars.repositoryId, repoId)),
|
||||
});
|
||||
|
||||
return !!existing;
|
||||
}
|
||||
|
||||
export async function getRepositoryWithStars(owner: string, name: string) {
|
||||
const repo = await getRepository(owner, name);
|
||||
if (!repo) return null;
|
||||
|
||||
const starCount = await getStarCount(repo.id);
|
||||
const starred = await isStarredByUser(repo.id);
|
||||
|
||||
return { ...repo, starCount, starred };
|
||||
}
|
||||
|
||||
export async function getUserRepositoriesWithStars(username: string) {
|
||||
const repos = await getUserRepositories(username);
|
||||
|
||||
const reposWithStars = await Promise.all(
|
||||
repos.map(async (repo) => {
|
||||
const starCount = await getStarCount(repo.id);
|
||||
return { ...repo, starCount };
|
||||
})
|
||||
);
|
||||
|
||||
return reposWithStars;
|
||||
}
|
||||
|
||||
export async function updateRepository(
|
||||
repoId: string,
|
||||
data: { name?: string; description?: string; visibility?: "public" | "private" }
|
||||
) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const repo = await db.query.repositories.findFirst({
|
||||
where: eq(repositories.id, repoId),
|
||||
});
|
||||
|
||||
if (!repo) {
|
||||
throw new Error("Repository not found");
|
||||
}
|
||||
|
||||
if (repo.ownerId !== session.user.id) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const oldName = repo.name;
|
||||
let newName = oldName;
|
||||
|
||||
if (data.name && data.name !== oldName) {
|
||||
newName = data.name.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
if (!/^[a-zA-Z0-9_.-]+$/.test(newName)) {
|
||||
throw new Error("Invalid repository name");
|
||||
}
|
||||
|
||||
const existing = await db.query.repositories.findFirst({
|
||||
where: and(eq(repositories.ownerId, session.user.id), eq(repositories.name, newName)),
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new Error("Repository with this name already exists");
|
||||
}
|
||||
}
|
||||
|
||||
const [updated] = await db
|
||||
.update(repositories)
|
||||
.set({
|
||||
name: newName,
|
||||
description: data.description !== undefined ? data.description || null : repo.description,
|
||||
visibility: data.visibility || repo.visibility,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(repositories.id, repoId))
|
||||
.returning();
|
||||
|
||||
const username = (session.user as { username?: string }).username;
|
||||
revalidatePath(`/${username}/${oldName}`);
|
||||
revalidatePath(`/${username}/${newName}`);
|
||||
revalidatePath(`/${username}`);
|
||||
revalidatePath("/");
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
export async function getRepoBranches(owner: string, repoName: string) {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.username, owner),
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const repoPrefix = getRepoPrefix(user.id, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
try {
|
||||
const branches = await git.listBranches({ fs, gitdir: "/" });
|
||||
return branches;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoCommits(
|
||||
owner: string,
|
||||
repoName: string,
|
||||
branch: string,
|
||||
limit: number = 30,
|
||||
skip: number = 0
|
||||
) {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.username, owner),
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return { commits: [], hasMore: false };
|
||||
}
|
||||
|
||||
const repoPrefix = getRepoPrefix(user.id, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
try {
|
||||
const commits = await git.log({
|
||||
fs,
|
||||
gitdir: "/",
|
||||
ref: branch,
|
||||
depth: skip + limit + 1,
|
||||
});
|
||||
|
||||
const paginatedCommits = commits.slice(skip, skip + limit);
|
||||
const hasMore = commits.length > skip + limit;
|
||||
|
||||
return {
|
||||
commits: paginatedCommits.map((c) => ({
|
||||
oid: c.oid,
|
||||
message: c.commit.message,
|
||||
author: {
|
||||
name: c.commit.author.name,
|
||||
email: c.commit.author.email,
|
||||
},
|
||||
timestamp: c.commit.author.timestamp * 1000,
|
||||
})),
|
||||
hasMore,
|
||||
};
|
||||
} catch {
|
||||
return { commits: [], hasMore: false };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoCommitCount(owner: string, repoName: string, branch: string) {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.username, owner),
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const repoPrefix = getRepoPrefix(user.id, `${repoName}.git`);
|
||||
const fs = createR2Fs(repoPrefix);
|
||||
|
||||
try {
|
||||
const commits = await git.log({
|
||||
fs,
|
||||
gitdir: "/",
|
||||
ref: branch,
|
||||
});
|
||||
return commits.length;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPublicRepositories(
|
||||
sortBy: "stars" | "updated" | "created" = "updated",
|
||||
limit: number = 20,
|
||||
offset: number = 0
|
||||
) {
|
||||
const allRepos = await db
|
||||
.select({
|
||||
id: repositories.id,
|
||||
name: repositories.name,
|
||||
description: repositories.description,
|
||||
visibility: repositories.visibility,
|
||||
ownerId: repositories.ownerId,
|
||||
defaultBranch: repositories.defaultBranch,
|
||||
createdAt: repositories.createdAt,
|
||||
updatedAt: repositories.updatedAt,
|
||||
ownerUsername: users.username,
|
||||
ownerName: users.name,
|
||||
ownerImage: users.image,
|
||||
starCount: sql<number>`(SELECT COUNT(*) FROM stars WHERE stars.repository_id = ${repositories.id})`.as("star_count"),
|
||||
})
|
||||
.from(repositories)
|
||||
.innerJoin(users, eq(repositories.ownerId, users.id))
|
||||
.where(eq(repositories.visibility, "public"))
|
||||
.orderBy(
|
||||
sortBy === "stars"
|
||||
? desc(sql`star_count`)
|
||||
: sortBy === "created"
|
||||
? desc(repositories.createdAt)
|
||||
: desc(repositories.updatedAt)
|
||||
)
|
||||
.limit(limit + 1)
|
||||
.offset(offset);
|
||||
|
||||
const hasMore = allRepos.length > limit;
|
||||
const repos = allRepos.slice(0, limit);
|
||||
|
||||
return {
|
||||
repos: repos.map((r) => ({
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
description: r.description,
|
||||
visibility: r.visibility as "public" | "private",
|
||||
defaultBranch: r.defaultBranch,
|
||||
createdAt: r.createdAt,
|
||||
updatedAt: r.updatedAt,
|
||||
starCount: Number(r.starCount),
|
||||
owner: {
|
||||
id: r.ownerId,
|
||||
username: r.ownerUsername,
|
||||
name: r.ownerName,
|
||||
image: r.ownerImage,
|
||||
},
|
||||
})),
|
||||
hasMore,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue