OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / core / core / relay-node / api-eviction-purge.js

75 lines · javascript ref:
import { isValidHexKey } from '../constants.js'
import { formatErr } from '../error-prefixes.js'

export const MAX_PURGE_APP_KEYS = 50
export const EVICTION_PURGE_AUTH_MESSAGE = 'Unauthorized — API key required for /api/eviction/purge'

const EVICTION_PURGE_ROUTES = Object.freeze({
  'POST /api/eviction/purge': Object.freeze({
    kind: 'eviction-purge',
    authMessage: EVICTION_PURGE_AUTH_MESSAGE
  })
})

export function resolveEvictionPurgeRoute (method, path) {
  const route = EVICTION_PURGE_ROUTES[`${method} ${path}`]
  if (!route) return null
  return { ...route }
}

function badRequest (message) {
  return { error: formatErr('BAD_REQUEST', message) }
}

function normalizeFreedBytes (value) {
  return typeof value === 'number' && Number.isFinite(value) && value >= 0
    ? value
    : 0
}

export async function runEvictionPurgeAction ({ body, node }) {
  if (!body || !Array.isArray(body.appKeys) || body.appKeys.length === 0) {
    return { ok: false, status: 400, payload: badRequest('appKeys (non-empty array) required') }
  }
  if (body.appKeys.length > MAX_PURGE_APP_KEYS) {
    return { ok: false, status: 400, payload: badRequest('max 50 appKeys per request') }
  }

  const results = []
  for (const appKey of body.appKeys) {
    if (!isValidHexKey(appKey, 64)) {
      results.push({ appKey, ok: false, error: 'invalid appKey' })
      continue
    }
    try {
      if (!node || typeof node.manualPurge !== 'function') {
        throw new Error('manual purge unavailable')
      }
      const out = await node.manualPurge(appKey)
      results.push({
        appKey,
        ok: true,
        bytes: normalizeFreedBytes(out && out.bytes)
      })
    } catch (err) {
      results.push({
        appKey,
        ok: false,
        error: err && (err.code || err.message) ? (err.code || err.message) : String(err || 'unknown error')
      })
    }
  }

  const purged = results.filter(r => r.ok)
  return {
    ok: true,
    status: 200,
    payload: {
      ok: true,
      purged: purged.length,
      freedBytes: purged.reduce((total, r) => total + normalizeFreedBytes(r.bytes), 0),
      results
    }
  }
}