@appwarden/middleware
Version:
Instantly disable all user interaction with your app deployed on Cloudflare or Vercel
182 lines (178 loc) • 5.48 kB
JavaScript
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/react-router-cloudflare.ts
import { waitUntil } from "cloudflare:workers";
import { ZodError } from "zod";
// src/schemas/react-router-cloudflare.ts
import { z } from "zod";
var ReactRouterCloudflareConfigSchema = 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/react-router-cloudflare.ts
function getAppwardenConfiguration(generatedConfig, config) {
return parseMergedConfig(
generatedConfig,
config,
ReactRouterCloudflareConfigSchema.parse
);
}
var createConfigEvaluationHeartbeatResponse = (request, configErrors = [
createHeartbeatConfigError(
["config"],
"custom",
"Appwarden config evaluation failed"
)
]) => {
return handleHeartbeatRequest(
request,
HEARTBEAT_SERVICES.CLOUDFLARE_REACT_ROUTER,
configErrors
);
};
var handleReactRouterHeartbeatRequest = (request, configFn) => {
try {
const validationResult = ReactRouterCloudflareConfigSchema.safeParse(configFn());
return handleHeartbeatRequest(
request,
HEARTBEAT_SERVICES.CLOUDFLARE_REACT_ROUTER,
validationResult.success ? [] : sanitizeConfigErrors(validationResult.error)
);
} catch (error) {
return createConfigEvaluationHeartbeatResponse(
request,
error instanceof ZodError ? sanitizeConfigErrors(error) : void 0
);
}
};
function createAppwardenMiddleware(configFn) {
return async (args, next) => {
const startTime = getNowMs();
const { request } = 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)) {
return handleReactRouterHeartbeatRequest(request, configFn);
}
try {
const configInput = configFn();
const validationResult = ReactRouterCloudflareConfigSchema.safeParse(configInput);
if (!validationResult.success) {
console.error(
printMessage(
`Config validation failed: ${validationResult.error.message}`
)
);
return next();
}
config = validationResult.data;
debugFn = debug(config.debug);
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 result = await checkLockStatus({
request,
appwardenApiToken: config.appwardenApiToken,
appwardenApiHostname: config.appwardenApiHostname,
debug: config.debug,
lockPageSlug: config.lockPageSlug,
waitUntil
});
if (result.isLocked) {
const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url);
debugFn(`Website is locked - redirecting to ${lockPageUrl.pathname}`);
throw createRedirect(lockPageUrl);
}
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 response = await next();
const finalResponse = isResponseLike(response) ? await applyCspToResponse(response) : response;
logElapsed(debugFn, startTime);
return finalResponse;
};
}
export {
createAppwardenMiddleware,
getAppwardenConfiguration
};