function normalizeServerUrl(serverUrl) { return serverUrl.trim().replace(/\/+$/, ""); } export function buildApiBaseUrl(serverUrl) { const normalized = normalizeServerUrl(serverUrl); return normalized.endsWith("/api/v1") ? normalized : `${normalized}/api/v1`; } export function buildHeaders(serverConfig) { const headers = { Accept: "application/json", }; if (serverConfig.authMethod === "token" && serverConfig.token) { headers.Authorization = `token ${serverConfig.token}`; } else if ( serverConfig.authMethod === "password" && serverConfig.username && serverConfig.password ) { headers.Authorization = `Basic ${btoa( `${serverConfig.username}:${serverConfig.password}` )}`; } return headers; } export async function fetchCurrentUser(serverConfig) { const apiBase = buildApiBaseUrl(serverConfig.serverUrl); const response = await fetch(`${apiBase}/user`, { headers: buildHeaders(serverConfig), }); if (!response.ok) { throw new Error(`Gitea API error: ${response.status}`); } return response.json(); } export async function fetchRepositories(serverConfig, page = 1, limit = 50) { const apiBase = buildApiBaseUrl(serverConfig.serverUrl); const url = `${apiBase}/user/repos?page=${page}&limit=${limit}&sort=updated`; const response = await fetch(url, { headers: buildHeaders(serverConfig), }); if (!response.ok) { throw new Error(`Gitea API error: ${response.status}`); } return response.json(); } function encodePathSegment(value = "") { return encodeURIComponent(value); } function encodeRepoPath(path = "") { return path .split("/") .filter(Boolean) .map(encodePathSegment) .join("/"); } function splitRepoFullName(fullName = "") { const [owner, repo] = fullName.split("/"); if (!owner || !repo) { throw new Error("Repository full name must be in owner/repo format."); } return { owner, repo }; } export async function fetchRepoBranches(serverConfig, fullName) { const apiBase = buildApiBaseUrl(serverConfig.serverUrl); const { owner, repo } = splitRepoFullName(fullName); const url = `${apiBase}/repos/${encodePathSegment(owner)}/${encodePathSegment(repo)}/branches`; const response = await fetch(url, { headers: buildHeaders(serverConfig), }); if (!response.ok) { throw new Error(`Gitea API error: ${response.status}`); } return response.json(); } export async function fetchRepoContents(serverConfig, fullName, path = "", ref = "") { const apiBase = buildApiBaseUrl(serverConfig.serverUrl); const { owner, repo } = splitRepoFullName(fullName); const encodedPath = encodeRepoPath(path); const pathSuffix = encodedPath ? `/${encodedPath}` : ""; const query = ref ? `?ref=${encodeURIComponent(ref)}` : ""; const url = `${apiBase}/repos/${encodePathSegment(owner)}/${encodePathSegment(repo)}/contents${pathSuffix}${query}`; const response = await fetch(url, { headers: buildHeaders(serverConfig), }); if (!response.ok) { throw new Error(`Gitea API error: ${response.status}`); } return response.json(); }