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>
164 lines
5.7 KiB
JavaScript
164 lines
5.7 KiB
JavaScript
"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'
|
|
});
|
|
}
|
|
}
|