fix: source-aware repo extraction, Gitea readme/languages endpoints

Use CASE/source instead of COALESCE for repo name extraction — Gitea's
repo.name is the short name while full_name includes the owner prefix.
Fix Gitea README fetch to use /contents/README.md with base64 decoding
instead of the nonexistent /readme endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 16:18:40 +03:00
parent ba216580ea
commit 46ef63a68e
2 changed files with 45 additions and 25 deletions

View File

@@ -54,12 +54,19 @@ impl EventReader for PgStore {
AND ($2::timestamptz IS NULL OR occurred_at < $2) AND ($2::timestamptz IS NULL OR occurred_at < $2)
AND ($3::text[] IS NULL OR source = ANY($3)) AND ($3::text[] IS NULL OR source = ANY($3))
AND ($4::bool OR public = true) AND ($4::bool OR public = true)
AND ($6::text IS NULL OR COALESCE( AND ($6::text IS NULL OR (CASE source
payload->'repo'->>'name', WHEN 'github' THEN COALESCE(
payload->'repository'->>'full_name', payload->'repo'->>'name',
payload->>'_repo', payload->'repository'->>'full_name'
payload->>'product' )
) = $6) WHEN 'gitea' THEN COALESCE(
payload->'repo'->>'full_name',
payload->'repo'->>'name'
)
WHEN 'hg' THEN payload->>'_repo'
WHEN 'bugzilla' THEN payload->>'product'
ELSE NULL
END) = $6)
ORDER BY occurred_at DESC ORDER BY occurred_at DESC
LIMIT $5 LIMIT $5
"#, "#,
@@ -134,12 +141,19 @@ impl EventReader for PgStore {
MAX(occurred_at) AS last_activity MAX(occurred_at) AS last_activity
FROM ( FROM (
SELECT source, occurred_at, SELECT source, occurred_at,
COALESCE( CASE source
payload->'repo'->>'name', WHEN 'github' THEN COALESCE(
payload->'repository'->>'full_name', payload->'repo'->>'name',
payload->>'_repo', payload->'repository'->>'full_name'
payload->>'product' )
) AS repo, WHEN 'gitea' THEN COALESCE(
payload->'repo'->>'full_name',
payload->'repo'->>'name'
)
WHEN 'hg' THEN payload->>'_repo'
WHEN 'bugzilla' THEN payload->>'product'
ELSE NULL
END AS repo,
CASE source CASE source
WHEN 'github' THEN 'github.com' WHEN 'github' THEN 'github.com'
WHEN 'gitea' THEN COALESCE(payload->>'_host', 'git.lair.cafe') WHEN 'gitea' THEN COALESCE(payload->>'_host', 'git.lair.cafe')

View File

@@ -102,20 +102,26 @@ export async function fetchProjects(): Promise<ProjectSummary[]> {
return resp.json(); return resp.json();
} }
/** Fetch repo README as rendered HTML or raw markdown. */ /** Fetch repo README as raw markdown. */
export async function fetchReadme(source: Source, host: string, repo: string): Promise<string | null> { export async function fetchReadme(source: Source, host: string, repo: string): Promise<string | null> {
const baseUrl = source === 'github' if (source === 'github') {
? `https://api.github.com/repos/${repo}/readme` const resp = await fetch(`https://api.github.com/repos/${repo}/readme`, {
: source === 'gitea' headers: { 'Accept': 'application/vnd.github.raw+json' },
? `https://${host}/api/v1/repos/${repo}/readme` });
: null; if (!resp.ok) return null;
if (!baseUrl) return null; return resp.text();
}
const resp = await fetch(baseUrl, { if (source === 'gitea') {
headers: { 'Accept': 'application/vnd.github.raw+json' }, // Gitea returns JSON with base64-encoded content
}); const resp = await fetch(`https://${host}/api/v1/repos/${repo}/contents/README.md`);
if (!resp.ok) return null; if (!resp.ok) return null;
return resp.text(); const data = await resp.json();
if (data.encoding === 'base64' && data.content) {
return atob(data.content);
}
return data.content ?? null;
}
return null;
} }
/** Fetch repo languages as { language: bytes } map. */ /** Fetch repo languages as { language: bytes } map. */