import { Base64 } from "js-base64"; import type { GitProvider } from "@tinacms/datalayer"; export interface GiteaGitProviderOptions { owner: string; repo: string; token: string; branch: string; baseUrl: string; commitMessage?: string; rootPath?: string; } export class GiteaGitProvider implements GitProvider { owner: string; repo: string; branch: string; baseUrl: string; commitMessage: string; rootPath?: string; private token: string; constructor(args: GiteaGitProviderOptions) { this.owner = args.owner; this.repo = args.repo; this.branch = args.branch; this.baseUrl = args.baseUrl.replace(/\/$/, ""); this.commitMessage = args.commitMessage || "Edited with TinaCMS"; this.rootPath = args.rootPath; this.token = args.token; } private getApiUrl(path: string): string { return `${this.baseUrl}/api/v1/repos/${this.owner}/${this.repo}/contents/${path}`; } private getHeaders(): Record { return { Authorization: `token ${this.token}`, "Content-Type": "application/json", Accept: "application/json", }; } private getKeyWithPath(key: string): string { return this.rootPath ? `${this.rootPath}/${key}` : key; } private async getFileSha( keyWithPath: string ): Promise { try { const url = `${this.getApiUrl(keyWithPath)}?ref=${encodeURIComponent(this.branch)}`; const response = await fetch(url, { method: "GET", headers: this.getHeaders(), }); if (!response.ok) { return undefined; } const data = await response.json(); return data.sha; } catch { return undefined; } } async onPut(key: string, value: string): Promise { const keyWithPath = this.getKeyWithPath(key); const sha = await this.getFileSha(keyWithPath); const body: Record = { message: this.commitMessage, content: Base64.encode(value), branch: this.branch, }; if (sha) { body.sha = sha; } const response = await fetch(this.getApiUrl(keyWithPath), { method: sha ? "PUT" : "POST", headers: this.getHeaders(), body: JSON.stringify(body), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Failed to save ${keyWithPath}: ${response.status} ${errorText}` ); } } async onDelete(key: string): Promise { const keyWithPath = this.getKeyWithPath(key); const sha = await this.getFileSha(keyWithPath); if (!sha) { throw new Error( `Could not find file ${keyWithPath} in repo ${this.owner}/${this.repo}` ); } const response = await fetch(this.getApiUrl(keyWithPath), { method: "DELETE", headers: this.getHeaders(), body: JSON.stringify({ message: this.commitMessage, branch: this.branch, sha, }), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Failed to delete ${keyWithPath}: ${response.status} ${errorText}` ); } } }