import { env } from "cloudflare:workers"; import { all, first } from "./db"; import { branchSha, listRefs } from "./git/refs"; import { withStore } from "./git/store"; import { renderMarkdown } from "./markdown"; import { REPO_SELECT } from "./repos"; import type { RepoRow } from "~/lib/types"; const DAY_MS = 24 * 60 * 60 * 1000; const GRAPH_WEEKS = 52; const WEEK_DAYS = 7; const README_NAMES = new Set(["readme.md", "readme.markdown"]); const MAX_README_BYTES = 256 * 1024; const README_CACHE_TTL_SECONDS = 60 * 60 * 24 * 7; const decoder = new TextDecoder(); export type ContributionDay = { date: string; count: number }; /** Daily repository activity counts (public repos unless `includePrivate`) for roughly the last year, oldest first, aligned to whole weeks. */ export async function contributionGraph(accountId: string, includePrivate: boolean): Promise { const now = Date.now(); const today = new Date(now); const midnight = Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); const start = new Date(midnight - (GRAPH_WEEKS * WEEK_DAYS + today.getUTCDay()) * DAY_MS); const rows = await all<{ day: string; count: number }>( `SELECT strftime('%Y-%m-%d', ac.created_at / 1000, 'unixepoch') AS day, COUNT(*) AS count FROM activity ac LEFT JOIN repos r ON r.id = ac.repo_id WHERE ac.actor_id = ? AND ac.created_at >= ? AND ac.kind != 'view' AND r.id IS NOT NULL AND (? = 1 OR r.visibility = 'public') GROUP BY day`, accountId, start.getTime(), includePrivate ? 1 : 0, ); const counts = new Map(rows.map((row) => [row.day, row.count])); const days: ContributionDay[] = []; for (let time = start.getTime(); time <= now; time += DAY_MS) { const date = new Date(time).toISOString().slice(0, 10); days.push({ date, count: counts.get(date) ?? 0 }); } return days; } /** Renders README.md from the public `/` repo, cached in KV by commit. */ export async function profileReadme(accountName: string): Promise { const repo = await first( `${REPO_SELECT} WHERE a.name = ?1 AND r.name = ?1 AND r.visibility = 'public' AND r.status = 'ready'`, accountName, ); if (!repo) return null; const refs = await listRefs(repo); const sha = branchSha(refs, repo.default_branch); if (!sha) return null; const cacheKey = `profile-readme:${repo.id}:${sha}`; const cached = await env.KV.get(cacheKey); if (cached !== null) return cached || null; const html = await withStore(repo.artifacts_name, async (store) => { const commit = await store.commit(sha); if (!commit) return ""; const entries = await store.listTree(commit.treeSha, ""); const entry = entries.find( (candidate) => candidate.type !== "tree" && README_NAMES.has(candidate.name.toLowerCase()), ); if (!entry) return ""; const bytes = await store.blob(entry.sha); if (!bytes || bytes.byteLength > MAX_README_BYTES) return ""; return renderMarkdown(decoder.decode(bytes), { owner: repo.owner_name, repo: repo.name, ref: repo.default_branch, basePath: "", }); }); await env.KV.put(cacheKey, html, { expirationTtl: README_CACHE_TTL_SECONDS }); return html || null; }