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>
114 lines
4.0 KiB
JavaScript
114 lines
4.0 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.generateToken = generateToken;
|
|
exports.verifyToken = verifyToken;
|
|
exports.authenticateUser = authenticateUser;
|
|
exports.authMiddleware = authMiddleware;
|
|
exports.requireRole = requireRole;
|
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
const bcrypt_1 = __importDefault(require("bcrypt"));
|
|
const migrate_1 = require("../db/migrate");
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'change_this_in_production';
|
|
function generateToken(user) {
|
|
return jsonwebtoken_1.default.sign({ id: user.id, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '7d' });
|
|
}
|
|
function verifyToken(token) {
|
|
try {
|
|
return jsonwebtoken_1.default.verify(token, JWT_SECRET);
|
|
}
|
|
catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
async function authenticateUser(email, password) {
|
|
const result = await migrate_1.pool.query('SELECT id, email, password_hash, role FROM users WHERE email = $1', [email]);
|
|
if (result.rows.length === 0) {
|
|
return null;
|
|
}
|
|
const user = result.rows[0];
|
|
const isValid = await bcrypt_1.default.compare(password, user.password_hash);
|
|
if (!isValid) {
|
|
return null;
|
|
}
|
|
return {
|
|
id: user.id,
|
|
email: user.email,
|
|
role: user.role
|
|
};
|
|
}
|
|
async function authMiddleware(req, res, next) {
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
return res.status(401).json({ error: 'No token provided' });
|
|
}
|
|
const token = authHeader.substring(7);
|
|
// Try JWT first
|
|
const jwtUser = verifyToken(token);
|
|
if (jwtUser) {
|
|
req.user = jwtUser;
|
|
return next();
|
|
}
|
|
// If JWT fails, try API token
|
|
try {
|
|
const result = await migrate_1.pool.query(`
|
|
SELECT id, name, rate_limit, active, expires_at, allowed_endpoints
|
|
FROM api_tokens
|
|
WHERE token = $1
|
|
`, [token]);
|
|
if (result.rows.length === 0) {
|
|
return res.status(401).json({ error: 'Invalid token' });
|
|
}
|
|
const apiToken = result.rows[0];
|
|
// Check if token is active
|
|
if (!apiToken.active) {
|
|
return res.status(401).json({ error: 'Token is disabled' });
|
|
}
|
|
// Check if token is expired
|
|
if (apiToken.expires_at && new Date(apiToken.expires_at) < new Date()) {
|
|
return res.status(401).json({ error: 'Token has expired' });
|
|
}
|
|
// Check allowed endpoints
|
|
if (apiToken.allowed_endpoints && apiToken.allowed_endpoints.length > 0) {
|
|
const isAllowed = apiToken.allowed_endpoints.some((pattern) => {
|
|
// Simple wildcard matching
|
|
const regex = new RegExp('^' + pattern.replace('*', '.*') + '$');
|
|
return regex.test(req.path);
|
|
});
|
|
if (!isAllowed) {
|
|
return res.status(403).json({ error: 'Endpoint not allowed for this token' });
|
|
}
|
|
}
|
|
// Set API token on request for tracking
|
|
req.apiToken = {
|
|
id: apiToken.id,
|
|
name: apiToken.name,
|
|
rate_limit: apiToken.rate_limit
|
|
};
|
|
// Set a generic user for compatibility with existing code
|
|
req.user = {
|
|
id: apiToken.id,
|
|
email: `api-token-${apiToken.id}@system`,
|
|
role: 'api'
|
|
};
|
|
next();
|
|
}
|
|
catch (error) {
|
|
console.error('Error verifying API token:', error);
|
|
return res.status(500).json({ error: 'Authentication failed' });
|
|
}
|
|
}
|
|
function requireRole(...roles) {
|
|
return (req, res, next) => {
|
|
if (!req.user) {
|
|
return res.status(401).json({ error: 'Not authenticated' });
|
|
}
|
|
if (!roles.includes(req.user.role)) {
|
|
return res.status(403).json({ error: 'Insufficient permissions' });
|
|
}
|
|
next();
|
|
};
|
|
}
|