229 lines
6.2 KiB
JavaScript
229 lines
6.2 KiB
JavaScript
/**
|
|
* GitHub Activity Data
|
|
* Fetches from Indiekit's endpoint-github public API
|
|
* Falls back to direct GitHub API if Indiekit is unavailable
|
|
*/
|
|
|
|
import EleventyFetch from "@11ty/eleventy-fetch";
|
|
|
|
const GITHUB_USERNAME = process.env.GITHUB_USERNAME || "";
|
|
const INDIEKIT_URL = process.env.SITE_URL || "https://example.com";
|
|
|
|
// Fallback featured repos if Indiekit API unavailable (from env: comma-separated)
|
|
const FALLBACK_FEATURED_REPOS = process.env.GITHUB_FEATURED_REPOS?.split(",").filter(Boolean) || [];
|
|
|
|
/**
|
|
* Fetch from Indiekit's public GitHub API endpoint
|
|
*/
|
|
async function fetchFromIndiekit(endpoint) {
|
|
try {
|
|
const url = `${INDIEKIT_URL}/githubapi/api/${endpoint}`;
|
|
console.log(`[githubActivity] Fetching from Indiekit: ${url}`);
|
|
const data = await EleventyFetch(url, {
|
|
duration: "15m",
|
|
type: "json",
|
|
});
|
|
console.log(`[githubActivity] Indiekit ${endpoint} success`);
|
|
return data;
|
|
} catch (error) {
|
|
console.log(
|
|
`[githubActivity] Indiekit API unavailable for ${endpoint}: ${error.message}`
|
|
);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch from GitHub API directly
|
|
*/
|
|
async function fetchFromGitHub(endpoint) {
|
|
const url = `https://api.github.com${endpoint}`;
|
|
const headers = {
|
|
Accept: "application/vnd.github.v3+json",
|
|
"User-Agent": "Eleventy-Site",
|
|
};
|
|
|
|
if (process.env.GITHUB_TOKEN) {
|
|
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
}
|
|
|
|
return await EleventyFetch(url, {
|
|
duration: "15m",
|
|
type: "json",
|
|
fetchOptions: { headers },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Truncate text with ellipsis
|
|
*/
|
|
function truncate(text, maxLength = 80) {
|
|
if (!text || text.length <= maxLength) return text || "";
|
|
return text.slice(0, maxLength - 1) + "...";
|
|
}
|
|
|
|
/**
|
|
* Extract commits from push events
|
|
*/
|
|
function extractCommits(events) {
|
|
if (!Array.isArray(events)) return [];
|
|
|
|
return events
|
|
.filter((event) => event.type === "PushEvent")
|
|
.flatMap((event) =>
|
|
(event.payload?.commits || []).map((commit) => ({
|
|
sha: commit.sha.slice(0, 7),
|
|
message: truncate(commit.message.split("\n")[0]),
|
|
url: `https://github.com/${event.repo.name}/commit/${commit.sha}`,
|
|
repo: event.repo.name,
|
|
repoUrl: `https://github.com/${event.repo.name}`,
|
|
date: event.created_at,
|
|
}))
|
|
)
|
|
.slice(0, 10);
|
|
}
|
|
|
|
/**
|
|
* Extract PRs/Issues from events
|
|
*/
|
|
function extractContributions(events) {
|
|
if (!Array.isArray(events)) return [];
|
|
|
|
return events
|
|
.filter(
|
|
(event) =>
|
|
(event.type === "PullRequestEvent" || event.type === "IssuesEvent") &&
|
|
event.payload?.action === "opened"
|
|
)
|
|
.map((event) => {
|
|
const item = event.payload.pull_request || event.payload.issue;
|
|
return {
|
|
type: event.type === "PullRequestEvent" ? "pr" : "issue",
|
|
title: truncate(item?.title),
|
|
url: item?.html_url,
|
|
repo: event.repo.name,
|
|
repoUrl: `https://github.com/${event.repo.name}`,
|
|
number: item?.number,
|
|
date: event.created_at,
|
|
};
|
|
})
|
|
.slice(0, 10);
|
|
}
|
|
|
|
/**
|
|
* Format starred repos
|
|
*/
|
|
function formatStarred(repos) {
|
|
if (!Array.isArray(repos)) return [];
|
|
|
|
return repos.map((repo) => ({
|
|
name: repo.full_name,
|
|
description: truncate(repo.description, 120),
|
|
url: repo.html_url,
|
|
stars: repo.stargazers_count,
|
|
language: repo.language,
|
|
topics: repo.topics?.slice(0, 5) || [],
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Fetch featured repos directly from GitHub (fallback)
|
|
*/
|
|
async function fetchFeaturedFromGitHub(repoList) {
|
|
const featured = [];
|
|
|
|
for (const repoFullName of repoList) {
|
|
try {
|
|
const repo = await fetchFromGitHub(`/repos/${repoFullName}`);
|
|
let commits = [];
|
|
try {
|
|
const commitsData = await fetchFromGitHub(
|
|
`/repos/${repoFullName}/commits?per_page=5`
|
|
);
|
|
commits = commitsData.map((c) => ({
|
|
sha: c.sha.slice(0, 7),
|
|
message: truncate(c.commit.message.split("\n")[0]),
|
|
url: c.html_url,
|
|
date: c.commit.author.date,
|
|
}));
|
|
} catch (e) {
|
|
console.log(`[githubActivity] Could not fetch commits for ${repoFullName}`);
|
|
}
|
|
|
|
featured.push({
|
|
fullName: repo.full_name,
|
|
name: repo.name,
|
|
description: repo.description,
|
|
url: repo.html_url,
|
|
stars: repo.stargazers_count,
|
|
forks: repo.forks_count,
|
|
language: repo.language,
|
|
isPrivate: repo.private,
|
|
commits,
|
|
});
|
|
} catch (error) {
|
|
console.log(`[githubActivity] Could not fetch ${repoFullName}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
return featured;
|
|
}
|
|
|
|
export default async function () {
|
|
try {
|
|
console.log("[githubActivity] Fetching GitHub data...");
|
|
|
|
// Try Indiekit public API first
|
|
const [indiekitStars, indiekitCommits, indiekitActivity, indiekitFeatured] =
|
|
await Promise.all([
|
|
fetchFromIndiekit("stars"),
|
|
fetchFromIndiekit("commits"),
|
|
fetchFromIndiekit("activity"),
|
|
fetchFromIndiekit("featured"),
|
|
]);
|
|
|
|
// Check if Indiekit API is available
|
|
const hasIndiekitData =
|
|
indiekitStars?.stars ||
|
|
indiekitCommits?.commits ||
|
|
indiekitFeatured?.featured;
|
|
|
|
if (hasIndiekitData) {
|
|
console.log("[githubActivity] Using Indiekit API data");
|
|
return {
|
|
stars: indiekitStars?.stars || [],
|
|
commits: indiekitCommits?.commits || [],
|
|
activity: indiekitActivity?.activity || [],
|
|
featured: indiekitFeatured?.featured || [],
|
|
source: "indiekit",
|
|
};
|
|
}
|
|
|
|
// Fallback to direct GitHub API
|
|
console.log("[githubActivity] Falling back to GitHub API");
|
|
|
|
const [events, starred, featured] = await Promise.all([
|
|
fetchFromGitHub(`/users/${GITHUB_USERNAME}/events/public?per_page=50`),
|
|
fetchFromGitHub(`/users/${GITHUB_USERNAME}/starred?per_page=20&sort=created`),
|
|
fetchFeaturedFromGitHub(FALLBACK_FEATURED_REPOS),
|
|
]);
|
|
|
|
return {
|
|
stars: formatStarred(starred || []),
|
|
commits: extractCommits(events || []),
|
|
contributions: extractContributions(events || []),
|
|
featured,
|
|
source: "github",
|
|
};
|
|
} catch (error) {
|
|
console.error("[githubActivity] Error:", error.message);
|
|
return {
|
|
stars: [],
|
|
commits: [],
|
|
contributions: [],
|
|
featured: [],
|
|
source: "error",
|
|
};
|
|
}
|
|
}
|