'use strict'; const { downloadBodyToFile, fetchOrThrow } = require('./http-download'); function normalizeGiteaBaseUrl(raw) { let b = String(raw || '').trim().replace(/\/+$/, ''); if (!b) return ''; if (!/^https?:\/\//i.test(b)) b = `https://${b}`; return b; } function giteaApiBase(cfg) { const base = normalizeGiteaBaseUrl(cfg.gitea.base_url); return `${base}/api/v1`; } function giteaToken(cfg) { const name = cfg.gitea && cfg.gitea.token_env; if (name && process.env[name]) return String(process.env[name]).trim(); return String(process.env.GITEA_TOKEN || '').trim(); } function giteaHeaders(token, json = false) { const h = { 'User-Agent': 'Fractured-Launcher-Electron' }; if (json) h.Accept = 'application/json'; if (token) h.Authorization = `token ${token}`; return h; } function useGiteaReleases(cfg) { const g = cfg.gitea; if (!g) return false; return !!(String(g.base_url || '').trim() && String(g.owner || '').trim() && String(g.repo || '').trim()); } async function fetchGiteaReleaseRecord(cfg) { const api = giteaApiBase(cfg); const { owner, repo } = cfg.gitea; const tag = (cfg.gitea.release_tag || 'latest').trim() || 'latest'; const token = giteaToken(cfg); let listUrl; if (tag.toLowerCase() === 'latest') { listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/latest`; } else { listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`; } const res = await fetchOrThrow(listUrl, { headers: giteaHeaders(token, true) }); const text = await res.text(); if (!res.ok) { let hint = ''; if (res.status === 404) hint = ' (wrong tag / no release / check base_url owner repo)'; if (res.status === 401 || res.status === 403) hint = ' (set GITEA_TOKEN or gitea.token_env)'; throw new Error(`Gitea release ${res.status}${hint}: ${text.slice(0, 600)}`); } return JSON.parse(text); } async function listGiteaReleaseAttachmentNames(cfg) { const rel = await fetchGiteaReleaseRecord(cfg); const list = rel.attachments || rel.assets || []; return list.map((x) => x.name).filter(Boolean); } async function downloadGiteaReleaseAsset(cfg, assetName, destPath) { const token = giteaToken(cfg); const rel = await fetchGiteaReleaseRecord(cfg); const list = rel.attachments || rel.assets || []; let downloadUrl = ''; for (const a of list) { if (a.name !== assetName) continue; downloadUrl = a.browser_download_url || a.download_url || ''; break; } if (!downloadUrl) { const names = list.map((x) => x.name).filter(Boolean); throw new Error(`Gitea release asset "${assetName}" not found; attachments: ${names.join(', ') || '(none)'}`); } const h = { Accept: 'application/octet-stream' }; if (token) h.Authorization = `token ${token}`; const dl = await fetchOrThrow(downloadUrl, { headers: h, redirect: 'follow' }); await downloadBodyToFile(dl, destPath); } /** * Base URL for electron-updater generic provider (expects latest.yml under this path). * Matches Gitea’s pattern: …/owner/repo/releases/download/{tag}/latest.yml */ async function getGiteaUpdaterFeedBase(cfg) { if (!useGiteaReleases(cfg)) return null; const api = giteaApiBase(cfg); const { owner, repo } = cfg.gitea; const tag = (cfg.gitea.release_tag || 'latest').trim() || 'latest'; const token = giteaToken(cfg); let listUrl; if (tag.toLowerCase() === 'latest') { listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/latest`; } else { listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`; } const res = await fetchOrThrow(listUrl, { headers: giteaHeaders(token, true) }); if (!res.ok) return null; const rel = await res.json(); const tagName = rel.tag_name; if (!tagName || typeof tagName !== 'string') return null; const root = normalizeGiteaBaseUrl(cfg.gitea.base_url); const url = `${root}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/download/${encodeURIComponent(tagName)}/`; return { url, token }; } module.exports = { downloadGiteaReleaseAsset, fetchGiteaReleaseRecord, listGiteaReleaseAttachmentNames, giteaToken, useGiteaReleases, getGiteaUpdaterFeedBase, };