UNPKG

@appwarden/middleware

Version:

Instantly disable all user interaction with your app deployed on Cloudflare or Vercel

244 lines (235 loc) 6.93 kB
import { UseAppwardenInputSchema, lockPageSlugRefinement } from "./chunk-AP6A6BXL.js"; import { useContentSecurityPolicy } from "./chunk-CRDJAOJ7.js"; import { checkLockStatus } from "./chunk-SFJU4R6J.js"; import { buildLockPageUrl, createHeartbeatConfigError, createRedirect, debug, handleHeartbeatRequest, isHTMLRequest, isHeartbeatRequest, isOnLockPage, parseMergedConfig, printMessage, sanitizeConfigErrors } from "./chunk-Q7ZBW3WZ.js"; import { HEARTBEAT_SERVICES, getErrors } from "./chunk-3OXAKJPA.js"; // src/runners/appwarden-on-cloudflare.ts import { ZodError } from "zod"; // src/schemas/cloudflare.ts import { z } from "zod"; var ConfigFnInputSchema = z.function().args(z.custom()).returns(z.lazy(() => lockPageSlugRefinement(UseAppwardenInputSchema))); // src/utils/middleware.ts var usePipeline = (...initMiddlewares) => { const stack = [...initMiddlewares]; const execute = async (context) => { const runner = async (prevIndex, index) => { if (index === prevIndex) { throw new Error("next() called multiple times"); } if (index >= stack.length) { return; } const middleware = stack[index]; const next = async () => runner(index, index + 1); await middleware(context, next); }; await runner(-1, 0); }; return { execute }; }; // src/utils/cloudflare/insert-errors-logs.ts function htmlEscape(str) { return str.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026"); } var insertErrorLogs = async (context, error) => { const errors = getErrors(error); for (const err of errors) { console.log(printMessage(err)); } const safeErrors = JSON.stringify( errors.map((err) => printMessage(err)) ); const escapedErrors = htmlEscape(safeErrors); return new HTMLRewriter().on("body", { element: (elem) => { elem.setAttribute("data-appwarden-logs", escapedErrors); elem.append( `<script>const logs=JSON.parse(document.body.getAttribute("data-appwarden-logs"));for(const log of logs){console.error(log)}</script>`, { html: true } ); } }).transform(await fetch(context.request.clone())); }; // src/middlewares/use-appwarden.ts var useAppwarden = (input) => async (context, next) => { const { request } = context; let shouldCallNext = true; try { const requestUrl = new URL(request.url); if (request.method.toUpperCase() === "OPTIONS") { return; } if (!isHTMLRequest(request)) { return; } const lockPageSlug = input.multidomainConfig?.[requestUrl.hostname]?.lockPageSlug ?? input.lockPageSlug; if (!lockPageSlug) { return; } if (isOnLockPage(lockPageSlug, request.url)) { return; } const result = await checkLockStatus({ request, appwardenApiToken: input.appwardenApiToken, appwardenApiHostname: input.appwardenApiHostname, debug: input.debug, lockPageSlug, waitUntil: (fn) => context.waitUntil(fn) }); if (result.isLocked) { const lockPageUrl = buildLockPageUrl(lockPageSlug, request.url); context.response = createRedirect(lockPageUrl); shouldCallNext = false; return; } } catch (e) { const message = "Appwarden encountered an unknown error. Please contact Appwarden support at https://appwarden.io/join-community."; console.error( printMessage( e instanceof Error ? `${message} - ${e.message}` : message ) ); } finally { if (shouldCallNext) { await next(); } } }; // src/middlewares/use-fetch-origin.ts var useFetchOrigin = () => async (context, next) => { context.response = await fetch(new Request(context.request)); await next(); }; // src/runners/appwarden-on-cloudflare.ts var RefinedUseAppwardenInputSchema = lockPageSlugRefinement( UseAppwardenInputSchema ); function getAppwardenConfiguration(generatedConfig, config) { return parseMergedConfig( generatedConfig, config, RefinedUseAppwardenInputSchema.parse ); } var appwardenOnCloudflare = (inputFn) => async (request, env, ctx) => { ctx.passThroughOnException(); const requestUrl = new URL(request.url); const requestContext = { env, ctx }; const parsedInput = ConfigFnInputSchema.safeParse(inputFn); if (isHeartbeatRequest(request, requestUrl)) { let configErrors = parsedInput.success ? [] : sanitizeConfigErrors(parsedInput.error); if (parsedInput.success) { try { parsedInput.data(requestContext); } catch (error) { if (error instanceof ZodError) { configErrors = sanitizeConfigErrors(error); } else { configErrors = [ createHeartbeatConfigError( ["config"], "custom", "Appwarden config evaluation failed" ) ]; } } } return handleHeartbeatRequest( request, HEARTBEAT_SERVICES.CLOUDFLARE, configErrors ); } if (!parsedInput.success) { const tempContext = { request, hostname: requestUrl.hostname, response: new Response("Unhandled response"), waitUntil: (fn) => ctx.waitUntil(fn), debug: () => { } // no-op debug for error case }; return insertErrorLogs(tempContext, parsedInput.error); } let input; try { input = parsedInput.data(requestContext); } catch (error) { if (error instanceof ZodError) { const tempContext = { request, hostname: requestUrl.hostname, response: new Response("Unhandled response"), waitUntil: (fn) => ctx.waitUntil(fn), debug: () => { } // no-op debug for error case }; return insertErrorLogs(tempContext, error); } throw error; } const domainDebug = input.multidomainConfig?.[requestUrl.hostname]?.debug ?? input.debug ?? false; const context = { request, hostname: requestUrl.hostname, response: new Response("Unhandled response"), // https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors waitUntil: (fn) => ctx.waitUntil(fn), debug: debug(domainDebug) }; try { const pipeline = [ useAppwarden({ ...input, debug: domainDebug }), useFetchOrigin() ]; const cspConfig = input.multidomainConfig?.[requestUrl.hostname]?.contentSecurityPolicy ?? input.contentSecurityPolicy; if (cspConfig) { pipeline.push(useContentSecurityPolicy(cspConfig)); } await usePipeline(...pipeline).execute(context); } catch (error) { if (error instanceof ZodError) { return insertErrorLogs(context, error); } throw error; } return context.response; }; // src/bundles/cloudflare.ts var createAppwardenMiddleware = appwardenOnCloudflare; export { createAppwardenMiddleware, getAppwardenConfiguration, useContentSecurityPolicy };