f0358dbdfe
Implements a read-only repository viewer for remote Gitea repos and local clones. Adds UI/CSS for viewer panels, breadcrumb/branch controls, file table, code/Markdown preview, and readme rendering (frontend/css/components.css, frontend/js/app.js). Extends app state and wiring (state.js, app.js) with viewer actions, branch/content loading, local/remote navigation, and preview helpers (base64 decoding, markdown rendering, syntax highlighting, 256 KB preview limit). Adds Gitea API helpers to fetch repo branches and contents (frontend/js/gitea-api.js) and Tauri JS bindings for local repo operations (tauri-api.js). Implements Rust backend commands to list branches, tree entries, and file contents (with size/binary checks and helper utilities) and wires them into the Tauri command registry (src-tauri/src/lib.rs). Also updates README to mention the new read-only viewer.
104 lines
3.0 KiB
JavaScript
104 lines
3.0 KiB
JavaScript
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();
|
|
}
|