From 46ef63a68e29f87ad84fef310cee1622d6e0c004 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Tue, 5 May 2026 16:18:40 +0300 Subject: [PATCH] fix: source-aware repo extraction, Gitea readme/languages endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- crates/moments-data/src/lib.rs | 38 +++++++++++++++++++++++----------- ui/src/api/client.ts | 32 ++++++++++++++++------------ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/crates/moments-data/src/lib.rs b/crates/moments-data/src/lib.rs index 25ba137..1986ad4 100644 --- a/crates/moments-data/src/lib.rs +++ b/crates/moments-data/src/lib.rs @@ -54,12 +54,19 @@ impl EventReader for PgStore { AND ($2::timestamptz IS NULL OR occurred_at < $2) AND ($3::text[] IS NULL OR source = ANY($3)) AND ($4::bool OR public = true) - AND ($6::text IS NULL OR COALESCE( - payload->'repo'->>'name', - payload->'repository'->>'full_name', - payload->>'_repo', - payload->>'product' - ) = $6) + AND ($6::text IS NULL OR (CASE source + WHEN 'github' THEN COALESCE( + payload->'repo'->>'name', + payload->'repository'->>'full_name' + ) + 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 LIMIT $5 "#, @@ -134,12 +141,19 @@ impl EventReader for PgStore { MAX(occurred_at) AS last_activity FROM ( SELECT source, occurred_at, - COALESCE( - payload->'repo'->>'name', - payload->'repository'->>'full_name', - payload->>'_repo', - payload->>'product' - ) AS repo, + CASE source + WHEN 'github' THEN COALESCE( + payload->'repo'->>'name', + payload->'repository'->>'full_name' + ) + 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 WHEN 'github' THEN 'github.com' WHEN 'gitea' THEN COALESCE(payload->>'_host', 'git.lair.cafe') diff --git a/ui/src/api/client.ts b/ui/src/api/client.ts index ce4283a..9da616a 100644 --- a/ui/src/api/client.ts +++ b/ui/src/api/client.ts @@ -102,20 +102,26 @@ export async function fetchProjects(): Promise { 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 { - const baseUrl = source === 'github' - ? `https://api.github.com/repos/${repo}/readme` - : source === 'gitea' - ? `https://${host}/api/v1/repos/${repo}/readme` - : null; - if (!baseUrl) return null; - - const resp = await fetch(baseUrl, { - headers: { 'Accept': 'application/vnd.github.raw+json' }, - }); - if (!resp.ok) return null; - return resp.text(); + if (source === 'github') { + const resp = await fetch(`https://api.github.com/repos/${repo}/readme`, { + headers: { 'Accept': 'application/vnd.github.raw+json' }, + }); + if (!resp.ok) return null; + return resp.text(); + } + if (source === 'gitea') { + // 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; + 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. */