2026-07-17 14:08:03 -05:00
|
|
|
import axios from "axios";
|
|
|
|
|
|
|
|
|
|
const rawGiteaBaseUrl = import.meta.env.VITE_GITEA_BASE_URL?.trim() ?? "";
|
|
|
|
|
const giteaToken = import.meta.env.VITE_GITEA_TOKEN?.trim() ?? "";
|
|
|
|
|
const rawGiteaRepoOwner = import.meta.env.VITE_GITEA_REPO_OWNER?.trim() ?? "";
|
|
|
|
|
const rawGiteaRepoName = import.meta.env.VITE_GITEA_REPO_NAME?.trim() ?? "";
|
|
|
|
|
|
|
|
|
|
export interface GiteaUser {
|
|
|
|
|
id: number;
|
|
|
|
|
login: string;
|
|
|
|
|
full_name?: string;
|
|
|
|
|
email?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 15:02:08 -05:00
|
|
|
interface GiteaRepoOwner {
|
|
|
|
|
login: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface GiteaRepoResponse {
|
|
|
|
|
name: string;
|
|
|
|
|
full_name?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
html_url?: string;
|
|
|
|
|
clone_url?: string;
|
|
|
|
|
ssh_url?: string;
|
|
|
|
|
updated_at?: string;
|
|
|
|
|
default_branch?: string;
|
|
|
|
|
private?: boolean;
|
|
|
|
|
owner?: GiteaRepoOwner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface GiteaBranchResponse {
|
|
|
|
|
name: string;
|
|
|
|
|
commit?: {
|
|
|
|
|
id?: string;
|
|
|
|
|
message?: string;
|
|
|
|
|
timestamp?: string;
|
|
|
|
|
url?: string;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface GiteaRepoSummary {
|
|
|
|
|
name: string;
|
|
|
|
|
fullName: string;
|
|
|
|
|
description: string;
|
|
|
|
|
webUrl: string;
|
|
|
|
|
cloneUrl: string;
|
|
|
|
|
defaultBranch: string;
|
|
|
|
|
isPrivate: boolean;
|
|
|
|
|
lastCommitAt: string;
|
|
|
|
|
lastCommitSha: string;
|
|
|
|
|
lastCommitMessage: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 14:08:03 -05:00
|
|
|
export const giteaConfig = {
|
|
|
|
|
baseUrl: rawGiteaBaseUrl,
|
|
|
|
|
hasToken: giteaToken.length > 0,
|
|
|
|
|
repoOwner: rawGiteaRepoOwner,
|
|
|
|
|
repoName: rawGiteaRepoName,
|
|
|
|
|
targetRepo:
|
|
|
|
|
rawGiteaRepoOwner.length > 0 && rawGiteaRepoName.length > 0
|
|
|
|
|
? `${rawGiteaRepoOwner}/${rawGiteaRepoName}`
|
|
|
|
|
: "",
|
|
|
|
|
isConfigured: rawGiteaBaseUrl.length > 0 && giteaToken.length > 0
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-17 15:02:08 -05:00
|
|
|
export function getApiBaseUrl(baseUrl: string): string {
|
2026-07-17 14:08:03 -05:00
|
|
|
const normalized = baseUrl.replace(/\/+$/, "");
|
|
|
|
|
return normalized.endsWith("/api/v1") ? normalized : `${normalized}/api/v1`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 15:02:08 -05:00
|
|
|
function assertGiteaConfigured(): void {
|
2026-07-17 14:08:03 -05:00
|
|
|
if (!giteaConfig.isConfigured) {
|
|
|
|
|
throw new Error("Set VITE_GITEA_BASE_URL and VITE_GITEA_TOKEN before testing the connection.");
|
|
|
|
|
}
|
2026-07-17 15:02:08 -05:00
|
|
|
}
|
2026-07-17 14:08:03 -05:00
|
|
|
|
2026-07-17 15:02:08 -05:00
|
|
|
function createGiteaClient() {
|
|
|
|
|
assertGiteaConfigured();
|
|
|
|
|
return axios.create({
|
2026-07-17 14:08:03 -05:00
|
|
|
baseURL: getApiBaseUrl(giteaConfig.baseUrl),
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `token ${giteaToken}`
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-07-17 15:02:08 -05:00
|
|
|
}
|
2026-07-17 14:08:03 -05:00
|
|
|
|
2026-07-17 15:02:08 -05:00
|
|
|
export async function getCurrentGiteaUser(): Promise<GiteaUser> {
|
|
|
|
|
const client = createGiteaClient();
|
2026-07-17 14:08:03 -05:00
|
|
|
const response = await client.get<GiteaUser>("/user");
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
2026-07-17 15:02:08 -05:00
|
|
|
|
|
|
|
|
export async function getReposOrderedByLastCommit(): Promise<GiteaRepoSummary[]> {
|
|
|
|
|
const client = axios.create({
|
|
|
|
|
baseURL: getApiBaseUrl(giteaConfig.baseUrl),
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `token ${giteaToken}`
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
assertGiteaConfigured();
|
|
|
|
|
|
|
|
|
|
const repos: GiteaRepoResponse[] = [];
|
|
|
|
|
let page = 1;
|
|
|
|
|
|
|
|
|
|
while (page <= 10) {
|
|
|
|
|
const response = await client.get<GiteaRepoResponse[]>("/user/repos", {
|
|
|
|
|
params: {
|
|
|
|
|
limit: 50,
|
|
|
|
|
page
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
repos.push(...response.data);
|
|
|
|
|
|
|
|
|
|
if (response.data.length < 50) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
page += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const repoDetails = await Promise.all(
|
|
|
|
|
repos.map(async (repo): Promise<GiteaRepoSummary> => {
|
|
|
|
|
const owner = repo.owner?.login || giteaConfig.repoOwner;
|
|
|
|
|
const defaultBranch = repo.default_branch || "main";
|
|
|
|
|
const fullName = repo.full_name || `${owner}/${repo.name}`;
|
|
|
|
|
|
|
|
|
|
let branch: GiteaBranchResponse | null = null;
|
|
|
|
|
try {
|
|
|
|
|
const branchResponse = await client.get<GiteaBranchResponse>(
|
|
|
|
|
`/repos/${owner}/${repo.name}/branches/${defaultBranch}`
|
|
|
|
|
);
|
|
|
|
|
branch = branchResponse.data;
|
|
|
|
|
} catch {
|
|
|
|
|
branch = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
name: repo.name,
|
|
|
|
|
fullName,
|
|
|
|
|
description: repo.description || "",
|
|
|
|
|
webUrl: repo.html_url || `${giteaConfig.baseUrl.replace(/\/+$/, "")}/${fullName}`,
|
|
|
|
|
cloneUrl: repo.clone_url || "",
|
|
|
|
|
defaultBranch,
|
|
|
|
|
isPrivate: Boolean(repo.private),
|
|
|
|
|
lastCommitAt: branch?.commit?.timestamp || repo.updated_at || "",
|
|
|
|
|
lastCommitSha: branch?.commit?.id || "",
|
|
|
|
|
lastCommitMessage: branch?.commit?.message?.split("\n")[0] || "No commit message available"
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return repoDetails.sort((a, b) => {
|
|
|
|
|
const aTime = a.lastCommitAt ? Date.parse(a.lastCommitAt) : 0;
|
|
|
|
|
const bTime = b.lastCommitAt ? Date.parse(b.lastCommitAt) : 0;
|
|
|
|
|
return bTime - aTime || a.fullName.localeCompare(b.fullName);
|
|
|
|
|
});
|
|
|
|
|
}
|