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:
163
backend/dist/middleware/wordpressPermissions.js
vendored
Normal file
163
backend/dist/middleware/wordpressPermissions.js
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.validateWordPressPermissions = validateWordPressPermissions;
|
||||
const migrate_1 = require("../db/migrate");
|
||||
const ipaddr_js_1 = __importDefault(require("ipaddr.js"));
|
||||
/**
|
||||
* Validates if an IP address matches any of the allowed IP patterns
|
||||
* Supports CIDR notation and wildcards
|
||||
*/
|
||||
function isIpAllowed(clientIp, allowedIps) {
|
||||
try {
|
||||
const clientAddr = ipaddr_js_1.default.process(clientIp);
|
||||
for (const allowedIp of allowedIps) {
|
||||
const trimmed = allowedIp.trim();
|
||||
if (!trimmed)
|
||||
continue;
|
||||
// Check for CIDR notation
|
||||
if (trimmed.includes('/')) {
|
||||
try {
|
||||
const [subnet, bits] = trimmed.split('/');
|
||||
const range = ipaddr_js_1.default.parseCIDR(trimmed);
|
||||
if (clientAddr.match(range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.warn(`Invalid CIDR notation: ${trimmed}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exact match
|
||||
try {
|
||||
const allowedAddr = ipaddr_js_1.default.process(trimmed);
|
||||
if (clientAddr.toString() === allowedAddr.toString()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.warn(`Invalid IP address: ${trimmed}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error processing client IP:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validates if a domain matches any of the allowed domain patterns
|
||||
* Supports wildcard subdomains (*.example.com)
|
||||
*/
|
||||
function isDomainAllowed(origin, allowedDomains) {
|
||||
try {
|
||||
// Extract domain from origin URL
|
||||
const url = new URL(origin);
|
||||
const domain = url.hostname;
|
||||
for (const allowedDomain of allowedDomains) {
|
||||
const trimmed = allowedDomain.trim();
|
||||
if (!trimmed)
|
||||
continue;
|
||||
// Wildcard subdomain support
|
||||
if (trimmed.startsWith('*.')) {
|
||||
const baseDomain = trimmed.substring(2);
|
||||
if (domain === baseDomain || domain.endsWith('.' + baseDomain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exact match
|
||||
if (domain === trimmed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error processing domain:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* WordPress API Permissions Middleware
|
||||
* Validates API access based on WordPress permissions table
|
||||
*/
|
||||
async function validateWordPressPermissions(req, res, next) {
|
||||
// Get API key from header
|
||||
const apiKey = req.headers['x-api-key'];
|
||||
// If no API key provided, skip WordPress validation
|
||||
if (!apiKey) {
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
// Query WordPress permissions table
|
||||
const result = await migrate_1.pool.query(`
|
||||
SELECT id, user_name, api_key, allowed_ips, allowed_domains, is_active
|
||||
FROM wp_dutchie_api_permissions
|
||||
WHERE api_key = $1 AND is_active = 1
|
||||
`, [apiKey]);
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(401).json({
|
||||
error: 'Invalid API key'
|
||||
});
|
||||
}
|
||||
const permission = result.rows[0];
|
||||
// Get client IP
|
||||
const clientIp = req.headers['x-forwarded-for']?.split(',')[0].trim() ||
|
||||
req.headers['x-real-ip'] ||
|
||||
req.ip ||
|
||||
req.connection.remoteAddress ||
|
||||
'';
|
||||
// Validate IP if configured
|
||||
if (permission.allowed_ips) {
|
||||
const allowedIps = permission.allowed_ips.split('\n').filter((ip) => ip.trim());
|
||||
if (allowedIps.length > 0 && !isIpAllowed(clientIp, allowedIps)) {
|
||||
return res.status(403).json({
|
||||
error: 'IP address not allowed',
|
||||
client_ip: clientIp
|
||||
});
|
||||
}
|
||||
}
|
||||
// Validate domain if configured
|
||||
const origin = req.get('origin') || req.get('referer') || '';
|
||||
if (permission.allowed_domains && origin) {
|
||||
const allowedDomains = permission.allowed_domains.split('\n').filter((d) => d.trim());
|
||||
if (allowedDomains.length > 0 && !isDomainAllowed(origin, allowedDomains)) {
|
||||
return res.status(403).json({
|
||||
error: 'Domain not allowed',
|
||||
origin: origin
|
||||
});
|
||||
}
|
||||
}
|
||||
// Update last_used_at timestamp (async, don't wait)
|
||||
migrate_1.pool.query(`
|
||||
UPDATE wp_dutchie_api_permissions
|
||||
SET last_used_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
`, [permission.id]).catch((err) => {
|
||||
console.error('Error updating last_used_at:', err);
|
||||
});
|
||||
// Set apiToken on request for tracking middleware
|
||||
// Default rate limit of 100 requests/minute for WordPress permissions
|
||||
req.apiToken = {
|
||||
id: permission.id,
|
||||
name: permission.user_name,
|
||||
rate_limit: 100
|
||||
};
|
||||
next();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('WordPress permissions validation error:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error during API validation'
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user