@appwarden/middleware 3.2.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -22
- package/chunk-3MKVGKH7.js +81 -0
- package/chunk-G4RRFNHY.js +332 -0
- package/chunk-HCGLR3Z3.js +97 -0
- package/{chunk-ZX5QO4Y2.js → chunk-QVRWMYGL.js} +31 -58
- package/{chunk-COV6SHCD.js → chunk-U3T4R5KZ.js} +2 -4
- package/chunk-YBWFEBZC.js +53 -0
- package/cloudflare/astro.d.ts +4 -0
- package/cloudflare/astro.js +54 -11
- package/cloudflare/nextjs.d.ts +4 -0
- package/cloudflare/nextjs.js +57 -10
- package/cloudflare/react-router.d.ts +5 -0
- package/cloudflare/react-router.js +54 -11
- package/cloudflare/tanstack-start.d.ts +5 -0
- package/cloudflare/tanstack-start.js +54 -11
- package/cloudflare-36BOGAYU.js +28 -0
- package/cloudflare.d.ts +440 -8
- package/cloudflare.js +44 -54
- package/index.d.ts +2 -1
- package/index.js +5 -4
- package/package.json +8 -3
- package/{use-content-security-policy-DjRTjIpm.d.ts → use-content-security-policy-DUYpyUPy.d.ts} +1 -18
- package/use-content-security-policy-Dvc-oObb.d.ts +17 -0
- package/vercel.d.ts +688 -0
- package/vercel.js +82 -13
- package/chunk-A5XGYLYS.js +0 -196
- package/chunk-L5EQIJZB.js +0 -54
- package/chunk-MDODCAA3.js +0 -232
package/cloudflare/astro.js
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useContentSecurityPolicy
|
|
3
|
+
} from "../chunk-YBWFEBZC.js";
|
|
1
4
|
import {
|
|
2
5
|
validateConfig
|
|
3
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-U3T4R5KZ.js";
|
|
4
7
|
import {
|
|
5
8
|
checkLockStatus
|
|
6
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-3MKVGKH7.js";
|
|
7
10
|
import {
|
|
8
|
-
AppwardenApiTokenSchema,
|
|
9
|
-
BooleanSchema,
|
|
10
11
|
TEMPORARY_REDIRECT_STATUS,
|
|
11
12
|
buildLockPageUrl,
|
|
12
13
|
createRedirect,
|
|
14
|
+
debug,
|
|
13
15
|
isOnLockPage
|
|
14
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-QVRWMYGL.js";
|
|
15
17
|
import {
|
|
16
|
-
|
|
18
|
+
UseCSPInputSchema,
|
|
19
|
+
isHTMLRequest
|
|
20
|
+
} from "../chunk-HCGLR3Z3.js";
|
|
21
|
+
import {
|
|
22
|
+
AppwardenApiTokenSchema,
|
|
23
|
+
BooleanSchema,
|
|
17
24
|
printMessage
|
|
18
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-G4RRFNHY.js";
|
|
19
26
|
|
|
20
27
|
// src/schemas/astro-cloudflare.ts
|
|
21
28
|
import { z } from "zod";
|
|
@@ -27,12 +34,16 @@ var AstroCloudflareConfigSchema = z.object({
|
|
|
27
34
|
/** Optional custom API hostname (defaults to https://api.appwarden.io) */
|
|
28
35
|
appwardenApiHostname: z.string().optional(),
|
|
29
36
|
/** Enable debug logging */
|
|
30
|
-
debug: BooleanSchema.default(false)
|
|
37
|
+
debug: BooleanSchema.default(false),
|
|
38
|
+
/** Optional Content Security Policy configuration */
|
|
39
|
+
contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional()
|
|
31
40
|
});
|
|
32
41
|
|
|
33
42
|
// src/adapters/astro-cloudflare.ts
|
|
43
|
+
var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
34
44
|
function createAppwardenMiddleware(configFn) {
|
|
35
45
|
return async (context, next) => {
|
|
46
|
+
const startTime = getNowMs();
|
|
36
47
|
const { request } = context;
|
|
37
48
|
const locals = context.locals;
|
|
38
49
|
try {
|
|
@@ -45,15 +56,23 @@ function createAppwardenMiddleware(configFn) {
|
|
|
45
56
|
);
|
|
46
57
|
return next();
|
|
47
58
|
}
|
|
48
|
-
|
|
59
|
+
const config = configFn(runtime);
|
|
60
|
+
const debugFn = debug(config.debug ?? false);
|
|
61
|
+
const requestUrl = new URL(request.url);
|
|
62
|
+
const isHTML = isHTMLRequest(request);
|
|
63
|
+
debugFn(
|
|
64
|
+
`Appwarden middleware invoked for ${requestUrl.pathname}`,
|
|
65
|
+
`isHTML: ${isHTML}`
|
|
66
|
+
);
|
|
67
|
+
if (!isHTML) {
|
|
49
68
|
return next();
|
|
50
69
|
}
|
|
51
|
-
const config = configFn(runtime);
|
|
52
70
|
const hasError = validateConfig(config, AstroCloudflareConfigSchema);
|
|
53
71
|
if (hasError) {
|
|
54
72
|
return next();
|
|
55
73
|
}
|
|
56
74
|
if (isOnLockPage(config.lockPageSlug, request.url)) {
|
|
75
|
+
debugFn("Already on lock page - skipping");
|
|
57
76
|
return next();
|
|
58
77
|
}
|
|
59
78
|
const result = await checkLockStatus({
|
|
@@ -66,6 +85,7 @@ function createAppwardenMiddleware(configFn) {
|
|
|
66
85
|
});
|
|
67
86
|
if (result.isLocked) {
|
|
68
87
|
const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url);
|
|
88
|
+
debugFn(`Site is locked - redirecting to ${lockPageUrl.pathname}`);
|
|
69
89
|
if (context.redirect) {
|
|
70
90
|
return context.redirect(
|
|
71
91
|
lockPageUrl.toString(),
|
|
@@ -74,7 +94,30 @@ function createAppwardenMiddleware(configFn) {
|
|
|
74
94
|
}
|
|
75
95
|
return createRedirect(lockPageUrl);
|
|
76
96
|
}
|
|
77
|
-
|
|
97
|
+
debugFn("Site is unlocked to origin");
|
|
98
|
+
const response = await next();
|
|
99
|
+
if (config.contentSecurityPolicy) {
|
|
100
|
+
debugFn("Applying CSP middleware");
|
|
101
|
+
const cspContext = {
|
|
102
|
+
request,
|
|
103
|
+
response,
|
|
104
|
+
hostname: requestUrl.hostname,
|
|
105
|
+
waitUntil: (fn) => runtime.ctx.waitUntil(fn),
|
|
106
|
+
debug: debugFn
|
|
107
|
+
};
|
|
108
|
+
await useContentSecurityPolicy(config.contentSecurityPolicy)(
|
|
109
|
+
cspContext,
|
|
110
|
+
async () => {
|
|
111
|
+
}
|
|
112
|
+
// no-op next
|
|
113
|
+
);
|
|
114
|
+
const elapsed2 = Math.round(getNowMs() - startTime);
|
|
115
|
+
debugFn(`Middleware executed in ${elapsed2}ms`);
|
|
116
|
+
return cspContext.response;
|
|
117
|
+
}
|
|
118
|
+
const elapsed = Math.round(getNowMs() - startTime);
|
|
119
|
+
debugFn(`Middleware executed in ${elapsed}ms`);
|
|
120
|
+
return response;
|
|
78
121
|
} catch (error) {
|
|
79
122
|
if (error instanceof Response) {
|
|
80
123
|
throw error;
|
package/cloudflare/nextjs.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
2
|
+
import { U as UseCSPInput } from '../use-content-security-policy-DUYpyUPy.js';
|
|
3
|
+
import 'zod';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Cloudflare runtime context provided by @opennextjs/cloudflare.
|
|
@@ -20,6 +22,8 @@ interface NextJsCloudflareAppwardenConfig {
|
|
|
20
22
|
appwardenApiHostname?: string;
|
|
21
23
|
/** Enable debug logging */
|
|
22
24
|
debug?: boolean;
|
|
25
|
+
/** Optional Content Security Policy configuration (headers only, no HTML rewriting; `{{nonce}}` placeholders are not supported) */
|
|
26
|
+
contentSecurityPolicy?: UseCSPInput;
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
29
|
* Configuration function that receives the Cloudflare runtime and returns the config.
|
package/cloudflare/nextjs.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateConfig
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-U3T4R5KZ.js";
|
|
4
4
|
import {
|
|
5
5
|
checkLockStatus
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-3MKVGKH7.js";
|
|
7
7
|
import {
|
|
8
|
-
AppwardenApiTokenSchema,
|
|
9
|
-
BooleanSchema,
|
|
10
8
|
TEMPORARY_REDIRECT_STATUS,
|
|
11
9
|
buildLockPageUrl,
|
|
10
|
+
debug,
|
|
12
11
|
isOnLockPage
|
|
13
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-QVRWMYGL.js";
|
|
13
|
+
import {
|
|
14
|
+
UseCSPInputSchema,
|
|
15
|
+
isHTMLRequest
|
|
16
|
+
} from "../chunk-HCGLR3Z3.js";
|
|
14
17
|
import {
|
|
15
|
-
|
|
18
|
+
AppwardenApiTokenSchema,
|
|
19
|
+
BooleanSchema,
|
|
16
20
|
printMessage
|
|
17
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-G4RRFNHY.js";
|
|
18
22
|
|
|
19
23
|
// src/adapters/nextjs-cloudflare.ts
|
|
20
24
|
import {
|
|
@@ -23,6 +27,17 @@ import {
|
|
|
23
27
|
|
|
24
28
|
// src/schemas/nextjs-cloudflare.ts
|
|
25
29
|
import { z } from "zod";
|
|
30
|
+
var NextJsCloudflareCSPInputSchema = UseCSPInputSchema.refine(
|
|
31
|
+
(values) => {
|
|
32
|
+
if (!values.directives) return true;
|
|
33
|
+
const serialized = JSON.stringify(values.directives);
|
|
34
|
+
return !serialized.includes("{{nonce}}");
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: ["directives"],
|
|
38
|
+
message: "Nonce-based CSP is not supported in the Next.js Cloudflare adapter. Remove '{{nonce}}' placeholders from your CSP directives, as this adapter does not inject nonces into HTML."
|
|
39
|
+
}
|
|
40
|
+
);
|
|
26
41
|
var NextJsCloudflareConfigSchema = z.object({
|
|
27
42
|
/** The slug/path of the lock page to redirect to when the site is locked */
|
|
28
43
|
lockPageSlug: z.string(),
|
|
@@ -31,24 +46,36 @@ var NextJsCloudflareConfigSchema = z.object({
|
|
|
31
46
|
/** Optional custom API hostname (defaults to https://api.appwarden.io) */
|
|
32
47
|
appwardenApiHostname: z.string().optional(),
|
|
33
48
|
/** Enable debug logging */
|
|
34
|
-
debug: BooleanSchema.default(false)
|
|
49
|
+
debug: BooleanSchema.default(false),
|
|
50
|
+
/** Optional Content Security Policy configuration (headers only, no HTML rewriting; '{{nonce}}' is not supported) */
|
|
51
|
+
contentSecurityPolicy: z.lazy(() => NextJsCloudflareCSPInputSchema).optional()
|
|
35
52
|
});
|
|
36
53
|
|
|
37
54
|
// src/adapters/nextjs-cloudflare.ts
|
|
55
|
+
var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
38
56
|
function createAppwardenMiddleware(configFn) {
|
|
39
57
|
return async (request, _event) => {
|
|
58
|
+
const startTime = getNowMs();
|
|
40
59
|
try {
|
|
41
60
|
const { getCloudflareContext } = await import("@opennextjs/cloudflare");
|
|
42
61
|
const { env, ctx } = await getCloudflareContext();
|
|
43
|
-
|
|
62
|
+
const config = configFn({ env, ctx });
|
|
63
|
+
const debugFn = debug(config.debug ?? false);
|
|
64
|
+
const requestUrl = new URL(request.url);
|
|
65
|
+
const isHTML = isHTMLRequest(request);
|
|
66
|
+
debugFn(
|
|
67
|
+
`Appwarden middleware invoked for ${requestUrl.pathname}`,
|
|
68
|
+
`isHTML: ${isHTML}`
|
|
69
|
+
);
|
|
70
|
+
if (!isHTML) {
|
|
44
71
|
return NextResponse.next();
|
|
45
72
|
}
|
|
46
|
-
const config = configFn({ env, ctx });
|
|
47
73
|
const hasError = validateConfig(config, NextJsCloudflareConfigSchema);
|
|
48
74
|
if (hasError) {
|
|
49
75
|
return NextResponse.next();
|
|
50
76
|
}
|
|
51
77
|
if (isOnLockPage(config.lockPageSlug, request.url)) {
|
|
78
|
+
debugFn("Already on lock page - skipping");
|
|
52
79
|
return NextResponse.next();
|
|
53
80
|
}
|
|
54
81
|
const result = await checkLockStatus({
|
|
@@ -61,8 +88,28 @@ function createAppwardenMiddleware(configFn) {
|
|
|
61
88
|
});
|
|
62
89
|
if (result.isLocked) {
|
|
63
90
|
const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url);
|
|
91
|
+
debugFn(`Site is locked - redirecting to ${lockPageUrl.pathname}`);
|
|
64
92
|
return NextResponse.redirect(lockPageUrl, TEMPORARY_REDIRECT_STATUS);
|
|
65
93
|
}
|
|
94
|
+
debugFn("Site is unlocked");
|
|
95
|
+
if (config.contentSecurityPolicy && config.contentSecurityPolicy.mode !== "disabled") {
|
|
96
|
+
debugFn(
|
|
97
|
+
`Applying CSP headers in ${config.contentSecurityPolicy.mode} mode`
|
|
98
|
+
);
|
|
99
|
+
const { makeCSPHeader } = await import("../cloudflare-36BOGAYU.js");
|
|
100
|
+
const [headerName, headerValue] = makeCSPHeader(
|
|
101
|
+
"",
|
|
102
|
+
config.contentSecurityPolicy.directives,
|
|
103
|
+
config.contentSecurityPolicy.mode
|
|
104
|
+
);
|
|
105
|
+
const response = NextResponse.next();
|
|
106
|
+
response.headers.set(headerName, headerValue);
|
|
107
|
+
const elapsed2 = Math.round(getNowMs() - startTime);
|
|
108
|
+
debugFn(`Middleware executed in ${elapsed2}ms`);
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
111
|
+
const elapsed = Math.round(getNowMs() - startTime);
|
|
112
|
+
debugFn(`Middleware executed in ${elapsed}ms`);
|
|
66
113
|
return NextResponse.next();
|
|
67
114
|
} catch (error) {
|
|
68
115
|
console.error(
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { U as UseCSPInput } from '../use-content-security-policy-DUYpyUPy.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Cloudflare context provided by React Router on Cloudflare Workers.
|
|
3
6
|
* This is the shape of `context.cloudflare` in React Router loaders/actions.
|
|
@@ -23,6 +26,8 @@ interface ReactRouterAppwardenConfig {
|
|
|
23
26
|
appwardenApiHostname?: string;
|
|
24
27
|
/** Enable debug logging */
|
|
25
28
|
debug?: boolean;
|
|
29
|
+
/** Optional Content Security Policy configuration */
|
|
30
|
+
contentSecurityPolicy?: UseCSPInput;
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
33
|
* Configuration function that receives the Cloudflare context and returns the config.
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useContentSecurityPolicy
|
|
3
|
+
} from "../chunk-YBWFEBZC.js";
|
|
1
4
|
import {
|
|
2
5
|
validateConfig
|
|
3
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-U3T4R5KZ.js";
|
|
4
7
|
import {
|
|
5
8
|
checkLockStatus
|
|
6
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-3MKVGKH7.js";
|
|
7
10
|
import {
|
|
8
|
-
AppwardenApiTokenSchema,
|
|
9
|
-
BooleanSchema,
|
|
10
11
|
buildLockPageUrl,
|
|
11
12
|
createRedirect,
|
|
13
|
+
debug,
|
|
12
14
|
isOnLockPage
|
|
13
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-QVRWMYGL.js";
|
|
14
16
|
import {
|
|
15
|
-
|
|
17
|
+
UseCSPInputSchema,
|
|
18
|
+
isHTMLRequest
|
|
19
|
+
} from "../chunk-HCGLR3Z3.js";
|
|
20
|
+
import {
|
|
21
|
+
AppwardenApiTokenSchema,
|
|
22
|
+
BooleanSchema,
|
|
16
23
|
printMessage
|
|
17
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-G4RRFNHY.js";
|
|
18
25
|
|
|
19
26
|
// src/schemas/react-router-cloudflare.ts
|
|
20
27
|
import { z } from "zod";
|
|
@@ -26,13 +33,16 @@ var ReactRouterCloudflareConfigSchema = z.object({
|
|
|
26
33
|
/** Optional custom API hostname (defaults to https://api.appwarden.io) */
|
|
27
34
|
appwardenApiHostname: z.string().optional(),
|
|
28
35
|
/** Enable debug logging */
|
|
29
|
-
debug: BooleanSchema.default(false)
|
|
36
|
+
debug: BooleanSchema.default(false),
|
|
37
|
+
/** Optional Content Security Policy configuration */
|
|
38
|
+
contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional()
|
|
30
39
|
});
|
|
31
40
|
|
|
32
41
|
// src/adapters/react-router-cloudflare.ts
|
|
33
42
|
var cloudflareContextSymbol = /* @__PURE__ */ Symbol.for(
|
|
34
43
|
"@appwarden/middleware:cloudflare"
|
|
35
44
|
);
|
|
45
|
+
var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
36
46
|
function getCloudflareContext(context) {
|
|
37
47
|
if (context?.cloudflare) {
|
|
38
48
|
return context.cloudflare;
|
|
@@ -50,6 +60,7 @@ function getCloudflareContext(context) {
|
|
|
50
60
|
}
|
|
51
61
|
function createAppwardenMiddleware(configFn) {
|
|
52
62
|
return async (args, next) => {
|
|
63
|
+
const startTime = getNowMs();
|
|
53
64
|
const { request, context } = args;
|
|
54
65
|
try {
|
|
55
66
|
const cloudflare = getCloudflareContext(context);
|
|
@@ -61,15 +72,23 @@ function createAppwardenMiddleware(configFn) {
|
|
|
61
72
|
);
|
|
62
73
|
return next();
|
|
63
74
|
}
|
|
64
|
-
|
|
75
|
+
const config = configFn(cloudflare);
|
|
76
|
+
const debugFn = debug(config.debug ?? false);
|
|
77
|
+
const requestUrl = new URL(request.url);
|
|
78
|
+
const isHTML = isHTMLRequest(request);
|
|
79
|
+
debugFn(
|
|
80
|
+
`Appwarden middleware invoked for ${requestUrl.pathname}`,
|
|
81
|
+
`isHTML: ${isHTML}`
|
|
82
|
+
);
|
|
83
|
+
if (!isHTML) {
|
|
65
84
|
return next();
|
|
66
85
|
}
|
|
67
|
-
const config = configFn(cloudflare);
|
|
68
86
|
const hasError = validateConfig(config, ReactRouterCloudflareConfigSchema);
|
|
69
87
|
if (hasError) {
|
|
70
88
|
return next();
|
|
71
89
|
}
|
|
72
90
|
if (isOnLockPage(config.lockPageSlug, request.url)) {
|
|
91
|
+
debugFn("Already on lock page - skipping");
|
|
73
92
|
return next();
|
|
74
93
|
}
|
|
75
94
|
const result = await checkLockStatus({
|
|
@@ -82,9 +101,33 @@ function createAppwardenMiddleware(configFn) {
|
|
|
82
101
|
});
|
|
83
102
|
if (result.isLocked) {
|
|
84
103
|
const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url);
|
|
104
|
+
debugFn(`Site is locked - redirecting to ${lockPageUrl.pathname}`);
|
|
85
105
|
throw createRedirect(lockPageUrl);
|
|
86
106
|
}
|
|
87
|
-
|
|
107
|
+
debugFn("Site is unlocked to origin");
|
|
108
|
+
const response = await next();
|
|
109
|
+
if (config.contentSecurityPolicy && response instanceof Response) {
|
|
110
|
+
debugFn("Applying CSP middleware");
|
|
111
|
+
const cspContext = {
|
|
112
|
+
request,
|
|
113
|
+
response,
|
|
114
|
+
hostname: requestUrl.hostname,
|
|
115
|
+
waitUntil: (fn) => cloudflare.ctx.waitUntil(fn),
|
|
116
|
+
debug: debugFn
|
|
117
|
+
};
|
|
118
|
+
await useContentSecurityPolicy(config.contentSecurityPolicy)(
|
|
119
|
+
cspContext,
|
|
120
|
+
async () => {
|
|
121
|
+
}
|
|
122
|
+
// no-op next
|
|
123
|
+
);
|
|
124
|
+
const elapsed2 = Math.round(getNowMs() - startTime);
|
|
125
|
+
debugFn(`Middleware executed in ${elapsed2}ms`);
|
|
126
|
+
return cspContext.response;
|
|
127
|
+
}
|
|
128
|
+
const elapsed = Math.round(getNowMs() - startTime);
|
|
129
|
+
debugFn(`Middleware executed in ${elapsed}ms`);
|
|
130
|
+
return response;
|
|
88
131
|
} catch (error) {
|
|
89
132
|
if (error instanceof Response) {
|
|
90
133
|
throw error;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { U as UseCSPInput } from '../use-content-security-policy-DUYpyUPy.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Cloudflare context provided by TanStack Start on Cloudflare Workers.
|
|
3
6
|
* This is the shape of the cloudflare context available in middleware.
|
|
@@ -18,6 +21,8 @@ interface TanStackStartAppwardenConfig {
|
|
|
18
21
|
appwardenApiHostname?: string;
|
|
19
22
|
/** Enable debug logging */
|
|
20
23
|
debug?: boolean;
|
|
24
|
+
/** Optional Content Security Policy configuration */
|
|
25
|
+
contentSecurityPolicy?: UseCSPInput;
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
28
|
* Configuration function that receives the Cloudflare context and returns the config.
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useContentSecurityPolicy
|
|
3
|
+
} from "../chunk-YBWFEBZC.js";
|
|
1
4
|
import {
|
|
2
5
|
validateConfig
|
|
3
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-U3T4R5KZ.js";
|
|
4
7
|
import {
|
|
5
8
|
checkLockStatus
|
|
6
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-3MKVGKH7.js";
|
|
7
10
|
import {
|
|
8
|
-
AppwardenApiTokenSchema,
|
|
9
|
-
BooleanSchema,
|
|
10
11
|
buildLockPageUrl,
|
|
11
12
|
createRedirect,
|
|
13
|
+
debug,
|
|
12
14
|
isOnLockPage
|
|
13
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-QVRWMYGL.js";
|
|
14
16
|
import {
|
|
15
|
-
|
|
17
|
+
UseCSPInputSchema,
|
|
18
|
+
isHTMLRequest
|
|
19
|
+
} from "../chunk-HCGLR3Z3.js";
|
|
20
|
+
import {
|
|
21
|
+
AppwardenApiTokenSchema,
|
|
22
|
+
BooleanSchema,
|
|
16
23
|
printMessage
|
|
17
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-G4RRFNHY.js";
|
|
18
25
|
|
|
19
26
|
// src/schemas/tanstack-start-cloudflare.ts
|
|
20
27
|
import { z } from "zod";
|
|
@@ -26,12 +33,16 @@ var TanStackStartCloudflareConfigSchema = z.object({
|
|
|
26
33
|
/** Optional custom API hostname (defaults to https://api.appwarden.io) */
|
|
27
34
|
appwardenApiHostname: z.string().optional(),
|
|
28
35
|
/** Enable debug logging */
|
|
29
|
-
debug: BooleanSchema.default(false)
|
|
36
|
+
debug: BooleanSchema.default(false),
|
|
37
|
+
/** Optional Content Security Policy configuration */
|
|
38
|
+
contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional()
|
|
30
39
|
});
|
|
31
40
|
|
|
32
41
|
// src/adapters/tanstack-start-cloudflare.ts
|
|
42
|
+
var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
33
43
|
function createAppwardenMiddleware(configFn) {
|
|
34
44
|
return async (args) => {
|
|
45
|
+
const startTime = getNowMs();
|
|
35
46
|
const { request, next, context } = args;
|
|
36
47
|
try {
|
|
37
48
|
const cloudflare = context.cloudflare;
|
|
@@ -43,10 +54,17 @@ function createAppwardenMiddleware(configFn) {
|
|
|
43
54
|
);
|
|
44
55
|
return next();
|
|
45
56
|
}
|
|
46
|
-
|
|
57
|
+
const config = configFn(cloudflare);
|
|
58
|
+
const debugFn = debug(config.debug ?? false);
|
|
59
|
+
const requestUrl = new URL(request.url);
|
|
60
|
+
const isHTML = isHTMLRequest(request);
|
|
61
|
+
debugFn(
|
|
62
|
+
`Appwarden middleware invoked for ${requestUrl.pathname}`,
|
|
63
|
+
`isHTML: ${isHTML}`
|
|
64
|
+
);
|
|
65
|
+
if (!isHTML) {
|
|
47
66
|
return next();
|
|
48
67
|
}
|
|
49
|
-
const config = configFn(cloudflare);
|
|
50
68
|
const hasError = validateConfig(
|
|
51
69
|
config,
|
|
52
70
|
TanStackStartCloudflareConfigSchema
|
|
@@ -55,6 +73,7 @@ function createAppwardenMiddleware(configFn) {
|
|
|
55
73
|
return next();
|
|
56
74
|
}
|
|
57
75
|
if (isOnLockPage(config.lockPageSlug, request.url)) {
|
|
76
|
+
debugFn("Already on lock page - skipping");
|
|
58
77
|
return next();
|
|
59
78
|
}
|
|
60
79
|
const result = await checkLockStatus({
|
|
@@ -67,9 +86,33 @@ function createAppwardenMiddleware(configFn) {
|
|
|
67
86
|
});
|
|
68
87
|
if (result.isLocked) {
|
|
69
88
|
const lockPageUrl = buildLockPageUrl(config.lockPageSlug, request.url);
|
|
89
|
+
debugFn(`Site is locked - redirecting to ${lockPageUrl.pathname}`);
|
|
70
90
|
throw createRedirect(lockPageUrl);
|
|
71
91
|
}
|
|
72
|
-
|
|
92
|
+
debugFn("Site is unlocked to origin");
|
|
93
|
+
const response = await next();
|
|
94
|
+
if (config.contentSecurityPolicy && response instanceof Response) {
|
|
95
|
+
debugFn("Applying CSP middleware");
|
|
96
|
+
const cspContext = {
|
|
97
|
+
request,
|
|
98
|
+
response,
|
|
99
|
+
hostname: requestUrl.hostname,
|
|
100
|
+
waitUntil: (fn) => cloudflare.ctx.waitUntil(fn),
|
|
101
|
+
debug: debugFn
|
|
102
|
+
};
|
|
103
|
+
await useContentSecurityPolicy(config.contentSecurityPolicy)(
|
|
104
|
+
cspContext,
|
|
105
|
+
async () => {
|
|
106
|
+
}
|
|
107
|
+
// no-op next
|
|
108
|
+
);
|
|
109
|
+
const elapsed2 = Math.round(getNowMs() - startTime);
|
|
110
|
+
debugFn(`Middleware executed in ${elapsed2}ms`);
|
|
111
|
+
return cspContext.response;
|
|
112
|
+
}
|
|
113
|
+
const elapsed = Math.round(getNowMs() - startTime);
|
|
114
|
+
debugFn(`Middleware executed in ${elapsed}ms`);
|
|
115
|
+
return response;
|
|
73
116
|
} catch (error) {
|
|
74
117
|
if (error instanceof Response) {
|
|
75
118
|
throw error;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CSP_KEYWORDS,
|
|
3
|
+
autoQuoteCSPDirectiveArray,
|
|
4
|
+
autoQuoteCSPDirectiveValue,
|
|
5
|
+
autoQuoteCSPKeyword,
|
|
6
|
+
deleteEdgeValue,
|
|
7
|
+
getLockValue,
|
|
8
|
+
insertErrorLogs,
|
|
9
|
+
isCSPKeyword,
|
|
10
|
+
isQuoted,
|
|
11
|
+
makeCSPHeader,
|
|
12
|
+
store,
|
|
13
|
+
syncEdgeValue
|
|
14
|
+
} from "./chunk-G4RRFNHY.js";
|
|
15
|
+
export {
|
|
16
|
+
CSP_KEYWORDS,
|
|
17
|
+
autoQuoteCSPDirectiveArray,
|
|
18
|
+
autoQuoteCSPDirectiveValue,
|
|
19
|
+
autoQuoteCSPKeyword,
|
|
20
|
+
deleteEdgeValue,
|
|
21
|
+
getLockValue,
|
|
22
|
+
insertErrorLogs,
|
|
23
|
+
isCSPKeyword,
|
|
24
|
+
isQuoted,
|
|
25
|
+
makeCSPHeader,
|
|
26
|
+
store,
|
|
27
|
+
syncEdgeValue
|
|
28
|
+
};
|