122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
import { GITEA_USERNAME } from './config'; // keep only safe, non-secret values
|
|
import type {
|
|
GiteaRepo,
|
|
GiteaCommit,
|
|
HeatmapEntry,
|
|
CommitStats,
|
|
} from './types';
|
|
|
|
const API_PREFIX = '/api/gitea';
|
|
|
|
async function get<T>(path: string): Promise<T> {
|
|
const res = await fetch(`${API_PREFIX}${path}`, {
|
|
headers: { Accept: 'application/json' },
|
|
cache: 'no-store',
|
|
});
|
|
if (!res.ok) throw new Error(`Gitea proxy ${path}: HTTP ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export async function getRepos(limit = 6): Promise<GiteaRepo[]> {
|
|
return get<GiteaRepo[]>(
|
|
`/users/${GITEA_USERNAME}/repos?limit=${limit}&sort=newest`
|
|
);
|
|
}
|
|
|
|
export async function getHeatmap(): Promise<HeatmapEntry[]> {
|
|
return get<HeatmapEntry[]>(`/users/${GITEA_USERNAME}/heatmap`);
|
|
}
|
|
|
|
export async function getRepoCommits(
|
|
repo: string,
|
|
limit = 5
|
|
): Promise<GiteaCommit[]> {
|
|
return get<GiteaCommit[]>(
|
|
`/repos/${GITEA_USERNAME}/${repo}/commits?limit=${limit}`
|
|
);
|
|
}
|
|
|
|
export async function getRecentCommits(
|
|
repos: GiteaRepo[],
|
|
perRepo = 4
|
|
): Promise<GiteaCommit[]> {
|
|
const results = await Promise.allSettled(
|
|
repos.slice(0, 4).map((r) =>
|
|
getRepoCommits(r.name, perRepo).then((commits) =>
|
|
commits.map((c) => ({
|
|
...c,
|
|
_repo: r.name,
|
|
_repoUrl: r.html_url,
|
|
}))
|
|
)
|
|
)
|
|
);
|
|
|
|
const all: GiteaCommit[] = [];
|
|
for (const r of results) {
|
|
if (r.status === 'fulfilled') all.push(...r.value);
|
|
}
|
|
|
|
return all
|
|
.sort(
|
|
(a, b) =>
|
|
new Date(b.created).getTime() - new Date(a.created).getTime()
|
|
)
|
|
.slice(0, 15);
|
|
}
|
|
|
|
/** Local date key (avoids UTC shift bugs) */
|
|
function dayKey(date: Date): string {
|
|
const y = date.getFullYear();
|
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
const d = String(date.getDate()).padStart(2, '0');
|
|
return `${y}-${m}-${d}`;
|
|
}
|
|
|
|
export function calcStats(heatmap: HeatmapEntry[]): CommitStats {
|
|
const yearAgo = Date.now() / 1000 - 365 * 86400;
|
|
const year = heatmap.filter((e) => e.timestamp > yearAgo);
|
|
const total = year.reduce((s, e) => s + e.contributions, 0);
|
|
const activeDays = year.filter((e) => e.contributions > 0).length;
|
|
|
|
const dateSet = new Set<string>();
|
|
for (const e of year) {
|
|
if (e.contributions <= 0) continue;
|
|
const d = new Date(e.timestamp * 1000);
|
|
dateSet.add(dayKey(d));
|
|
}
|
|
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
// Current streak (backwards from today)
|
|
let currentStreak = 0;
|
|
const cur = new Date(today);
|
|
if (!dateSet.has(dayKey(cur))) {
|
|
cur.setDate(cur.getDate() - 1);
|
|
}
|
|
while (dateSet.has(dayKey(cur))) {
|
|
currentStreak++;
|
|
cur.setDate(cur.getDate() - 1);
|
|
}
|
|
|
|
// Longest streak
|
|
let longestStreak = 0;
|
|
let streak = 0;
|
|
const sorted = Array.from(dateSet).sort();
|
|
for (let i = 0; i < sorted.length; i++) {
|
|
if (i === 0) {
|
|
streak = 1;
|
|
longestStreak = 1;
|
|
continue;
|
|
}
|
|
const prev = new Date(sorted[i - 1] + 'T00:00:00');
|
|
prev.setDate(prev.getDate() + 1);
|
|
if (dayKey(prev) === sorted[i]) streak++;
|
|
else streak = 1;
|
|
if (streak > longestStreak) longestStreak = streak;
|
|
}
|
|
|
|
return { total, currentStreak, longestStreak, activeDays };
|
|
}
|