UNPKG

nuxt-kql

Version:

Kirby's Query Language API for Nuxt

114 lines (113 loc) 3.7 kB
import { defineCachedFunction, useRuntimeConfig } from "#imports"; import { consola } from "consola"; import { destr } from "destr"; import { createError, defineEventHandler, getRouterParam, readBody, setResponseHeader, setResponseStatus, splitCookiesString } from "h3"; import { base64ToUint8Array, uint8ArrayToBase64, uint8ArrayToString } from "uint8array-extras"; import { createAuthHeader } from "../utils.js"; const ignoredResponseHeaders = /* @__PURE__ */ new Set([ // https://github.com/h3js/h3/blob/fe9800bbbe9bda2972cc5d11db7353f4ab70f0ba/src/utils/proxy.ts#L97 "content-encoding", "content-length", // Reduce information leakage "server", "x-powered-by" ]); export default defineEventHandler(async (event) => { const kql = useRuntimeConfig(event).kql; const body = await readBody(event); const key = decodeURIComponent(getRouterParam(event, "key")); const isQueryRequest = key.startsWith("$kql"); const fetcher = async (event2, { key: key2, query, path, headers, method, body: body2 }) => { const isQueryRequest2 = key2.startsWith("$kql"); const response = await globalThis.$fetch.raw(isQueryRequest2 ? kql.prefix : path, { responseType: "arrayBuffer", ignoreResponseError: true, baseURL: kql.url, ...isQueryRequest2 ? { method: "POST", body: query } : { query, method, body: body2 }, headers: { ...headers, ...createAuthHeader(kql) } }); const dataArray = new Uint8Array(response._data ?? []); const data = uint8ArrayToBase64(dataArray); return { status: response.status, statusText: response.statusText, headers: [...response.headers.entries()], data }; }; const cachedFetcher = defineCachedFunction(fetcher, { name: "nuxt-kql", base: kql.server.storage, swr: kql.server.swr, maxAge: kql.server.maxAge, getKey: (event2, { key: key2 }) => key2 }); if (isQueryRequest) { if (!body.query?.query) { throw createError({ statusCode: 400, statusMessage: "KQL query is empty" }); } } else { if (body.path && new URL(body.path, "http://localhost").origin !== "http://localhost") { throw createError({ statusCode: 400, statusMessage: "Absolute URLs are not allowed" }); } } try { const response = kql.server.cache && body.cache ? await cachedFetcher(event, { key, ...body }) : await fetcher(event, { key, ...body }); const dataArray = base64ToUint8Array(response.data); if (response.status >= 400 && response.status < 600) { if (isQueryRequest) { consola.error(`Failed KQL query "${body.query?.query}" (...) with status code ${response.status}: `, destr( uint8ArrayToString(dataArray) )); if (kql.server.verboseErrors) consola.log("Full KQL query request:", body.query); } else { consola.error(`Failed ${(body.method || "get").toUpperCase()} request to "${body.path}"`); } } const cookies = []; for (const [key2, value] of response.headers) { if (ignoredResponseHeaders.has(key2)) continue; if (key2 === "set-cookie") { cookies.push(...splitCookiesString(value)); continue; } setResponseHeader(event, key2, value); } if (cookies.length > 0) setResponseHeader(event, "set-cookie", cookies); setResponseStatus(event, response.status, response.statusText); return dataArray; } catch (error) { consola.error(error); throw createError({ statusCode: 503, statusMessage: "Service Unavailable" }); } });