OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-booking / lib / response.js

85 lines · javascript ref:
/**
 * Response Helpers — standardized HTTP response utilities.
 * Same pattern as hypernote.
 */

export function json (res, data, status = 200) {
  res.statusCode = status
  res.setHeader('Content-Type', 'application/json')
  res.setHeader('X-Content-Type-Options', 'nosniff')
  res.end(JSON.stringify(data))
}

export function html (res, content, status = 200) {
  res.statusCode = status
  res.setHeader('Content-Type', 'text/html')
  res.setHeader('X-Frame-Options', 'DENY')
  res.setHeader('X-Content-Type-Options', 'nosniff')
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin')
  res.end(content)
}

export function text (res, content, status = 200) {
  res.statusCode = status
  res.setHeader('Content-Type', 'text/plain')
  res.end(content)
}

export function error (res, message, status = 500) {
  res.statusCode = status
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify({ error: message }))
}

export function notFound (res, message = 'Not found') {
  error(res, message, 404)
}

export function badRequest (res, message = 'Bad request') {
  error(res, message, 400)
}

export function unauthorized (res, message = 'Unauthorized') {
  res.statusCode = 401
  res.setHeader('WWW-Authenticate', 'Bearer realm="hyper-booking"')
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify({ error: message }))
}

const MAX_BODY_SIZE = 1024 * 1024 // 1MB

export async function readBody (req) {
  let body = ''
  let size = 0
  for await (const chunk of req) {
    size += chunk.length
    if (size > MAX_BODY_SIZE) {
      throw new Error('Request body too large (max 1MB)')
    }
    body += chunk.toString()
  }
  return body
}

export async function readJson (req) {
  const body = await readBody(req)
  if (!body) return {}
  return JSON.parse(body)
}

export function escapeHtml (text) {
  if (!text) return ''
  return String(text)
    .replace(/&/g, '&')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}

export function redirect (res, url, status = 302) {
  res.statusCode = status
  res.setHeader('Location', url)
  res.end()
}