import config from '../config.js'; /** * Gitea API client — uses API token auth */ const headers = () => ({ 'Content-Type': 'application/json', 'Authorization': `token ${config.giteaToken}`, }); const apiUrl = (path) => `${config.giteaUrl}/api/v1${path}`; async function request(path, opts = {}) { const url = apiUrl(path); const res = await fetch(url, { ...opts, headers: { ...headers(), ...opts.headers }, }); if (!res.ok) { const body = await res.text().catch(() => ''); throw new Error(`Gitea API error: ${res.status} ${res.statusText} — ${body}`); } return res.json(); } /** * List all accessible repositories */ export async function listRepos(page = 1, limit = 50) { return request(`/repos/search?page=${page}&limit=${limit}&sort=updated&order=desc`); } /** * Get a repository by owner/name */ export async function getRepo(owner, name) { return request(`/repos/${owner}/${name}`); } /** * List branches for a repository */ export async function listBranches(owner, name) { return request(`/repos/${owner}/${name}/branches`); } /** * Get the latest commit on a branch */ export async function getLatestCommit(owner, name, branch = 'main') { const commits = await request(`/repos/${owner}/${name}/commits?sha=${branch}&limit=1`); if (commits.length === 0) return null; return { sha: commits[0].sha, message: commits[0].commit?.message || '', author: commits[0].commit?.author?.name || '', date: commits[0].commit?.author?.date || '', }; } /** * Create a webhook on a Gitea repository */ export async function createWebhook(owner, name, targetUrl, secret) { return request(`/repos/${owner}/${name}/hooks`, { method: 'POST', body: JSON.stringify({ type: 'gitea', config: { url: targetUrl, content_type: 'json', secret: secret, }, events: ['push'], active: true, }), }); } /** * Delete a webhook from a Gitea repository */ export async function deleteWebhook(owner, name, hookId) { const url = apiUrl(`/repos/${owner}/${name}/hooks/${hookId}`); const res = await fetch(url, { method: 'DELETE', headers: headers(), }); if (!res.ok && res.status !== 404) { throw new Error(`Gitea API error: ${res.status}`); } } /** * List webhooks for a repository */ export async function listWebhooks(owner, name) { return request(`/repos/${owner}/${name}/hooks`); } /** * Get the clone URL (with token embedded for private repos) */ export function getCloneUrl(repoUrl) { // repoUrl is like "http://gitea:3000/owner/repo" // We need to inject the token for authenticated clone const url = new URL(repoUrl); url.username = 'autodeployer'; url.password = config.giteaToken; return url.toString(); } /** * Parse a Gitea repo URL into owner/name */ export function parseRepoUrl(repoUrl) { const url = new URL(repoUrl); const parts = url.pathname.replace(/^\//, '').replace(/\.git$/, '').split('/'); if (parts.length < 2) throw new Error(`Invalid repo URL: ${repoUrl}`); return { owner: parts[0], name: parts[1] }; } /** * Test the Gitea connection */ export async function testConnection() { try { const user = await request('/user'); return { ok: true, user: user.login }; } catch (err) { return { ok: false, error: err.message }; } }