Merge branch 'main' of https://git.365devnet.eu/365DevNet/365devnet
This commit is contained in:
255
src/components/LatestCommits.jsx
Normal file
255
src/components/LatestCommits.jsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
const BATCH_SIZE = 5;
|
||||
|
||||
const LatestCommits = ({ lang = 'en', totalContributions }) => {
|
||||
const [commits, setCommits] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [hasMore, setHasMore] = useState(false);
|
||||
const [offset, setOffset] = useState(0);
|
||||
|
||||
// Format date to a readable format
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
const day = date.toLocaleString(lang, { day: '2-digit' });
|
||||
const month = date.toLocaleString(lang, { month: 'short' }).toLowerCase().replace('.', '');
|
||||
const year = date.getFullYear();
|
||||
return `${day}-${month}-${year}`;
|
||||
};
|
||||
|
||||
const fetchCommits = async (newOffset = 0, append = false) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch(`/api/commits?limit=${BATCH_SIZE}&offset=${newOffset}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
setCommits(prev => append ? [...prev, ...data.commits] : data.commits);
|
||||
setHasMore(data.hasMore);
|
||||
setOffset(newOffset + BATCH_SIZE);
|
||||
} else {
|
||||
setError(data.error || 'Failed to fetch commits');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching commits:', err);
|
||||
setError('Failed to fetch commits');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCommits(0, false);
|
||||
}, []);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
fetchCommits(offset, true);
|
||||
};
|
||||
|
||||
if (loading && commits.length === 0) {
|
||||
return (
|
||||
<div class="backdrop-blur-sm bg-white/70 dark:bg-slate-900/70 rounded-xl shadow p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="text-3xl mr-4">💻</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl font-bold">Latest Commits</h2>
|
||||
<p class="text-gray-600 dark:text-slate-300">
|
||||
Showing contributions for user <span class="font-semibold text-blue-600 dark:text-blue-400">Richard</span> (all repositories)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center py-12">
|
||||
<div class="animate-spin text-4xl mb-4">⏳</div>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-lg">
|
||||
Loading latest commits...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div class="backdrop-blur-sm bg-white/70 dark:bg-slate-900/70 rounded-xl shadow p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="text-3xl mr-4">💻</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl font-bold">Latest Commits</h2>
|
||||
<p class="text-gray-600 dark:text-slate-300">
|
||||
Showing contributions for user <span class="font-semibold text-blue-600 dark:text-blue-400">Richard</span> (all repositories)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center py-12">
|
||||
<div class="text-4xl mb-4">❌</div>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-lg">
|
||||
{error}
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-500 text-sm mt-2">
|
||||
Please try refreshing or check back later.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="backdrop-blur-sm bg-white/70 dark:bg-slate-900/70 rounded-xl shadow p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="text-3xl mr-4">💻</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl font-bold">Latest Commits</h2>
|
||||
<p class="text-gray-600 dark:text-slate-300">
|
||||
Showing contributions for user <span class="font-semibold text-blue-600 dark:text-blue-400">Richard</span> (all repositories)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{commits.length > 0 ? (
|
||||
<>
|
||||
<div class="space-y-4">
|
||||
{commits.map((commit, i) => {
|
||||
// Use totalContributions for commit numbering
|
||||
const commitNumber = totalContributions !== undefined ? (totalContributions - i) : (i + 1);
|
||||
return (
|
||||
<div key={commit.sha} class="backdrop-blur-sm bg-gradient-to-r from-white/50 to-gray-50/50 dark:from-slate-800/50 dark:to-slate-900/50 rounded-xl shadow-sm hover:shadow-md transition-all duration-200 border border-gray-200/50 dark:border-slate-700/50 overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="flex flex-col gap-4 md:flex-row md:items-start md:gap-6">
|
||||
{/* Commit Number Badge */}
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full flex items-center justify-center text-sm font-bold">
|
||||
{commitNumber}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-lg text-gray-900 dark:text-white mb-2 break-words">
|
||||
{commit.message.split('\n')[0] || 'No message'}
|
||||
</h3>
|
||||
|
||||
{/* Format commit description with bullet points on new lines */}
|
||||
{commit.message && commit.message.split('\n').length > 1 && (
|
||||
(() => {
|
||||
const lines = commit.message.split('\n').slice(1);
|
||||
const bullets = lines.filter(line => line.trim().startsWith('- '));
|
||||
if (bullets.length > 0) {
|
||||
return (
|
||||
<ul class="text-sm text-gray-600 dark:text-gray-300 list-disc ml-5 space-y-1">
|
||||
{lines.map((line, idx) =>
|
||||
line.trim().startsWith('- ')
|
||||
? <li key={idx} class="break-words">{line.trim().slice(2)}</li>
|
||||
: line.trim() !== '' && <li key={idx} class="list-none pl-0 break-words">{line}</li>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
} else {
|
||||
const description = lines.filter(line => line.trim() !== '').join(' ');
|
||||
return description && (
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 break-words">{description}</p>
|
||||
);
|
||||
}
|
||||
})()
|
||||
)}
|
||||
|
||||
{/* Repository Info */}
|
||||
{commit.repo && (
|
||||
<div class="flex items-center mt-3 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div class="text-lg mr-2">📂</div>
|
||||
<a
|
||||
href={commit.repo_url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
>
|
||||
{commit.repo}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Commit Info Sidebar */}
|
||||
<div class="flex-shrink-0 md:min-w-[200px]">
|
||||
<div class="bg-gray-50/80 dark:bg-slate-800/80 rounded-lg p-4 space-y-3">
|
||||
{/* Date */}
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">📅</div>
|
||||
<span class="font-medium text-gray-700 dark:text-gray-200">
|
||||
{commit.date ? formatDate(commit.date) : 'Unknown date'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Author */}
|
||||
{commit.author && (
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">👤</div>
|
||||
<span class="text-gray-600 dark:text-gray-300">
|
||||
{commit.author === 'becarta' ? 'Richard Bergsma' : commit.author}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Commit Hash */}
|
||||
{commit.sha && (
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">🔗</div>
|
||||
<a
|
||||
href={commit.compare_url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="group"
|
||||
>
|
||||
<code class="px-2 py-1 rounded bg-blue-50 dark:bg-blue-900/50 border border-blue-200 dark:border-blue-700 font-mono text-xs text-blue-700 dark:text-blue-300 group-hover:bg-blue-100 dark:group-hover:bg-blue-800/50 transition-colors">
|
||||
{commit.sha.slice(0, 8)}
|
||||
</code>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{hasMore && (
|
||||
<div class="flex justify-center mt-8">
|
||||
<button
|
||||
onClick={handleLoadMore}
|
||||
disabled={loading}
|
||||
class="px-6 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg shadow transition-colors duration-200 flex items-center gap-2"
|
||||
>
|
||||
{loading ? (
|
||||
<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
)}
|
||||
{loading ? 'Loading...' : 'Load More'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-4xl mb-4">🤷♂️</div>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-lg">
|
||||
No commits found at this time.
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-500 text-sm mt-2">
|
||||
Please check back later or contact support if the issue persists.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LatestCommits;
|
@@ -1,12 +1,9 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
import Layout from '../../layouts/PageLayout.astro';
|
||||
import { getPermalink } from '../../utils/permalinks';
|
||||
import { getTranslation } from '../../i18n/translations';
|
||||
import React, { useMemo } from 'react';
|
||||
import dynamic from 'astro/dynamic';
|
||||
import ContributionCalendar from '../../components/ContributionCalendar.jsx';
|
||||
import CollapsibleIntro from '../../components/CollapsibleIntro.jsx';
|
||||
import LatestCommits from '../../components/LatestCommits.jsx';
|
||||
|
||||
const metadata = {
|
||||
title: 'Development Activity & Code Contributions | 365DevNet Ecosystem',
|
||||
@@ -19,7 +16,7 @@ const t = getTranslation(lang);
|
||||
// Fetch Gitea user heatmap at build time (cached)
|
||||
async function getUserHeatmap(username) {
|
||||
const url = `https://git.365devnet.eu/api/v1/users/${username}/heatmap`;
|
||||
let heatmap = [];
|
||||
let heatmap: Array<{ timestamp: number; contributions: number }> = [];
|
||||
try {
|
||||
const headers = { accept: 'application/json' };
|
||||
if (import.meta.env.GITEA_TOKEN) {
|
||||
@@ -33,7 +30,7 @@ async function getUserHeatmap(username) {
|
||||
console.error('Error fetching user heatmap:', error);
|
||||
}
|
||||
// Aggregate by date
|
||||
const contributions = {};
|
||||
const contributions: Record<string, number> = {};
|
||||
for (const entry of heatmap) {
|
||||
const date = new Date(entry.timestamp * 1000).toISOString().slice(0, 10);
|
||||
contributions[date] = (contributions[date] || 0) + entry.contributions;
|
||||
@@ -43,54 +40,6 @@ async function getUserHeatmap(username) {
|
||||
|
||||
const contributionData = await getUserHeatmap('Richard');
|
||||
|
||||
// Fetch latest user commits from activities/feeds
|
||||
async function getUserCommits(username) {
|
||||
const url = `https://git.365devnet.eu/api/v1/users/${username}/activities/feeds`;
|
||||
let feeds = [];
|
||||
try {
|
||||
const headers = { accept: 'application/json' };
|
||||
if (import.meta.env.GITEA_TOKEN) {
|
||||
headers['Authorization'] = `token ${import.meta.env.GITEA_TOKEN}`;
|
||||
}
|
||||
const response = await fetch(url, { headers });
|
||||
if (response.ok) {
|
||||
feeds = await response.json();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching user feeds:', error);
|
||||
}
|
||||
// Only keep commit_repo events
|
||||
const commits = [];
|
||||
for (const feed of feeds) {
|
||||
if (feed.op_type === 'commit_repo' && feed.content) {
|
||||
let content;
|
||||
try {
|
||||
content = JSON.parse(feed.content);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
// Each feed may have multiple commits
|
||||
if (content.Commits && Array.isArray(content.Commits)) {
|
||||
for (const commit of content.Commits) {
|
||||
commits.push({
|
||||
sha: commit.Sha1,
|
||||
message: commit.Message,
|
||||
author: commit.AuthorName,
|
||||
date: commit.Timestamp,
|
||||
repo: feed.repo ? feed.repo.full_name : '',
|
||||
repo_url: feed.repo ? feed.repo.html_url : '',
|
||||
compare_url: content.CompareURL ? `https://git.365devnet.eu/${content.CompareURL}` : '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commits.length >= 10) break;
|
||||
}
|
||||
return commits.slice(0, 10);
|
||||
}
|
||||
|
||||
const userCommits = await getUserCommits('Richard');
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return [
|
||||
{ params: { lang: 'en' } },
|
||||
@@ -100,17 +49,8 @@ export async function getStaticPaths() {
|
||||
];
|
||||
}
|
||||
|
||||
// Format date to a readable format
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
const day = date.toLocaleString(lang, { day: '2-digit' });
|
||||
const month = date.toLocaleString(lang, { month: 'short' }).toLowerCase().replace('.', '');
|
||||
const year = date.getFullYear();
|
||||
return `${day}-${month}-${year}`;
|
||||
};
|
||||
|
||||
// Calculate some stats from contribution data
|
||||
const totalContributions = Object.values(contributionData).reduce((sum, count) => sum + count, 0);
|
||||
const totalContributions = Object.values(contributionData).reduce((sum: number, count: number) => sum + count, 0);
|
||||
const contributionDays = Object.keys(contributionData).length;
|
||||
const averagePerDay = contributionDays > 0 ? (totalContributions / contributionDays).toFixed(1) : 0;
|
||||
---
|
||||
@@ -128,11 +68,6 @@ const averagePerDay = contributionDays > 0 ? (totalContributions / contributionD
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible Intro
|
||||
<div class="backdrop-blur-sm bg-white/70 dark:bg-slate-900/70 rounded-xl shadow p-6 mb-8">
|
||||
<CollapsibleIntro text={t.development.intro} client:visible />
|
||||
</div>-->
|
||||
|
||||
<!-- Stats Overview -->
|
||||
<div class="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div class="backdrop-blur-sm bg-gradient-to-br from-green-50/80 to-teal-50/80 dark:from-slate-800/80 dark:to-slate-900/80 rounded-xl shadow p-6 text-center">
|
||||
@@ -167,132 +102,7 @@ const averagePerDay = contributionDays > 0 ? (totalContributions / contributionD
|
||||
<ContributionCalendar data={contributionData} client:visible />
|
||||
</div>
|
||||
|
||||
<!-- Latest Commits Section -->
|
||||
<div class="backdrop-blur-sm bg-white/70 dark:bg-slate-900/70 rounded-xl shadow p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="text-3xl mr-4">💻</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold">{t.development.latestCommits || 'Latest Commits'}</h2>
|
||||
<p class="text-gray-600 dark:text-slate-300">
|
||||
Showing contributions for user <span class="font-semibold text-blue-600 dark:text-blue-400">Richard</span> (all repositories)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{Array.isArray(userCommits) && userCommits.length > 0 ? (
|
||||
<div class="space-y-4">
|
||||
{userCommits.map((commit, index) => (
|
||||
<div class="backdrop-blur-sm bg-gradient-to-r from-white/50 to-gray-50/50 dark:from-slate-800/50 dark:to-slate-900/50 rounded-xl shadow-sm hover:shadow-md transition-all duration-200 border border-gray-200/50 dark:border-slate-700/50 overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="flex flex-col gap-4 md:flex-row md:items-start md:gap-6">
|
||||
<!-- Commit Number Badge -->
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full flex items-center justify-center text-sm font-bold">
|
||||
{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-lg text-gray-900 dark:text-white mb-2 break-words">
|
||||
{commit.message.split('\n')[0] || 'No message'}
|
||||
</h3>
|
||||
|
||||
{/* Format commit description with bullet points on new lines */}
|
||||
{commit.message && commit.message.split('\n').length > 1 && (
|
||||
(() => {
|
||||
const lines = commit.message.split('\n').slice(1);
|
||||
const bullets = lines.filter(line => line.trim().startsWith('- '));
|
||||
if (bullets.length > 0) {
|
||||
return (
|
||||
<ul class="text-sm text-gray-600 dark:text-gray-300 list-disc ml-5 space-y-1">
|
||||
{lines.map((line, idx) =>
|
||||
line.trim().startsWith('- ')
|
||||
? <li key={idx} class="break-words">{line.trim().slice(2)}</li>
|
||||
: line.trim() !== '' && <li key={idx} class="list-none pl-0 break-words">{line}</li>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
} else {
|
||||
const description = lines.filter(line => line.trim() !== '').join(' ');
|
||||
return description && (
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 break-words">{description}</p>
|
||||
);
|
||||
}
|
||||
})()
|
||||
)}
|
||||
|
||||
<!-- Repository Info -->
|
||||
{commit.repo && (
|
||||
<div class="flex items-center mt-3 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div class="text-lg mr-2">📂</div>
|
||||
<a
|
||||
href={commit.repo_url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
>
|
||||
{commit.repo}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<!-- Commit Info Sidebar -->
|
||||
<div class="flex-shrink-0 md:min-w-[200px]">
|
||||
<div class="bg-gray-50/80 dark:bg-slate-800/80 rounded-lg p-4 space-y-3">
|
||||
{/* Date */}
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">📅</div>
|
||||
<span class="font-medium text-gray-700 dark:text-gray-200">
|
||||
{commit.date ? formatDate(commit.date) : 'Unknown date'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Author */}
|
||||
{commit.author && (
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">👤</div>
|
||||
<span class="text-gray-600 dark:text-gray-300">
|
||||
{commit.author === 'becarta' ? 'Richard Bergsma' : commit.author}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Commit Hash */}
|
||||
{commit.sha && (
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="text-gray-500 dark:text-gray-400 mr-2">🔗</div>
|
||||
<a
|
||||
href={commit.compare_url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="group"
|
||||
>
|
||||
<code class="px-2 py-1 rounded bg-blue-50 dark:bg-blue-900/50 border border-blue-200 dark:border-blue-700 font-mono text-xs text-blue-700 dark:text-blue-300 group-hover:bg-blue-100 dark:group-hover:bg-blue-800/50 transition-colors">
|
||||
{commit.sha.slice(0, 8)}
|
||||
</code>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-4xl mb-4">🤷♂️</div>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-lg">
|
||||
Unable to fetch commit history at this time.
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-500 text-sm mt-2">
|
||||
Please check back later or contact support if the issue persists.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<!-- Latest Commits Section - Now Dynamic -->
|
||||
<LatestCommits lang={lang} totalContributions={totalContributions} client:visible />
|
||||
</div>
|
||||
</Layout>
|
97
src/pages/api/commits.ts
Normal file
97
src/pages/api/commits.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
const username = 'Richard';
|
||||
const url = `https://git.365devnet.eu/api/v1/users/${username}/activities/feeds`;
|
||||
|
||||
const headers: HeadersInit = {
|
||||
accept: 'application/json'
|
||||
};
|
||||
|
||||
if (import.meta.env.GITEA_TOKEN) {
|
||||
headers['Authorization'] = `token ${import.meta.env.GITEA_TOKEN}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, { headers });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Gitea API responded with status: ${response.status}`);
|
||||
}
|
||||
|
||||
const feeds = await response.json();
|
||||
|
||||
// Only keep commit_repo events
|
||||
const commits: Array<{
|
||||
sha: string;
|
||||
message: string;
|
||||
author: string;
|
||||
date: string;
|
||||
repo: string;
|
||||
repo_url: string;
|
||||
compare_url: string;
|
||||
}> = [];
|
||||
|
||||
for (const feed of feeds) {
|
||||
if (feed.op_type === 'commit_repo' && feed.content) {
|
||||
let content;
|
||||
try {
|
||||
content = JSON.parse(feed.content);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
// Each feed may have multiple commits
|
||||
if (content.Commits && Array.isArray(content.Commits)) {
|
||||
for (const commit of content.Commits) {
|
||||
commits.push({
|
||||
sha: commit.Sha1,
|
||||
message: commit.Message,
|
||||
author: commit.AuthorName,
|
||||
date: commit.Timestamp,
|
||||
repo: feed.repo ? feed.repo.full_name : '',
|
||||
repo_url: feed.repo ? feed.repo.html_url : '',
|
||||
compare_url: content.CompareURL ? `https://git.365devnet.eu/${content.CompareURL}` : '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pagination
|
||||
const { searchParams } = new URL(request.url);
|
||||
const limit = parseInt(searchParams.get('limit') || '5', 10);
|
||||
const offset = parseInt(searchParams.get('offset') || '0', 10);
|
||||
const pagedCommits = commits.slice(offset, offset + limit);
|
||||
const hasMore = offset + limit < commits.length;
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
commits: pagedCommits,
|
||||
hasMore,
|
||||
total: commits.length,
|
||||
timestamp: new Date().toISOString()
|
||||
}), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
'Expires': '0'
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching commits:', error);
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Failed to fetch commits',
|
||||
timestamp: new Date().toISOString()
|
||||
}), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user