← back to Vendor Discount Agent
server.js
255 lines
const express = require('express');
const helmet = require('helmet');
const path = require('path');
const { Pool } = require('pg');
const { checkVendorDiscount } = require('./discount-checker');
const app = express();
// Security headers via helmet (added 2026-05-04 overnight YOLO loop)
app.use(helmet({ contentSecurityPolicy: false }));
const PORT = 9860;
const pool = new Pool({
connectionString: (process.env.DATABASE_URL || 'postgresql://dw_admin@127.0.0.1:15432/dw_unified')
});
app.use(express.json());
// Hard 404 for snapshot / backup files so they never serve from the static root.
app.use((req, res, next) => {
if (/\.(bak|pre-)/i.test(req.path)) return res.status(404).end();
next();
});
app.use(express.static(path.join(__dirname, 'public')));
// Basic auth
app.use((req, res, next) => {
if (req.path === '/' || req.path.startsWith('/public')) return next();
if (req.path.startsWith('/api')) {
const auth = req.headers.authorization;
if (!auth || auth !== 'Basic ' + Buffer.from('admin:DWSecure2024!').toString('base64')) {
// Also allow no-auth for dashboard GET requests
if (req.method === 'GET') return next();
return res.status(401).json({ error: 'Unauthorized' });
}
}
next();
});
// Get all vendors with credentials
app.get('/api/vendors', async (req, res) => {
try {
const result = await pool.query(`
SELECT vendor_name, vendor_code, login_url, trade_username,
CASE WHEN trade_password IS NOT NULL AND trade_password != '' THEN true ELSE false END AS has_password,
vendor_discount_pct, pricing_unit, pricing_notes, catalog_count,
has_credentials
FROM vendor_registry
WHERE is_active = true
ORDER BY
CASE WHEN trade_password IS NOT NULL AND trade_password != '' THEN 0 ELSE 1 END,
catalog_count DESC
`);
res.json(result.rows);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Get vendors with stored credentials (ready to check)
app.get('/api/vendors/ready', async (req, res) => {
try {
const result = await pool.query(`
SELECT vendor_name, vendor_code, login_url, trade_username,
vendor_discount_pct, pricing_unit, pricing_notes, catalog_count
FROM vendor_registry
WHERE is_active = true
AND trade_username IS NOT NULL AND trade_username != ''
AND trade_password IS NOT NULL AND trade_password != ''
AND login_url IS NOT NULL AND login_url != ''
ORDER BY catalog_count DESC
`);
res.json(result.rows);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Get discount check history
app.get('/api/checks', async (req, res) => {
try {
const result = await pool.query(`
SELECT * FROM vendor_discount_checks
ORDER BY checked_at DESC
LIMIT 100
`);
res.json(result.rows);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Run discount check for a specific vendor
app.post('/api/check/:vendorCode', async (req, res) => {
const { vendorCode } = req.params;
try {
const vendorResult = await pool.query(
`SELECT * FROM vendor_registry WHERE vendor_code = $1`,
[vendorCode]
);
if (vendorResult.rows.length === 0) {
return res.status(404).json({ error: 'Vendor not found' });
}
const vendor = vendorResult.rows[0];
if (!vendor.trade_username || !vendor.trade_password || !vendor.login_url) {
return res.status(400).json({ error: 'Vendor missing credentials or login URL' });
}
res.json({ status: 'started', vendor: vendor.vendor_name, message: 'Discount check initiated' });
// Run async - don't block response
checkVendorDiscount(vendor, pool).catch(err => {
console.error(`Discount check failed for ${vendorCode}:`, err.message);
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Run all ready vendors
app.post('/api/check-all', async (req, res) => {
try {
const result = await pool.query(`
SELECT * FROM vendor_registry
WHERE is_active = true
AND trade_username IS NOT NULL AND trade_username != ''
AND trade_password IS NOT NULL AND trade_password != ''
AND login_url IS NOT NULL AND login_url != ''
ORDER BY catalog_count DESC
`);
const vendors = result.rows;
res.json({ status: 'started', count: vendors.length, vendors: vendors.map(v => v.vendor_name) });
// Run sequentially to avoid overwhelming browser
for (const vendor of vendors) {
try {
await checkVendorDiscount(vendor, pool);
console.log(`✓ Checked ${vendor.vendor_name}`);
} catch (err) {
console.error(`✗ Failed ${vendor.vendor_name}: ${err.message}`);
}
}
console.log('All vendor discount checks complete');
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Update vendor discount manually
app.post('/api/vendors/:vendorCode/discount', async (req, res) => {
const { vendorCode } = req.params;
const { discount_pct, pricing_notes, pricing_unit } = req.body;
try {
const updates = [];
const values = [];
let idx = 1;
if (discount_pct !== undefined) {
updates.push(`vendor_discount_pct = $${idx++}`);
values.push(discount_pct);
}
if (pricing_notes !== undefined) {
updates.push(`pricing_notes = $${idx++}`);
values.push(pricing_notes);
}
if (pricing_unit !== undefined) {
updates.push(`pricing_unit = $${idx++}`);
values.push(pricing_unit);
}
updates.push(`updated_at = NOW()`);
values.push(vendorCode);
const result = await pool.query(
`UPDATE vendor_registry SET ${updates.join(', ')} WHERE vendor_code = $${idx} RETURNING vendor_name, vendor_discount_pct, pricing_notes`,
values
);
if (result.rows.length === 0) return res.status(404).json({ error: 'Vendor not found' });
res.json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Add/update credentials for a vendor
app.post('/api/vendors/:vendorCode/credentials', async (req, res) => {
const { vendorCode } = req.params;
const { login_url, trade_username, trade_password } = req.body;
try {
const result = await pool.query(`
UPDATE vendor_registry
SET login_url = COALESCE($1, login_url),
trade_username = COALESCE($2, trade_username),
trade_password = COALESCE($3, trade_password),
has_credentials = true,
updated_at = NOW()
WHERE vendor_code = $4
RETURNING vendor_name, vendor_code, login_url, trade_username
`, [login_url, trade_username, trade_password, vendorCode]);
if (result.rows.length === 0) return res.status(404).json({ error: 'Vendor not found' });
res.json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Discount summary stats
app.get('/api/stats', async (req, res) => {
try {
const result = await pool.query(`
SELECT
COUNT(*) FILTER (WHERE is_active) AS total_active,
COUNT(*) FILTER (WHERE is_active AND vendor_discount_pct IS NOT NULL) AS has_discount,
COUNT(*) FILTER (WHERE is_active AND has_credentials) AS has_credentials,
COUNT(*) FILTER (WHERE is_active AND trade_password IS NOT NULL AND trade_password != '') AS has_full_creds,
COUNT(*) FILTER (WHERE is_active AND vendor_discount_pct IS NULL) AS missing_discount
FROM vendor_registry
`);
res.json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
async function ensureTable() {
await pool.query(`
CREATE TABLE IF NOT EXISTS vendor_discount_checks (
id SERIAL PRIMARY KEY,
vendor_code TEXT NOT NULL,
vendor_name TEXT,
checked_at TIMESTAMP DEFAULT NOW(),
status TEXT DEFAULT 'pending',
login_success BOOLEAN,
discount_found NUMERIC(5,2),
price_type TEXT,
sample_product TEXT,
sample_list_price NUMERIC(10,2),
sample_net_price NUMERIC(10,2),
calculated_discount NUMERIC(5,2),
screenshot_path TEXT,
notes TEXT,
error TEXT
)
`);
}
ensureTable().then(() => {
app.listen(PORT, '0.0.0.0', () => {
console.log(`Vendor Discount Agent running on port ${PORT}`);
console.log(`Dashboard: http://45.61.58.125:${PORT}`);
});
}).catch(err => {
console.error('Failed to start:', err);
process.exit(1);
});