The job_run_logs table tracks scheduled job orchestration, not individual worker jobs. Worker info (worker_id, worker_hostname) belongs on dispensary_crawl_jobs, not job_run_logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
95 lines
3.0 KiB
JavaScript
95 lines
3.0 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.trackApiUsage = trackApiUsage;
|
|
exports.checkRateLimit = checkRateLimit;
|
|
const migrate_1 = require("../db/migrate");
|
|
async function trackApiUsage(req, res, next) {
|
|
// Only track if authenticated via API token
|
|
if (!req.apiToken) {
|
|
return next();
|
|
}
|
|
const startTime = Date.now();
|
|
req.startTime = startTime;
|
|
// Get request size
|
|
const requestSize = req.headers['content-length']
|
|
? parseInt(req.headers['content-length'])
|
|
: 0;
|
|
// Capture original res.json to measure response
|
|
const originalJson = res.json.bind(res);
|
|
let responseSize = 0;
|
|
res.json = function (body) {
|
|
responseSize = JSON.stringify(body).length;
|
|
return originalJson(body);
|
|
};
|
|
// Track after response is sent
|
|
res.on('finish', async () => {
|
|
const responseTime = Date.now() - startTime;
|
|
try {
|
|
await migrate_1.pool.query(`
|
|
INSERT INTO api_token_usage (
|
|
token_id,
|
|
endpoint,
|
|
method,
|
|
status_code,
|
|
response_time_ms,
|
|
request_size,
|
|
response_size,
|
|
ip_address,
|
|
user_agent
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
`, [
|
|
req.apiToken.id,
|
|
req.path,
|
|
req.method,
|
|
res.statusCode,
|
|
responseTime,
|
|
requestSize,
|
|
responseSize,
|
|
req.ip,
|
|
req.headers['user-agent'] || null
|
|
]);
|
|
// Update last_used_at
|
|
await migrate_1.pool.query('UPDATE api_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE id = $1', [req.apiToken.id]);
|
|
}
|
|
catch (error) {
|
|
console.error('Error tracking API usage:', error);
|
|
}
|
|
});
|
|
next();
|
|
}
|
|
// Rate limiting check
|
|
async function checkRateLimit(req, res, next) {
|
|
if (!req.apiToken) {
|
|
return next();
|
|
}
|
|
const { id, rate_limit } = req.apiToken;
|
|
try {
|
|
// Count requests in the last minute
|
|
const result = await migrate_1.pool.query(`
|
|
SELECT COUNT(*) as request_count
|
|
FROM api_token_usage
|
|
WHERE token_id = $1
|
|
AND created_at > NOW() - INTERVAL '1 minute'
|
|
`, [id]);
|
|
const requestCount = parseInt(result.rows[0].request_count);
|
|
if (requestCount >= rate_limit) {
|
|
return res.status(429).json({
|
|
error: 'Rate limit exceeded',
|
|
limit: rate_limit,
|
|
current: requestCount,
|
|
retry_after: 60
|
|
});
|
|
}
|
|
// Add rate limit headers
|
|
res.setHeader('X-RateLimit-Limit', rate_limit.toString());
|
|
res.setHeader('X-RateLimit-Remaining', (rate_limit - requestCount).toString());
|
|
res.setHeader('X-RateLimit-Reset', new Date(Date.now() + 60000).toISOString());
|
|
next();
|
|
}
|
|
catch (error) {
|
|
console.error('Error checking rate limit:', error);
|
|
next();
|
|
}
|
|
}
|