fix(monitor): remove non-existent worker columns from job_run_logs query
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>
This commit is contained in:
94
backend/dist/middleware/apiTokenTracker.js
vendored
Normal file
94
backend/dist/middleware/apiTokenTracker.js
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
"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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user