feat: store node logo/favicon in postgres instead of S3
- Add logoData and faviconData columns to nodes table - Create /api/admin/node/upload endpoint for direct DB storage - Create /api/node/logo and /api/node/favicon endpoints to serve images - Update admin page to use new upload endpoint - Remove dependency on admin's personal S3 storage for node assets
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, nodes } from '@/db';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
if (!db) {
|
||||
return NextResponse.json({ error: 'Database not available' }, { status: 500 });
|
||||
}
|
||||
|
||||
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
|
||||
// 1. Try exact match
|
||||
let node = await db.query.nodes.findFirst({
|
||||
where: eq(nodes.domain, domain),
|
||||
});
|
||||
|
||||
// 2. Fallback: If not found, check if there is exactly ONE node in the system
|
||||
if (!node) {
|
||||
const allNodes = await db.query.nodes.findMany({ limit: 2 });
|
||||
if (allNodes.length === 1) {
|
||||
node = allNodes[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have favicon data
|
||||
if (!node?.faviconData) {
|
||||
return NextResponse.json({ error: 'Favicon not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Parse the data URL to extract MIME type and base64 data
|
||||
const dataUrl = node.faviconData;
|
||||
const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
|
||||
|
||||
if (!match) {
|
||||
// If not a proper data URL, try to serve as-is (backward compatibility)
|
||||
return NextResponse.json({ error: 'Invalid favicon data' }, { status: 500 });
|
||||
}
|
||||
|
||||
const mimeType = match[1];
|
||||
const base64Data = match[2];
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
|
||||
// Return the image with proper headers
|
||||
return new NextResponse(buffer, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': mimeType,
|
||||
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
|
||||
'Content-Length': buffer.length.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Favicon serve error:', error);
|
||||
return NextResponse.json({ error: 'Failed to serve favicon' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user