From c15cfc51a91e49bed6df3f3d971b9d9ba976ae9c Mon Sep 17 00:00:00 2001 From: fegger Date: Fri, 4 Sep 2026 17:38:13 +0200 Subject: [PATCH] Fall back to public Gitea stats when the API token is rejected Tokens without scopes (Gitea >= 1.19) or revoked tokens get 403 on /api/v1/user/repos; catch that and retry anonymously via /repos/search so the Git widget keeps showing public stats instead of an error. --- api/update-data.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/api/update-data.js b/api/update-data.js index b14dc7d..7fc091d 100644 --- a/api/update-data.js +++ b/api/update-data.js @@ -198,12 +198,23 @@ function normalizeCommit(raw, source) { async function getGiteaStats() { try { - const headers = GITEA_TOKEN ? { Authorization: `token ${GITEA_TOKEN}` } : {}; let repos = []; + let authHeaders = {}; + + // Try the token-authenticated path first (includes private repos). if (GITEA_TOKEN) { - repos = await fetchJson(`${GITEA_URL}/api/v1/user/repos?limit=10`, headers); - } else { - const search = await fetchJson(`${GITEA_URL}/api/v1/repos/search?limit=10`, headers); + try { + authHeaders = { Authorization: `token ${GITEA_TOKEN}` }; + repos = await fetchJson(`${GITEA_URL}/api/v1/user/repos?limit=10`, authHeaders); + } catch { + // Token missing scopes / revoked — fall back to anonymous public stats. + authHeaders = {}; + repos = []; + } + } + + if (repos.length === 0) { + const search = await fetchJson(`${GITEA_URL}/api/v1/repos/search?limit=10`, {}); repos = search.data || []; } repos = repos.slice(0, 8); @@ -213,7 +224,7 @@ async function getGiteaStats() { try { const list = await fetchJson( `${GITEA_URL}/api/v1/repos/${repo.full_name}/commits?limit=5`, - headers + authHeaders ); for (const c of list.slice(0, 5)) { commits.push(normalizeCommit({ ...c, repo: repo.full_name }, "gitea"));