Files
atridotdad/src/utils/gitea.ts
Atridad Lahiji 9b98476df6
All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m25s
3.1.0 - Added Gitea integration for Projects page
2025-10-08 16:12:26 -06:00

146 lines
3.8 KiB
TypeScript

export interface GiteaRepoInfo {
commits: number;
releases: number;
language: string;
updatedAt: string;
size: number;
defaultBranch: string;
topics: string[];
}
export interface GiteaConfig {
domain: string;
owner: string;
repo: string;
}
export function parseGiteaUrl(url: string): GiteaConfig | null {
try {
const urlObj = new URL(url);
const pathParts = urlObj.pathname.split("/").filter((p) => p);
if (pathParts.length >= 2) {
return {
domain: urlObj.origin,
owner: pathParts[0],
repo: pathParts[1],
};
}
} catch (e) {
// Invalid URL
}
return null;
}
export async function fetchGiteaRepoInfo(
config: GiteaConfig,
): Promise<GiteaRepoInfo | null> {
try {
const apiUrl = `${config.domain}/api/v1/repos/${config.owner}/${config.repo}`;
const response = await fetch(apiUrl, {
headers: {
Accept: "application/json",
},
signal: AbortSignal.timeout(5000),
});
if (!response.ok) {
return null;
}
const data = await response.json();
let commitsCount = 0;
try {
const commitsUrl = `${config.domain}/api/v1/repos/${config.owner}/${config.repo}/commits?limit=1&stat=false`;
const commitsResponse = await fetch(commitsUrl, {
headers: {
Accept: "application/json",
},
signal: AbortSignal.timeout(5000),
});
if (commitsResponse.ok) {
const totalCount = commitsResponse.headers.get("X-Total-Count");
commitsCount = totalCount ? parseInt(totalCount, 10) : 0;
}
} catch (error) {
// Ignore
}
let releasesCount = 0;
try {
const releasesUrl = `${config.domain}/api/v1/repos/${config.owner}/${config.repo}/releases`;
const releasesResponse = await fetch(releasesUrl, {
headers: {
Accept: "application/json",
},
signal: AbortSignal.timeout(5000),
});
if (releasesResponse.ok) {
const releases = await releasesResponse.json();
releasesCount = Array.isArray(releases) ? releases.length : 0;
}
} catch (error) {
// Ignore
}
return {
commits: commitsCount,
releases: releasesCount,
language: data.language || "Unknown",
updatedAt: data.updated_at || data.pushed_at || "",
size: data.size || 0,
defaultBranch: data.default_branch || "main",
topics: Array.isArray(data.topics) ? data.topics : [],
};
} catch (error) {
return null;
}
}
export async function fetchGiteaInfoFromUrl(
url: string,
): Promise<GiteaRepoInfo | null> {
const config = parseGiteaUrl(url);
if (!config) {
return null;
}
return fetchGiteaRepoInfo(config);
}
export function formatRelativeTime(dateString: string): string {
if (!dateString) return "Unknown";
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffMinutes = Math.floor(diffMs / (1000 * 60));
if (diffMinutes < 60) {
return `${diffMinutes} minute${diffMinutes !== 1 ? "s" : ""} ago`;
} else if (diffHours < 24) {
return `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
} else if (diffDays < 30) {
return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
} else if (diffDays < 365) {
const months = Math.floor(diffDays / 30);
return `${months} month${months !== 1 ? "s" : ""} ago`;
} else {
const years = Math.floor(diffDays / 365);
return `${years} year${years !== 1 ? "s" : ""} ago`;
}
}
export function formatRepoSize(sizeKb: number): string {
if (sizeKb < 1024) {
return `${sizeKb} KB`;
} else if (sizeKb < 1024 * 1024) {
return `${(sizeKb / 1024).toFixed(1)} MB`;
} else {
return `${(sizeKb / (1024 * 1024)).toFixed(1)} GB`;
}
}