Files
microdao-daarion/apps/web/src/app/api/assets/[...path]/route.ts
Apple f19d5de52b fix: Add HEAD method support and fix proxy URL in Next.js assets route
- Add HEAD method handler in Next.js route
- Fix proxy URL to use correct city-service endpoint
- Handle HEAD requests properly (return headers only)
- This should fix 405 errors when browser checks image availability
2025-12-02 08:50:13 -08:00

98 lines
3.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
/**
* API Proxy for MinIO assets
*
* Proxies requests to MinIO through city-service or directly to MinIO.
* This allows serving assets from https://daarion.space/api/assets/...
* instead of requiring assets.daarion.space DNS setup.
*/
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'http://minio:9000';
const ASSETS_BUCKET = process.env.ASSETS_BUCKET || 'daarion-assets';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> }
) {
return handleAssetRequest(request, params);
}
export async function HEAD(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> }
) {
return handleAssetRequest(request, params);
}
async function handleAssetRequest(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> }
) {
try {
const { path } = await params;
const objectPath = path.join('/');
// Construct MinIO URL
// Format: /api/assets/microdao/logo/2025/12/02/abc123.png
// Should proxy to: http://minio:9000/daarion-assets/microdao/logo/2025/12/02/abc123.png
const minioUrl = `${MINIO_ENDPOINT}/${ASSETS_BUCKET}/${objectPath}`;
// Proxy through city-service
// INTERNAL_API_URL is like http://daarion-city-service:7001
// We need: http://daarion-city-service:7001/city/assets/proxy/...
const cityServiceUrl = process.env.INTERNAL_API_URL || 'http://daarion-city-service:7001';
const proxyUrl = `${cityServiceUrl}/city/assets/proxy/${objectPath}`;
// Use the same HTTP method as the incoming request (GET or HEAD)
const method = request.method as 'GET' | 'HEAD';
// Try to fetch from city-service proxy
const response = await fetch(proxyUrl, {
method: method,
headers: {
'Accept': request.headers.get('Accept') || '*/*',
},
});
if (!response.ok) {
return new NextResponse('Asset not found', { status: 404 });
}
// Get content type from response
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
const contentLength = response.headers.get('Content-Length');
// For HEAD requests, return headers only
if (method === 'HEAD') {
return new NextResponse(null, {
status: 200,
headers: {
'Content-Type': contentType,
'Content-Length': contentLength || '0',
'Cache-Control': 'public, max-age=86400, immutable',
'Access-Control-Allow-Origin': '*',
},
});
}
// For GET requests, return file data
const blob = await response.blob();
const buffer = await blob.arrayBuffer();
return new NextResponse(buffer, {
status: 200,
headers: {
'Content-Type': contentType,
'Content-Length': contentLength || buffer.byteLength.toString(),
'Cache-Control': 'public, max-age=86400, immutable',
'Access-Control-Allow-Origin': '*',
},
});
} catch (error) {
console.error('[assets-proxy] Error:', error);
return new NextResponse('Internal Server Error', { status: 500 });
}
}