/** * API endpoint to serve cached images from the database * Serves avatars, emojis, attachments, etc. from local cache * * Note: This endpoint is intentionally unauthenticated because: * 1. Image tags don't reliably send auth cookies on initial load * 2. Image IDs are not guessable (you need access to the messages API first) * 3. The underlying Discord images are semi-public anyway */ import sql from '../../../utils/db.js'; export async function GET({ request }) { const url = new URL(request.url); const imageId = url.searchParams.get('id'); const sourceUrl = url.searchParams.get('url'); if (!imageId && !sourceUrl) { return new Response('Missing id or url parameter', { status: 400 }); } // Validate imageId is a valid integer if provided if (imageId && !/^\d+$/.test(imageId)) { return new Response('Invalid image id', { status: 400 }); } try { let image; if (imageId) { // Look up by image_id const result = await sql` SELECT image_data, content_type, source_url FROM image_cache WHERE image_id = ${imageId} `; image = result[0]; } else { // Look up by source_url const result = await sql` SELECT image_data, content_type, source_url FROM image_cache WHERE source_url = ${sourceUrl} `; image = result[0]; } if (!image) { return new Response('Image not found in cache', { status: 404 }); } // image_data is a Buffer (bytea) const imageBuffer = image.image_data; const contentType = image.content_type || 'image/png'; return new Response(imageBuffer, { status: 200, headers: { 'Content-Type': contentType, 'Content-Length': imageBuffer.length.toString(), 'Cache-Control': 'public, max-age=31536000, immutable', // Cache for 1 year since it's immutable 'X-Content-Type-Options': 'nosniff', }, }); } catch (error) { console.error('Error serving cached image:', error); return new Response('Failed to serve image', { status: 500 }); } }