scrape / lib /github.ts
kjbytes's picture
push code
ef4c36f
Raw
History Blame Contribute Delete
2 kB
import type { GitHubRepoData } from "@/types";
export function parseGitHubUrl(url: string): { owner: string; repo: string } | null {
const trimmed = url.trim();
const patterns = [
/github\.com\/([^/\s]+)\/([^/\s#?]+)/i,
/^([^/\s]+)\/([^/\s]+)$/,
];
for (const pattern of patterns) {
const match = trimmed.match(pattern);
if (match) {
return {
owner: match[1].replace(/\.git$/, ""),
repo: match[2].replace(/\.git$/, ""),
};
}
}
return null;
}
async function githubFetch(path: string): Promise<Response> {
const headers: Record<string, string> = {
Accept: "application/vnd.github+json",
"User-Agent": "caption-studio",
};
// ponytail: a placeholder token 401s every request — treat it as absent
const token = process.env.GITHUB_TOKEN?.trim();
if (token && token.length > 20) {
headers.Authorization = `Bearer ${token}`;
}
return fetch(`https://api.github.com${path}`, { headers });
}
export async function fetchRepoData(url: string): Promise<GitHubRepoData> {
const parsed = parseGitHubUrl(url);
if (!parsed) throw new Error("Invalid GitHub repo URL");
const { owner, repo } = parsed;
const repoRes = await githubFetch(`/repos/${owner}/${repo}`);
if (!repoRes.ok) {
throw new Error(
`GitHub repo fetch failed (${repoRes.status}): ${owner}/${repo}`,
);
}
const repoData = await repoRes.json();
let readme = "";
const readmeRes = await githubFetch(`/repos/${owner}/${repo}/readme`);
if (readmeRes.ok) {
readme = await readmeRes.text();
if (readme.length > 4000) readme = readme.slice(0, 4000) + "...";
}
return {
owner,
repo,
fullName: repoData.full_name as string,
htmlUrl: repoData.html_url as string,
description: (repoData.description as string) || "",
stars: (repoData.stargazers_count as number) || 0,
language: (repoData.language as string) || "Unknown",
topics: (repoData.topics as string[]) || [],
readme,
};
}