const express = require('express'); const axios = require('axios'); const logger = require('../utils/logger'); const { db } = require('../database/db'); const path = require('path'); const fs = require('fs').promises; const router = express.Router(); // Middleware to fix CORS for public image serving const fixImageCORS = (req, res, next) => { // Remove credentials header set by global CORS middleware res.removeHeader('Access-Control-Allow-Credentials'); // Set proper CORS for public images res.set({ 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', 'Cross-Origin-Resource-Policy': 'cross-origin' }); next(); }; // Handle OPTIONS preflight requests router.options('/', fixImageCORS, (req, res) => { res.status(204).end(); }); // Proxy external logos to handle CORS issues router.get('/', fixImageCORS, async (req, res) => { const { url } = req.query; if (!url) { return res.status(400).json({ error: 'URL parameter is required' }); } try { // Check if logo is cached const cached = await new Promise((resolve, reject) => { const query = 'SELECT logo_url, local_path FROM logo_cache WHERE logo_url = ? LIMIT 1'; db.get(query, [url], (err, row) => { console.log(`[LogoProxy] SQL: ${query} with url="${url}"`); if (err) { console.error(`[LogoProxy] DB Error:`, err); reject(err); } else { console.log(`[LogoProxy] DB Result:`, row ? JSON.stringify(row) : 'null'); resolve(row); } } ); }); console.log(`[LogoProxy] Cache lookup for ${url}: ${cached ? 'FOUND at ' + cached.local_path : 'NOT FOUND'}`); // If cached, serve from disk if (cached && cached.local_path) { const cachedPath = cached.local_path; try { const fileData = await fs.readFile(cachedPath); const ext = path.extname(cachedPath).toLowerCase(); const contentType = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml' }[ext] || 'image/png'; res.set({ 'Content-Type': contentType, 'Cache-Control': 'public, max-age=2592000' // Cache for 30 days }); return res.send(fileData); } catch (err) { logger.warn('Cached logo file not found, fetching fresh:', err.message); } } // Validate URL const logoUrl = new URL(url); // Fetch the image const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.9', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, maxRedirects: 5 }); // Set appropriate headers const contentType = response.headers['content-type'] || 'image/png'; res.set({ 'Content-Type': contentType, 'Cache-Control': 'public, max-age=86400' // Cache for 24 hours }); // Send the image res.send(response.data); } catch (error) { logger.error('Logo proxy error:', { url, error: error.message, status: error.response?.status }); // Return a 404 or error response res.status(error.response?.status || 500).json({ error: 'Failed to fetch logo', message: error.message }); } }); module.exports = router;