import { getSiteSetting } from "@/lib/site-settings"; const LABELS: Record = { bug: ["bug"], suggestion: ["enhancement"], question: ["question"], }; /** * Opens an issue on the configured Gitea repo for a support ticket. Returns * the issue's HTML URL on success, or null if Gitea isn't configured or the * request fails — callers must treat this as best-effort, not block on it, * since the ticket itself is already saved regardless. */ export async function createGiteaIssue(opts: { type: string; title: string; body: string; }): Promise<{ url: string | null; error: string | null }> { const [baseUrl, token, repo] = await Promise.all([ getSiteSetting("GITEA_URL"), getSiteSetting("GITEA_TOKEN"), getSiteSetting("GITEA_REPO"), ]); if (!baseUrl || !token || !repo) { return { url: null, error: null }; } try { const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/v1/repos/${repo}/issues`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `token ${token}`, }, body: JSON.stringify({ title: opts.title, body: opts.body, labels: LABELS[opts.type] ?? [], }), signal: AbortSignal.timeout(10000), }); if (!res.ok) { const text = await res.text().catch(() => ""); return { url: null, error: `Gitea returned ${res.status}: ${text.slice(0, 300)}` }; } const issue = (await res.json()) as { html_url?: string }; return { url: issue.html_url ?? null, error: issue.html_url ? null : "Gitea response had no html_url" }; } catch (err) { // A thrown fetch error (DNS failure, connection refused, timeout, TLS // error) never reaches the res.ok branch above, so its real cause would // otherwise be lost — err.message alone is often just "fetch failed" // with the actual reason nested in err.cause. Log server-side with the // full cause chain, and fold a short form into the returned message so // it also shows up in the admin support view's tooltip, not just logs. const cause = err instanceof Error && err.cause instanceof Error ? err.cause.message : undefined; const message = err instanceof Error ? err.message : "Unknown error"; console.error("[gitea] createGiteaIssue failed", { baseUrl, repo, message, cause }); return { url: null, error: cause ? `${message}: ${cause}` : message }; } }