patx/youtube-clone

import { createReadStream } from 'node:fs';
import { stat } from 'node:fs/promises';
import { extname, join, normalize, sep } from 'node:path';

const TYPES = {
  '.html': 'text/html; charset=utf-8',
  '.js': 'text/javascript; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.svg': 'image/svg+xml',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.webp': 'image/webp',
  '.ico': 'image/x-icon',
  '.woff2': 'font/woff2',
  '.mp4': 'video/mp4',
  '.webm': 'video/webm',
  '.mov': 'video/quicktime',
  '.m4v': 'video/x-m4v',
  '.ogg': 'video/ogg',
  '.mp3': 'audio/mpeg',
};

export const contentType = (path) => TYPES[extname(path).toLowerCase()] || 'application/octet-stream';

/** Resolve a URL path inside `root`, refusing anything that escapes it. */
export function safeJoin(root, urlPath) {
  const decoded = decodeURIComponent(urlPath).replace(/\0/g, '');
  const full = normalize(join(root, decoded));
  if (full !== root && !full.startsWith(root + sep)) return null;
  return full;
}

/**
 * Send a file, honouring a single Range header so <video> can seek.
 * Returns false when the file does not exist.
 */
export async function sendFile(req, res, path, { cacheControl = 'public, max-age=3600' } = {}) {
  let info;
  try {
    info = await stat(path);
    if (!info.isFile()) return false;
  } catch {
    return false;
  }

  const type = contentType(path);
  const etag = `W/"${info.size.toString(16)}-${Math.floor(info.mtimeMs).toString(16)}"`;
  const base = {
    'Content-Type': type,
    'Accept-Ranges': 'bytes',
    'Cache-Control': cacheControl,
    ETag: etag,
    'Last-Modified': info.mtime.toUTCString(),
  };

  if (req.headers['if-none-match'] === etag) {
    res.writeHead(304, base);
    res.end();
    return true;
  }

  const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || '');
  if (range) {
    const [, rawStart, rawEnd] = range;
    let start = rawStart === '' ? info.size - Number(rawEnd) : Number(rawStart);
    let end = rawStart === '' || rawEnd === '' ? info.size - 1 : Number(rawEnd);
    start = Math.max(0, start);
    end = Math.min(info.size - 1, end);
    if (!Number.isFinite(start) || !Number.isFinite(end) || start > end || start >= info.size) {
      res.writeHead(416, { ...base, 'Content-Range': `bytes */${info.size}` });
      res.end();
      return true;
    }
    res.writeHead(206, {
      ...base,
      'Content-Range': `bytes ${start}-${end}/${info.size}`,
      'Content-Length': end - start + 1,
    });
    if (req.method === 'HEAD') return res.end(), true;
    createReadStream(path, { start, end }).pipe(res);
    return true;
  }

  res.writeHead(200, { ...base, 'Content-Length': info.size });
  if (req.method === 'HEAD') return res.end(), true;
  createReadStream(path).pipe(res);
  return true;
}