import { incidentToFilename, renderMarkdown } from "./markdown"; import type { Incident } from "@/types/incident"; export type GitProvider = "gitea" | "github" | "gitlab"; export interface GitConfig { provider: GitProvider; url?: string; // required for gitea and self-hosted gitlab token: string; owner: string; repo: string; } export interface PushResult { success: boolean; url?: string; error?: string; } export async function pushIncidentToGit( incident: Incident, config: GitConfig, markdownTemplate?: string ): Promise { switch (config.provider) { case "gitea": return pushGitea(incident, config, markdownTemplate); case "github": return pushGitHub(incident, config, markdownTemplate); case "gitlab": return pushGitLab(incident, config, markdownTemplate); } } async function pushGitea( incident: Incident, config: GitConfig, markdownTemplate?: string ): Promise { const filepath = incidentToFilename(incident); const content = renderMarkdown(incident, markdownTemplate); const base64Content = btoa(unescape(encodeURIComponent(content))); const apiBase = (config.url ?? "").replace(/\/$/, ""); const endpoint = `${apiBase}/api/v1/repos/${config.owner}/${config.repo}/contents/${filepath}`; try { const existing = await fetch(endpoint, { headers: { Authorization: `Bearer ${config.token}` }, }); let sha: string | undefined; if (existing.ok) { const data = await existing.json() as { sha?: string }; sha = data.sha; } const body: Record = { message: `incident: ${incident.title}`, content: base64Content, }; if (sha) body.sha = sha; const res = await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${config.token}`, "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (!res.ok) { return { success: false, error: `HTTP ${res.status}: ${await res.text()}` }; } const data = await res.json() as { content?: { html_url?: string } }; return { success: true, url: data.content?.html_url }; } catch (e) { return { success: false, error: e instanceof Error ? e.message : String(e) }; } } async function pushGitHub( incident: Incident, config: GitConfig, markdownTemplate?: string ): Promise { const filepath = incidentToFilename(incident); const content = renderMarkdown(incident, markdownTemplate); const base64Content = btoa(unescape(encodeURIComponent(content))); const endpoint = `https://api.github.com/repos/${config.owner}/${config.repo}/contents/${filepath}`; try { const existing = await fetch(endpoint, { headers: { Authorization: `Bearer ${config.token}`, Accept: "application/vnd.github+json", }, }); let sha: string | undefined; if (existing.ok) { const data = await existing.json() as { sha?: string }; sha = data.sha; } const body: Record = { message: `incident: ${incident.title}`, content: base64Content, }; if (sha) body.sha = sha; const res = await fetch(endpoint, { method: "PUT", headers: { Authorization: `Bearer ${config.token}`, Accept: "application/vnd.github+json", "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (!res.ok) { return { success: false, error: `HTTP ${res.status}: ${await res.text()}` }; } const data = await res.json() as { content?: { html_url?: string } }; return { success: true, url: data.content?.html_url }; } catch (e) { return { success: false, error: e instanceof Error ? e.message : String(e) }; } } async function pushGitLab( incident: Incident, config: GitConfig, markdownTemplate?: string ): Promise { const filepath = incidentToFilename(incident); const content = renderMarkdown(incident, markdownTemplate); const base64Content = btoa(unescape(encodeURIComponent(content))); const apiBase = (config.url ?? "https://gitlab.com").replace(/\/$/, ""); const projectId = encodeURIComponent(`${config.owner}/${config.repo}`); const encodedPath = encodeURIComponent(filepath); const endpoint = `${apiBase}/api/v4/projects/${projectId}/repository/files/${encodedPath}`; try { const existing = await fetch(`${endpoint}?ref=HEAD`, { headers: { "PRIVATE-TOKEN": config.token }, }); const method = existing.ok ? "PUT" : "POST"; const res = await fetch(endpoint, { method, headers: { "PRIVATE-TOKEN": config.token, "Content-Type": "application/json", }, body: JSON.stringify({ branch: "main", content: base64Content, encoding: "base64", commit_message: `incident: ${incident.title}`, }), }); if (!res.ok) { return { success: false, error: `HTTP ${res.status}: ${await res.text()}` }; } const data = await res.json() as { file_path?: string }; const webUrl = data.file_path ? `${apiBase}/${config.owner}/${config.repo}/-/blob/main/${data.file_path}` : undefined; return { success: true, url: webUrl }; } catch (e) { return { success: false, error: e instanceof Error ? e.message : String(e) }; } }