@appwarden/middleware
Version:
Instantly disable all user interaction with your app deployed on Cloudflare or Vercel
181 lines (177 loc) • 5.62 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/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
};