mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-06 11:36:44 +03:00
- Implemented comprehensive GitHub API rate limit handling:
- Integrated @octokit/plugin-throttling for automatic retry with exponential backoff
- Added RateLimitManager service to track and enforce rate limits
- Store rate limit status in database for persistence across restarts
- Automatic pause and resume when limits are exceeded
- Proper user identification for 5000 req/hr authenticated limit (vs 60 unauthenticated)
- Improved rate limit UI/UX:
- Removed intrusive rate limit card from dashboard
- Toast notifications only at critical thresholds (80% and 100% usage)
- All rate limit events logged for debugging
- Optimized for GitHub's API constraints:
- Reduced default batch size from 10 to 5 repositories
- Added documentation about GitHub's 100 concurrent request limit
- Better handling of repositories with many issues/PRs
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import type { APIRoute } from "astro";
|
|
import { getNewEvents } from "@/lib/events";
|
|
|
|
export const GET: APIRoute = async ({ request }) => {
|
|
const url = new URL(request.url);
|
|
const userId = url.searchParams.get("userId");
|
|
|
|
if (!userId) {
|
|
return new Response("Missing userId", { status: 400 });
|
|
}
|
|
|
|
// Create a new ReadableStream for SSE
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
const encoder = new TextEncoder();
|
|
let lastEventTime = new Date();
|
|
|
|
// Send initial connection message
|
|
controller.enqueue(encoder.encode(": connected\n\n"));
|
|
|
|
// Poll for new events every 2 seconds
|
|
const pollInterval = setInterval(async () => {
|
|
try {
|
|
// Get new rate limit events
|
|
const newEvents = await getNewEvents({
|
|
userId,
|
|
channel: "rate-limit",
|
|
lastEventTime,
|
|
});
|
|
|
|
// Send each new event
|
|
for (const event of newEvents) {
|
|
const message = `event: rate-limit\ndata: ${JSON.stringify(event.payload)}\n\n`;
|
|
controller.enqueue(encoder.encode(message));
|
|
lastEventTime = new Date(event.createdAt);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error polling for events:", error);
|
|
}
|
|
}, 2000); // Poll every 2 seconds
|
|
|
|
// Send heartbeat every 30 seconds to keep connection alive
|
|
const heartbeatInterval = setInterval(() => {
|
|
try {
|
|
controller.enqueue(encoder.encode(": heartbeat\n\n"));
|
|
} catch (error) {
|
|
clearInterval(heartbeatInterval);
|
|
clearInterval(pollInterval);
|
|
}
|
|
}, 30000);
|
|
|
|
// Cleanup on close
|
|
request.signal.addEventListener("abort", () => {
|
|
clearInterval(pollInterval);
|
|
clearInterval(heartbeatInterval);
|
|
controller.close();
|
|
});
|
|
},
|
|
});
|
|
|
|
return new Response(stream, {
|
|
headers: {
|
|
"Content-Type": "text/event-stream",
|
|
"Cache-Control": "no-cache",
|
|
"Connection": "keep-alive",
|
|
"X-Accel-Buffering": "no", // Disable nginx buffering
|
|
},
|
|
});
|
|
}; |