nuxt-kql
Version:
Kirby's Query Language API for Nuxt
114 lines (113 loc) • 3.7 kB
JavaScript
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"
});
}
});