UNPKG

@appwarden/middleware

Version:

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

181 lines (177 loc) 5.62 kB
import { applyContentSecurityPolicyToResponse, isResponseLike } from "../chunk-NWGDCGLB.js"; import "../chunk-CRDJAOJ7.js"; import { getNowMs, logElapsed } from "../chunk-G6BMPIYD.js"; import { checkLockStatus } from "../chunk-SFJU4R6J.js"; import { buildLockPageUrl, createHeartbeatConfigError, createRedirect, debug, handleHeartbeatRequest, isHTMLRequest, isHeartbeatRequest, isOnLockPage, parseMergedConfig, printMessage, sanitizeConfigErrors } from "../chunk-Q7ZBW3WZ.js"; import { AppwardenApiHostnameSchema, AppwardenApiTokenSchema, BooleanSchema, HEARTBEAT_SERVICES, UseCSPInputSchema, ValidLockPageSlugSchema } from "../chunk-3OXAKJPA.js"; // src/adapters/tanstack-start-cloudflare.ts import { waitUntil } from "cloudflare:workers"; import { ZodError } from "zod"; // src/schemas/tanstack-start-cloudflare.ts import { z } from "zod"; var TanStackStartCloudflareConfigSchema = z.object({ /** The slug/path of the lock page to redirect to when the site is locked */ lockPageSlug: ValidLockPageSlugSchema, /** The Appwarden API token for authentication */ appwardenApiToken: AppwardenApiTokenSchema, /** Optional custom API hostname (defaults to https://api.appwarden.io) */ appwardenApiHostname: AppwardenApiHostnameSchema.optional(), /** Enable debug logging */ debug: BooleanSchema.default(false), /** Optional Content Security Policy configuration */ contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional() }); // src/adapters/tanstack-start-cloudflare.ts function getAppwardenConfiguration(generatedConfig, config) { return parseMergedConfig( generatedConfig, config, TanStackStartCloudflareConfigSchema.parse ); } var createTanStackHeartbeatResponse = (request, configFn) => { try { const validationResult = TanStackStartCloudflareConfigSchema.safeParse(configFn()); return handleHeartbeatRequest( request, HEARTBEAT_SERVICES.CLOUDFLARE_TANSTACK_START, validationResult.success ? [] : sanitizeConfigErrors(validationResult.error) ); } catch (error) { return handleHeartbeatRequest( request, HEARTBEAT_SERVICES.CLOUDFLARE_TANSTACK_START, error instanceof ZodError ? sanitizeConfigErrors(error) : [ createHeartbeatConfigError( ["config"], "custom", "Appwarden config evaluation failed" ) ] ); } }; function createAppwardenMiddleware(configFn) { const middleware = async (args) => { const startTime = getNowMs(); const { request, next } = args; let config; let debugFn; const requestUrl = new URL(request.url); const applyCspToResponse = async (response2) => { if (!config.contentSecurityPolicy || !isResponseLike(response2)) { return response2; } try { return await applyContentSecurityPolicyToResponse({ request, response: response2, hostname: requestUrl.hostname, waitUntil, debug: debugFn, contentSecurityPolicy: config.contentSecurityPolicy }); } catch (error) { console.error( printMessage( `Failed to apply content security policy: ${error instanceof Error ? error.message : String(error)}` ) ); return response2; } }; if (isHeartbeatRequest(request, requestUrl)) { throw createTanStackHeartbeatResponse(request, configFn); } try { const rawConfig = configFn(); const validationResult = TanStackStartCloudflareConfigSchema.safeParse(rawConfig); if (!validationResult.success) { console.error( printMessage( `Config validation failed: ${validationResult.error.message}` ) ); return next(); } config = validationResult.data; debugFn = debug(config.debug ?? false); const isHTML = isHTMLRequest(request); debugFn( `Appwarden middleware invoked for ${requestUrl.pathname}`, `isHTML: ${isHTML}` ); if (!isHTML) { return next(); } if (isOnLockPage(config.lockPageSlug, request.url)) { debugFn("Already on lock page - skipping lock status check"); } else { const lockStatus = await checkLockStatus({ request, appwardenApiToken: config.appwardenApiToken, appwardenApiHostname: config.appwardenApiHostname, debug: config.debug, lockPageSlug: config.lockPageSlug, waitUntil }); if (lockStatus.isLocked) { const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url); debugFn(`Website is locked - redirecting to ${lockPageUrl.pathname}`); const redirectResponse = await applyCspToResponse( createRedirect(lockPageUrl) ); logElapsed(debugFn, startTime); throw redirectResponse; } debugFn("Website is unlocked"); } } catch (error) { if (isResponseLike(error)) { throw error; } console.error( printMessage( `Unhandled error: ${error instanceof Error ? error.message : String(error)}` ) ); return next(); } const result = await next(); const response = isResponseLike(result.response) ? await applyCspToResponse(result.response) : result.response; logElapsed(debugFn, startTime); return response === result.response ? result : { ...result, response }; }; return middleware; } export { createAppwardenMiddleware, getAppwardenConfiguration };