Clone
// Static file serving for frontend
import { getAssetFromKV } from '@cloudflare/kv-asset-handler';

const MIME_TYPES = {
  'html': 'text/html',
  'js': 'application/javascript',
  'css': 'text/css',
  'png': 'image/png',
  'jpg': 'image/jpeg',
  'jpeg': 'image/jpeg',
  'svg': 'image/svg+xml',
  'ico': 'image/x-icon',
  'json': 'application/json',
  'woff': 'font/woff',
  'woff2': 'font/woff2',
  'ttf': 'font/ttf',
  'eot': 'font/eot'
};

export async function serveStaticAsset(request, env, ctx) {
  const url = new URL(request.url);
  let pathname = url.pathname;
  
  // Default to index.html for root
  if (pathname === '/' || pathname === '') {
    pathname = '/index.html';
  }
  
  // For client-side routing, serve index.html for non-asset paths
  const ext = pathname.split('.').pop();
  if (!MIME_TYPES[ext] && !pathname.startsWith('/api') && !pathname.startsWith('/health')) {
    pathname = '/index.html';
  }
  
  try {
    // Try to get the asset from R2 bucket (FRONTEND_ASSETS)
    if (env.FRONTEND_ASSETS) {
      const object = await env.FRONTEND_ASSETS.get(pathname.slice(1)); // Remove leading slash
      
      if (object) {
        const headers = new Headers();
        headers.set('Content-Type', MIME_TYPES[ext] || 'application/octet-stream');
        headers.set('Cache-Control', 'public, max-age=3600');
        
        return new Response(object.body, {
          status: 200,
          headers
        });
      }
    }
    
    // Fallback to KV if configured (for Cloudflare Pages migration)
    if (env.__STATIC_CONTENT) {
      return await getAssetFromKV(
        {
          request,
          waitUntil: ctx.waitUntil.bind(ctx),
        },
        {
          ASSET_NAMESPACE: env.__STATIC_CONTENT,
          ASSET_MANIFEST: JSON.parse(env.__STATIC_CONTENT_MANIFEST || '{}'),
        }
      );
    }
    
    return null;
  } catch (e) {
    console.error('Static asset error:', e);
    return null;
  }
}