import { config } from '../config.js' /** Zeitlimit fuer jeden Gitea-API-Aufruf, in ms (ueberschreibbar via `GITEA_TIMEOUT_MS`, s. config.js). */ const GITEA_TIMEOUT_MS = config.gitea.timeoutMs /** * Baut die Authorization-/Content-Type-Header fuer einen Gitea-API-Aufruf. * Wirft, wenn GITEA_API_TOKEN nicht gesetzt ist. */ function giteaHeaders() { if (!config.gitea.apiToken) { throw new Error('GITEA_API_TOKEN fehlt in .env') } return { Authorization: `token ${config.gitea.apiToken}`, 'Content-Type': 'application/json', } } /** * Ruft `${config.gitea.baseUrl}/api/v1` auf und liefert die geparste JSON-Antwort * (oder null ohne Body). Eine Nicht-2xx-Antwort wirft eine Error mit .status; ein 409 * "same name already exists" wird in die deutsche Meldung uebersetzt (Subdomain-/Repo-Name). * Bricht nach `GITEA_TIMEOUT_MS` ab (Standard 30 s); der Abbruch wirft eine Error OHNE .status, * damit repoMove/projectAssignment ihn wie "Gitea nicht erreichbar" einordnen. */ export async function giteaFetch(path, options = {}) { let response try { response = await fetch(`${config.gitea.baseUrl}/api/v1${path}`, { ...options, headers: { ...giteaHeaders(), ...options.headers }, signal: options.signal || AbortSignal.timeout(GITEA_TIMEOUT_MS), }) } catch (err) { if (err?.name === 'TimeoutError' || err?.name === 'AbortError') { throw new Error(`Gitea antwortet nicht (Zeitlimit ${Math.round(GITEA_TIMEOUT_MS / 1000)} s)`) } throw err } const text = await response.text() let data = null if (text) { try { data = JSON.parse(text) } catch { data = { message: text } } } if (!response.ok) { let message = data?.message || `Gitea ${response.status}` if (response.status === 409 && /same name already exists/i.test(message)) { message = 'Ein Gitea-Repository mit diesem Namen existiert bereits. Bitte einen anderen Subdomain-/Repo-Namen wählen.' } const error = new Error(message) error.status = response.status throw error } return data } /** * Zerlegt "owner/repo" in [owner, repo]. Reines String-Splitting ohne Validierung - * fuer nicht "owner/repo"-foermige Eingaben ist das Ergebnis entsprechend kurz/lang. */ export function splitRepo(fullName) { return String(fullName || '').split('/') }