docs: add missing OpenAPI annotations for signup/verify, billing/success, billing/webhook
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 16m15s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 16m15s
This commit is contained in:
parent
427ec8e894
commit
8b31d11e74
15 changed files with 2167 additions and 128 deletions
36
dist/middleware/pdfRateLimit.js
vendored
36
dist/middleware/pdfRateLimit.js
vendored
|
|
@ -29,17 +29,33 @@ function checkRateLimit(apiKey) {
|
|||
const limit = getRateLimit(apiKey);
|
||||
const entry = rateLimitStore.get(apiKey);
|
||||
if (!entry || now >= entry.resetTime) {
|
||||
const resetTime = now + RATE_WINDOW_MS;
|
||||
rateLimitStore.set(apiKey, {
|
||||
count: 1,
|
||||
resetTime: now + RATE_WINDOW_MS
|
||||
resetTime
|
||||
});
|
||||
return true;
|
||||
return {
|
||||
allowed: true,
|
||||
limit,
|
||||
remaining: limit - 1,
|
||||
resetTime
|
||||
};
|
||||
}
|
||||
if (entry.count >= limit) {
|
||||
return false;
|
||||
return {
|
||||
allowed: false,
|
||||
limit,
|
||||
remaining: 0,
|
||||
resetTime: entry.resetTime
|
||||
};
|
||||
}
|
||||
entry.count++;
|
||||
return true;
|
||||
return {
|
||||
allowed: true,
|
||||
limit,
|
||||
remaining: limit - entry.count,
|
||||
resetTime: entry.resetTime
|
||||
};
|
||||
}
|
||||
function getQueuedCountForKey(apiKey) {
|
||||
return pdfQueue.filter(w => w.apiKey === apiKey).length;
|
||||
|
|
@ -73,10 +89,16 @@ export function pdfRateLimitMiddleware(req, res, next) {
|
|||
const keyInfo = req.apiKeyInfo;
|
||||
const apiKey = keyInfo?.key || "unknown";
|
||||
// Check rate limit first
|
||||
if (!checkRateLimit(apiKey)) {
|
||||
const limit = getRateLimit(apiKey);
|
||||
const rateLimitResult = checkRateLimit(apiKey);
|
||||
// Set rate limit headers on ALL responses
|
||||
res.set('X-RateLimit-Limit', String(rateLimitResult.limit));
|
||||
res.set('X-RateLimit-Remaining', String(rateLimitResult.remaining));
|
||||
res.set('X-RateLimit-Reset', String(Math.ceil(rateLimitResult.resetTime / 1000)));
|
||||
if (!rateLimitResult.allowed) {
|
||||
const tier = isProKey(apiKey) ? "pro" : "free";
|
||||
res.status(429).json({ error: `Rate limit exceeded: ${limit} PDFs/min allowed for ${tier} tier. Retry after 60s.` });
|
||||
const retryAfterSeconds = Math.ceil((rateLimitResult.resetTime - Date.now()) / 1000);
|
||||
res.set('Retry-After', String(retryAfterSeconds));
|
||||
res.status(429).json({ error: `Rate limit exceeded: ${rateLimitResult.limit} PDFs/min allowed for ${tier} tier. Retry after ${retryAfterSeconds}s.` });
|
||||
return;
|
||||
}
|
||||
// Add concurrency control to the request (pass apiKey for fairness)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue