mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-07 03:56:46 +03:00
143 lines
4.5 KiB
TypeScript
143 lines
4.5 KiB
TypeScript
import type { APIRoute } from "astro";
|
|
import { db } from "@/lib/db";
|
|
import { organizations, repositories, configs } from "@/lib/db";
|
|
import { eq, sql, and, count } from "drizzle-orm";
|
|
import {
|
|
membershipRoleEnum,
|
|
type OrganizationsApiResponse,
|
|
} from "@/types/organizations";
|
|
import type { Organization } from "@/lib/db/schema";
|
|
import { repoStatusEnum } from "@/types/Repository";
|
|
import { jsonResponse, createSecureErrorResponse } from "@/lib/utils";
|
|
|
|
export const GET: APIRoute = async ({ request }) => {
|
|
const url = new URL(request.url);
|
|
const userId = url.searchParams.get("userId");
|
|
|
|
if (!userId) {
|
|
return jsonResponse({
|
|
data: {
|
|
success: false,
|
|
error: "Missing userId",
|
|
},
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Fetch the user's active configuration to respect filtering settings
|
|
const [config] = await db
|
|
.select()
|
|
.from(configs)
|
|
.where(and(eq(configs.userId, userId), eq(configs.isActive, true)));
|
|
|
|
if (!config) {
|
|
return jsonResponse({
|
|
data: {
|
|
success: false,
|
|
error: "No active configuration found for this user",
|
|
},
|
|
status: 404,
|
|
});
|
|
}
|
|
|
|
const githubConfig = config.githubConfig as {
|
|
mirrorStarred: boolean;
|
|
skipForks: boolean;
|
|
privateRepositories: boolean;
|
|
};
|
|
|
|
const rawOrgs = await db
|
|
.select()
|
|
.from(organizations)
|
|
.where(eq(organizations.userId, userId))
|
|
.orderBy(sql`name COLLATE NOCASE`);
|
|
|
|
// Calculate repository breakdowns for each organization
|
|
const orgsWithBreakdown = await Promise.all(
|
|
rawOrgs.map(async (org) => {
|
|
// Build base conditions for this organization (without private/fork filters)
|
|
const baseConditions = [
|
|
eq(repositories.userId, userId),
|
|
eq(repositories.organization, org.name)
|
|
];
|
|
|
|
if (!githubConfig.mirrorStarred) {
|
|
baseConditions.push(eq(repositories.isStarred, false));
|
|
}
|
|
|
|
// Get total count with all user config filters applied
|
|
const totalConditions = [...baseConditions];
|
|
if (githubConfig.skipForks) {
|
|
totalConditions.push(eq(repositories.isForked, false));
|
|
}
|
|
if (!githubConfig.privateRepositories) {
|
|
totalConditions.push(eq(repositories.isPrivate, false));
|
|
}
|
|
|
|
const [totalCount] = await db
|
|
.select({ count: count() })
|
|
.from(repositories)
|
|
.where(and(...totalConditions));
|
|
|
|
// Get public count
|
|
const publicConditions = [...baseConditions, eq(repositories.isPrivate, false)];
|
|
if (githubConfig.skipForks) {
|
|
publicConditions.push(eq(repositories.isForked, false));
|
|
}
|
|
|
|
const [publicCount] = await db
|
|
.select({ count: count() })
|
|
.from(repositories)
|
|
.where(and(...publicConditions));
|
|
|
|
// Get private count (only if private repos are enabled in config)
|
|
const [privateCount] = githubConfig.privateRepositories ? await db
|
|
.select({ count: count() })
|
|
.from(repositories)
|
|
.where(
|
|
and(
|
|
...baseConditions,
|
|
eq(repositories.isPrivate, true),
|
|
...(githubConfig.skipForks ? [eq(repositories.isForked, false)] : [])
|
|
)
|
|
) : [{ count: 0 }];
|
|
|
|
// Get fork count (only if forks are enabled in config)
|
|
const [forkCount] = !githubConfig.skipForks ? await db
|
|
.select({ count: count() })
|
|
.from(repositories)
|
|
.where(
|
|
and(
|
|
...baseConditions,
|
|
eq(repositories.isForked, true),
|
|
...(!githubConfig.privateRepositories ? [eq(repositories.isPrivate, false)] : [])
|
|
)
|
|
) : [{ count: 0 }];
|
|
|
|
return {
|
|
...org,
|
|
status: repoStatusEnum.parse(org.status),
|
|
membershipRole: membershipRoleEnum.parse(org.membershipRole),
|
|
lastMirrored: org.lastMirrored ?? undefined,
|
|
errorMessage: org.errorMessage ?? undefined,
|
|
repositoryCount: totalCount.count,
|
|
publicRepositoryCount: publicCount.count,
|
|
privateRepositoryCount: privateCount.count,
|
|
forkRepositoryCount: forkCount.count,
|
|
};
|
|
})
|
|
);
|
|
|
|
const resPayload: OrganizationsApiResponse = {
|
|
success: true,
|
|
message: "Organizations fetched successfully",
|
|
organizations: orgsWithBreakdown,
|
|
};
|
|
|
|
return jsonResponse({ data: resPayload, status: 200 });
|
|
} catch (error) {
|
|
return createSecureErrorResponse(error, "organizations fetch", 500);
|
|
}
|
|
};
|