@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/vercel.js
CHANGED
|
@@ -4,22 +4,27 @@ import {
|
|
|
4
4
|
} from "./chunk-QEFORWCW.js";
|
|
5
5
|
import {
|
|
6
6
|
validateConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-U3T4R5KZ.js";
|
|
8
8
|
import {
|
|
9
|
-
LockValue,
|
|
10
9
|
MemoryCache,
|
|
11
10
|
TEMPORARY_REDIRECT_STATUS,
|
|
12
11
|
buildLockPageUrl,
|
|
12
|
+
debug,
|
|
13
13
|
isOnLockPage
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QVRWMYGL.js";
|
|
15
15
|
import {
|
|
16
16
|
APPWARDEN_CACHE_KEY,
|
|
17
|
-
|
|
17
|
+
CSPDirectivesSchema,
|
|
18
|
+
CSPModeSchema,
|
|
18
19
|
errors,
|
|
19
20
|
globalErrors,
|
|
20
|
-
isHTMLRequest
|
|
21
|
+
isHTMLRequest
|
|
22
|
+
} from "./chunk-HCGLR3Z3.js";
|
|
23
|
+
import {
|
|
24
|
+
LockValue,
|
|
25
|
+
makeCSPHeader,
|
|
21
26
|
printMessage
|
|
22
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-G4RRFNHY.js";
|
|
23
28
|
|
|
24
29
|
// src/runners/appwarden-on-vercel.ts
|
|
25
30
|
import { waitUntil } from "@vercel/functions";
|
|
@@ -101,7 +106,7 @@ var APIError = class extends Error {
|
|
|
101
106
|
}
|
|
102
107
|
};
|
|
103
108
|
var syncEdgeValue = async (context) => {
|
|
104
|
-
debug(
|
|
109
|
+
context.debug("syncing with api");
|
|
105
110
|
try {
|
|
106
111
|
const response = await fetch(new URL("/v1/status/check", "https://api.appwarden.io"), {
|
|
107
112
|
method: "POST",
|
|
@@ -134,11 +139,43 @@ var syncEdgeValue = async (context) => {
|
|
|
134
139
|
};
|
|
135
140
|
|
|
136
141
|
// src/schemas/vercel.ts
|
|
142
|
+
var VercelCSPSchema = z.object({
|
|
143
|
+
mode: CSPModeSchema,
|
|
144
|
+
directives: z.lazy(() => CSPDirectivesSchema).optional().refine(
|
|
145
|
+
(val) => {
|
|
146
|
+
try {
|
|
147
|
+
if (typeof val === "string") {
|
|
148
|
+
JSON.parse(val);
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{ message: "DirectivesBadParse" /* DirectivesBadParse */ }
|
|
156
|
+
).refine(
|
|
157
|
+
(val) => {
|
|
158
|
+
if (!val) return true;
|
|
159
|
+
const serialized = typeof val === "string" ? val : JSON.stringify(val);
|
|
160
|
+
return !serialized.includes("{{nonce}}");
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
message: "Nonce-based CSP is not supported in Vercel Edge Middleware. Remove '{{nonce}}' placeholders from your CSP directives, as Vercel does not support nonce injection."
|
|
164
|
+
}
|
|
165
|
+
).transform(
|
|
166
|
+
(val) => typeof val === "string" ? JSON.parse(val) : val
|
|
167
|
+
)
|
|
168
|
+
}).refine(
|
|
169
|
+
(values) => ["report-only", "enforced"].includes(values.mode) ? !!values.directives : true,
|
|
170
|
+
{ path: ["directives"], message: "DirectivesRequired" /* DirectivesRequired */ }
|
|
171
|
+
);
|
|
137
172
|
var BaseNextJsConfigSchema = z.object({
|
|
138
173
|
cacheUrl: z.string(),
|
|
139
174
|
appwardenApiToken: z.string(),
|
|
140
175
|
vercelApiToken: z.string().optional(),
|
|
141
|
-
|
|
176
|
+
debug: z.boolean().optional(),
|
|
177
|
+
lockPageSlug: z.string().default("").transform((val) => val.replace(/^\/?/, "/")),
|
|
178
|
+
contentSecurityPolicy: VercelCSPSchema.optional()
|
|
142
179
|
});
|
|
143
180
|
var AppwardenConfigSchema = BaseNextJsConfigSchema.refine(
|
|
144
181
|
(data) => {
|
|
@@ -188,7 +225,6 @@ var AppwardenConfigSchema = BaseNextJsConfigSchema.refine(
|
|
|
188
225
|
});
|
|
189
226
|
|
|
190
227
|
// src/runners/appwarden-on-vercel.ts
|
|
191
|
-
debug("Instantiating isolate");
|
|
192
228
|
var memoryCache = new MemoryCache({ maxSize: 1 });
|
|
193
229
|
function safeWaitUntil(promise) {
|
|
194
230
|
try {
|
|
@@ -203,29 +239,54 @@ function createAppwardenMiddleware(config) {
|
|
|
203
239
|
return NextResponse.next();
|
|
204
240
|
}
|
|
205
241
|
const parsedConfig = AppwardenConfigSchema.parse(config);
|
|
242
|
+
const debugFn = debug(parsedConfig.debug ?? false);
|
|
243
|
+
const applyCspHeaders = (response) => {
|
|
244
|
+
const cspConfig = parsedConfig.contentSecurityPolicy;
|
|
245
|
+
if (cspConfig && ["enforced", "report-only"].includes(cspConfig.mode)) {
|
|
246
|
+
const [headerName, headerValue] = makeCSPHeader(
|
|
247
|
+
"",
|
|
248
|
+
cspConfig.directives,
|
|
249
|
+
cspConfig.mode
|
|
250
|
+
);
|
|
251
|
+
response.headers.set(headerName, headerValue);
|
|
252
|
+
}
|
|
253
|
+
return response;
|
|
254
|
+
};
|
|
206
255
|
try {
|
|
207
256
|
const requestUrl = new URL(request.url);
|
|
208
257
|
const isHTML = isHTMLRequest(request);
|
|
209
|
-
|
|
258
|
+
debugFn(
|
|
259
|
+
`Appwarden middleware invoked for ${requestUrl.pathname}`,
|
|
260
|
+
`isHTML: ${isHTML}`
|
|
261
|
+
);
|
|
210
262
|
if (!isHTML) {
|
|
263
|
+
debugFn("Non-HTML request detected - passing through");
|
|
211
264
|
return NextResponse.next();
|
|
212
265
|
}
|
|
213
266
|
if (!parsedConfig.lockPageSlug) {
|
|
267
|
+
debugFn("No lockPageSlug configured - passing through");
|
|
214
268
|
return NextResponse.next();
|
|
215
269
|
}
|
|
216
270
|
if (isOnLockPage(parsedConfig.lockPageSlug, request.url)) {
|
|
271
|
+
debugFn("Already on lock page - passing through");
|
|
217
272
|
return NextResponse.next();
|
|
218
273
|
}
|
|
219
274
|
const provider = isCacheUrl.edgeConfig(parsedConfig.cacheUrl) ? "edge-config" : "upstash";
|
|
275
|
+
debugFn(`Using provider: ${provider}`);
|
|
220
276
|
const cacheValue = memoryCache.get(APPWARDEN_CACHE_KEY);
|
|
221
277
|
const shouldRecheck = MemoryCache.isExpired(cacheValue);
|
|
222
278
|
if (!cacheValue || shouldRecheck) {
|
|
279
|
+
debugFn(
|
|
280
|
+
"Memory cache miss or expired - syncing edge value in background",
|
|
281
|
+
`shouldRecheck=${shouldRecheck}`
|
|
282
|
+
);
|
|
223
283
|
safeWaitUntil(
|
|
224
284
|
syncEdgeValue({
|
|
225
285
|
requestUrl,
|
|
226
286
|
cacheUrl: parsedConfig.cacheUrl,
|
|
227
287
|
appwardenApiToken: parsedConfig.appwardenApiToken,
|
|
228
|
-
vercelApiToken: parsedConfig.vercelApiToken
|
|
288
|
+
vercelApiToken: parsedConfig.vercelApiToken,
|
|
289
|
+
debug: debugFn
|
|
229
290
|
})
|
|
230
291
|
);
|
|
231
292
|
}
|
|
@@ -235,17 +296,25 @@ function createAppwardenMiddleware(config) {
|
|
|
235
296
|
provider
|
|
236
297
|
})).lockValue;
|
|
237
298
|
if (lockValue?.isLocked) {
|
|
299
|
+
debugFn("Site is locked - redirecting to lock page");
|
|
238
300
|
const lockPageUrl = buildLockPageUrl(
|
|
239
301
|
parsedConfig.lockPageSlug,
|
|
240
302
|
request.url
|
|
241
303
|
);
|
|
242
|
-
|
|
304
|
+
const redirectResponse = Response.redirect(
|
|
243
305
|
lockPageUrl.toString(),
|
|
244
306
|
TEMPORARY_REDIRECT_STATUS
|
|
245
307
|
);
|
|
308
|
+
return applyCspHeaders(redirectResponse);
|
|
246
309
|
}
|
|
247
|
-
|
|
310
|
+
const response = NextResponse.next();
|
|
311
|
+
debugFn("Site is not locked - passing through");
|
|
312
|
+
return applyCspHeaders(response);
|
|
248
313
|
} catch (e) {
|
|
314
|
+
debugFn(
|
|
315
|
+
"Error in Appwarden Vercel middleware",
|
|
316
|
+
e instanceof Error ? e.message : String(e)
|
|
317
|
+
);
|
|
249
318
|
const message = "Appwarden encountered an unknown error. Please contact Appwarden support at https://appwarden.io/join-community.";
|
|
250
319
|
if (e instanceof Error) {
|
|
251
320
|
if (!globalErrors.includes(e.message)) {
|
package/chunk-A5XGYLYS.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
debug,
|
|
3
|
-
isHTMLResponse,
|
|
4
|
-
printMessage
|
|
5
|
-
} from "./chunk-L5EQIJZB.js";
|
|
6
|
-
|
|
7
|
-
// src/schemas/use-content-security-policy.ts
|
|
8
|
-
import { z as z2 } from "zod";
|
|
9
|
-
|
|
10
|
-
// src/types/csp.ts
|
|
11
|
-
import { z } from "zod";
|
|
12
|
-
var stringySchema = z.union([z.array(z.string()), z.string(), z.boolean()]);
|
|
13
|
-
var ContentSecurityPolicySchema = z.object({
|
|
14
|
-
"default-src": stringySchema.optional(),
|
|
15
|
-
"script-src": stringySchema.optional(),
|
|
16
|
-
"style-src": stringySchema.optional(),
|
|
17
|
-
"img-src": stringySchema.optional(),
|
|
18
|
-
"connect-src": stringySchema.optional(),
|
|
19
|
-
"font-src": stringySchema.optional(),
|
|
20
|
-
"object-src": stringySchema.optional(),
|
|
21
|
-
"media-src": stringySchema.optional(),
|
|
22
|
-
"frame-src": stringySchema.optional(),
|
|
23
|
-
sandbox: stringySchema.optional(),
|
|
24
|
-
"report-uri": stringySchema.optional(),
|
|
25
|
-
"child-src": stringySchema.optional(),
|
|
26
|
-
"form-action": stringySchema.optional(),
|
|
27
|
-
"frame-ancestors": stringySchema.optional(),
|
|
28
|
-
"plugin-types": stringySchema.optional(),
|
|
29
|
-
"base-uri": stringySchema.optional(),
|
|
30
|
-
"report-to": stringySchema.optional(),
|
|
31
|
-
"worker-src": stringySchema.optional(),
|
|
32
|
-
"manifest-src": stringySchema.optional(),
|
|
33
|
-
"prefetch-src": stringySchema.optional(),
|
|
34
|
-
"navigate-to": stringySchema.optional(),
|
|
35
|
-
"require-sri-for": stringySchema.optional(),
|
|
36
|
-
"block-all-mixed-content": stringySchema.optional(),
|
|
37
|
-
"upgrade-insecure-requests": stringySchema.optional(),
|
|
38
|
-
"trusted-types": stringySchema.optional(),
|
|
39
|
-
"require-trusted-types-for": stringySchema.optional()
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// src/schemas/use-content-security-policy.ts
|
|
43
|
-
var CSPDirectivesSchema = z2.union([
|
|
44
|
-
z2.string(),
|
|
45
|
-
ContentSecurityPolicySchema
|
|
46
|
-
]);
|
|
47
|
-
var CSPModeSchema = z2.union([
|
|
48
|
-
z2.literal("disabled"),
|
|
49
|
-
z2.literal("report-only"),
|
|
50
|
-
z2.literal("enforced")
|
|
51
|
-
]).optional().default("disabled");
|
|
52
|
-
var UseCSPInputSchema = z2.object({
|
|
53
|
-
hostname: z2.string().optional(),
|
|
54
|
-
mode: CSPModeSchema,
|
|
55
|
-
directives: CSPDirectivesSchema.optional().refine(
|
|
56
|
-
(val) => {
|
|
57
|
-
try {
|
|
58
|
-
if (typeof val === "string") {
|
|
59
|
-
JSON.parse(val);
|
|
60
|
-
}
|
|
61
|
-
return true;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
{ message: "DirectivesBadParse" /* DirectivesBadParse */ }
|
|
67
|
-
).transform(
|
|
68
|
-
(val) => typeof val === "string" ? JSON.parse(val) : val
|
|
69
|
-
)
|
|
70
|
-
}).refine(
|
|
71
|
-
(values) => (
|
|
72
|
-
// validate that directives are provided when the mode is "report-only" or "enforced"
|
|
73
|
-
["report-only", "enforced"].includes(values.mode) ? !!values.directives : true
|
|
74
|
-
),
|
|
75
|
-
{ path: ["directives"], message: "DirectivesRequired" /* DirectivesRequired */ }
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// src/utils/cloudflare/csp-keywords.ts
|
|
79
|
-
var CSP_KEYWORDS = [
|
|
80
|
-
"self",
|
|
81
|
-
"none",
|
|
82
|
-
"unsafe-inline",
|
|
83
|
-
"unsafe-eval",
|
|
84
|
-
"unsafe-hashes",
|
|
85
|
-
"strict-dynamic",
|
|
86
|
-
"report-sample",
|
|
87
|
-
"unsafe-allow-redirects",
|
|
88
|
-
"wasm-unsafe-eval",
|
|
89
|
-
"trusted-types-eval",
|
|
90
|
-
"report-sha256",
|
|
91
|
-
"report-sha384",
|
|
92
|
-
"report-sha512",
|
|
93
|
-
"unsafe-webtransport-hashes"
|
|
94
|
-
];
|
|
95
|
-
var CSP_KEYWORDS_SET = new Set(CSP_KEYWORDS);
|
|
96
|
-
var isCSPKeyword = (value) => {
|
|
97
|
-
return CSP_KEYWORDS_SET.has(value.toLowerCase());
|
|
98
|
-
};
|
|
99
|
-
var isQuoted = (value) => {
|
|
100
|
-
return value.startsWith("'") && value.endsWith("'");
|
|
101
|
-
};
|
|
102
|
-
var autoQuoteCSPKeyword = (value) => {
|
|
103
|
-
const trimmed = value.trim();
|
|
104
|
-
if (isQuoted(trimmed)) {
|
|
105
|
-
return trimmed;
|
|
106
|
-
}
|
|
107
|
-
if (isCSPKeyword(trimmed)) {
|
|
108
|
-
return `'${trimmed}'`;
|
|
109
|
-
}
|
|
110
|
-
return trimmed;
|
|
111
|
-
};
|
|
112
|
-
var autoQuoteCSPDirectiveValue = (value) => {
|
|
113
|
-
return value.trim().split(/\s+/).filter(Boolean).map(autoQuoteCSPKeyword).join(" ");
|
|
114
|
-
};
|
|
115
|
-
var autoQuoteCSPDirectiveArray = (values) => {
|
|
116
|
-
return values.map(autoQuoteCSPKeyword);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// src/utils/cloudflare/make-csp-header.ts
|
|
120
|
-
var addNonce = (value, cspNonce) => value.replace("{{nonce}}", `'nonce-${cspNonce}'`);
|
|
121
|
-
var makeCSPHeader = (cspNonce, directives, mode) => {
|
|
122
|
-
const namesSeen = /* @__PURE__ */ new Set(), result = [];
|
|
123
|
-
Object.entries(directives ?? {}).forEach(([originalName, value]) => {
|
|
124
|
-
const name = originalName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
125
|
-
if (namesSeen.has(name)) {
|
|
126
|
-
throw new Error(`${originalName} is specified more than once`);
|
|
127
|
-
}
|
|
128
|
-
namesSeen.add(name);
|
|
129
|
-
let directiveValue;
|
|
130
|
-
if (Array.isArray(value)) {
|
|
131
|
-
directiveValue = autoQuoteCSPDirectiveArray(value).join(" ");
|
|
132
|
-
} else if (value === true) {
|
|
133
|
-
directiveValue = "";
|
|
134
|
-
} else if (typeof value === "string") {
|
|
135
|
-
directiveValue = autoQuoteCSPDirectiveValue(value);
|
|
136
|
-
} else {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (directiveValue) {
|
|
140
|
-
result.push(`${name} ${addNonce(directiveValue, cspNonce)}`);
|
|
141
|
-
} else {
|
|
142
|
-
result.push(name);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
return [
|
|
146
|
-
mode === "enforced" ? "Content-Security-Policy" : "Content-Security-Policy-Report-Only",
|
|
147
|
-
result.join("; ")
|
|
148
|
-
];
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// src/middlewares/use-content-security-policy.ts
|
|
152
|
-
var AppendAttribute = (attribute, nonce) => ({
|
|
153
|
-
element: function(element) {
|
|
154
|
-
element.setAttribute(attribute, nonce);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
var useContentSecurityPolicy = (input) => {
|
|
158
|
-
const parsedInput = UseCSPInputSchema.safeParse(input);
|
|
159
|
-
if (!parsedInput.success) {
|
|
160
|
-
throw parsedInput.error;
|
|
161
|
-
}
|
|
162
|
-
const config = parsedInput.data;
|
|
163
|
-
return async (context, next) => {
|
|
164
|
-
await next();
|
|
165
|
-
if (config.hostname && context.hostname !== config.hostname) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const { response } = context;
|
|
169
|
-
if (
|
|
170
|
-
// if the csp is disabled
|
|
171
|
-
!["enforced", "report-only"].includes(config.mode)
|
|
172
|
-
) {
|
|
173
|
-
debug(printMessage("csp is disabled"));
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (response.headers.has("Content-Type") && !isHTMLResponse(response)) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const cspNonce = crypto.randomUUID();
|
|
180
|
-
const [cspHeaderName, cspHeaderValue] = makeCSPHeader(
|
|
181
|
-
cspNonce,
|
|
182
|
-
config.directives,
|
|
183
|
-
config.mode
|
|
184
|
-
);
|
|
185
|
-
const nextResponse = new Response(response.body, response);
|
|
186
|
-
nextResponse.headers.set(cspHeaderName, cspHeaderValue);
|
|
187
|
-
nextResponse.headers.set("content-type", "text/html; charset=utf-8");
|
|
188
|
-
context.response = new HTMLRewriter().on("style", AppendAttribute("nonce", cspNonce)).on("script", AppendAttribute("nonce", cspNonce)).transform(nextResponse);
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
export {
|
|
193
|
-
CSPDirectivesSchema,
|
|
194
|
-
CSPModeSchema,
|
|
195
|
-
useContentSecurityPolicy
|
|
196
|
-
};
|
package/chunk-L5EQIJZB.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// src/constants.ts
|
|
2
|
-
var LOCKDOWN_TEST_EXPIRY_MS = 5 * 60 * 1e3;
|
|
3
|
-
var errors = { badCacheConnection: "BAD_CACHE_CONNECTION" };
|
|
4
|
-
var globalErrors = [errors.badCacheConnection];
|
|
5
|
-
var APPWARDEN_TEST_ROUTE = "/_appwarden/test";
|
|
6
|
-
var APPWARDEN_CACHE_KEY = "appwarden-lock";
|
|
7
|
-
|
|
8
|
-
// src/utils/debug.ts
|
|
9
|
-
var debug = (...msg) => {
|
|
10
|
-
if (true) {
|
|
11
|
-
const formatted = msg.map((m) => {
|
|
12
|
-
if (typeof m === "object" && m !== null) {
|
|
13
|
-
if (m instanceof Error) {
|
|
14
|
-
return m.stack ?? m.message;
|
|
15
|
-
}
|
|
16
|
-
try {
|
|
17
|
-
return JSON.stringify(m);
|
|
18
|
-
} catch {
|
|
19
|
-
try {
|
|
20
|
-
return String(m);
|
|
21
|
-
} catch {
|
|
22
|
-
return "[Unserializable value]";
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return m;
|
|
27
|
-
});
|
|
28
|
-
console.log(...formatted);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// src/utils/print-message.ts
|
|
33
|
-
var addSlashes = (str) => str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/\u0000/g, "\\0").replace(/<\/script>/gi, "<\\/script>");
|
|
34
|
-
var printMessage = (message) => `[@appwarden/middleware] ${addSlashes(message)}`;
|
|
35
|
-
|
|
36
|
-
// src/utils/request-checks.ts
|
|
37
|
-
function isHTMLResponse(response) {
|
|
38
|
-
return response.headers.get("Content-Type")?.includes("text/html") ?? false;
|
|
39
|
-
}
|
|
40
|
-
function isHTMLRequest(request) {
|
|
41
|
-
return request.headers.get("accept")?.includes("text/html") ?? false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
LOCKDOWN_TEST_EXPIRY_MS,
|
|
46
|
-
errors,
|
|
47
|
-
globalErrors,
|
|
48
|
-
APPWARDEN_TEST_ROUTE,
|
|
49
|
-
APPWARDEN_CACHE_KEY,
|
|
50
|
-
debug,
|
|
51
|
-
printMessage,
|
|
52
|
-
isHTMLResponse,
|
|
53
|
-
isHTMLRequest
|
|
54
|
-
};
|
package/chunk-MDODCAA3.js
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LockValue,
|
|
3
|
-
MemoryCache
|
|
4
|
-
} from "./chunk-ZX5QO4Y2.js";
|
|
5
|
-
import {
|
|
6
|
-
APPWARDEN_CACHE_KEY,
|
|
7
|
-
APPWARDEN_TEST_ROUTE,
|
|
8
|
-
debug,
|
|
9
|
-
printMessage
|
|
10
|
-
} from "./chunk-L5EQIJZB.js";
|
|
11
|
-
|
|
12
|
-
// src/utils/cloudflare/cloudflare-cache.ts
|
|
13
|
-
var store = {
|
|
14
|
-
json: (context, cacheKey, options) => {
|
|
15
|
-
const cacheKeyUrl = new URL(cacheKey, context.serviceOrigin);
|
|
16
|
-
return {
|
|
17
|
-
getValue: () => getCacheValue(context, cacheKeyUrl),
|
|
18
|
-
updateValue: (json) => updateCacheValue(context, cacheKeyUrl, json, options?.ttl),
|
|
19
|
-
deleteValue: () => clearCache(context, cacheKeyUrl)
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
var getCacheValue = async (context, cacheKey) => {
|
|
24
|
-
const match = await context.cache.match(cacheKey);
|
|
25
|
-
if (!match) {
|
|
26
|
-
debug(`[${cacheKey.pathname}] Cache MISS!`);
|
|
27
|
-
return void 0;
|
|
28
|
-
}
|
|
29
|
-
debug(`[${cacheKey.pathname}] Cache MATCH!`);
|
|
30
|
-
return match;
|
|
31
|
-
};
|
|
32
|
-
var updateCacheValue = async (context, cacheKey, value, ttl) => {
|
|
33
|
-
debug(
|
|
34
|
-
"updating cache...",
|
|
35
|
-
cacheKey.href,
|
|
36
|
-
value,
|
|
37
|
-
ttl ? `expires in ${ttl}s` : ""
|
|
38
|
-
);
|
|
39
|
-
await context.cache.put(
|
|
40
|
-
cacheKey,
|
|
41
|
-
new Response(JSON.stringify(value), {
|
|
42
|
-
headers: {
|
|
43
|
-
"content-type": "application/json",
|
|
44
|
-
...ttl && {
|
|
45
|
-
"cache-control": `max-age=${ttl}`
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
var clearCache = (context, cacheKey) => context.cache.delete(cacheKey);
|
|
52
|
-
|
|
53
|
-
// src/utils/cloudflare/delete-edge-value.ts
|
|
54
|
-
var deleteEdgeValue = async (context) => {
|
|
55
|
-
try {
|
|
56
|
-
switch (context.provider) {
|
|
57
|
-
case "cloudflare-cache": {
|
|
58
|
-
const success = await context.edgeCache.deleteValue();
|
|
59
|
-
if (!success) {
|
|
60
|
-
throw new Error();
|
|
61
|
-
}
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
default:
|
|
65
|
-
throw new Error(`Unsupported provider: ${context.provider}`);
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {
|
|
68
|
-
const message = "Failed to delete edge value";
|
|
69
|
-
console.error(
|
|
70
|
-
printMessage(e instanceof Error ? `${message} - ${e.message}` : message)
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// src/utils/cloudflare/get-lock-value.ts
|
|
76
|
-
var getLockValue = async (context) => {
|
|
77
|
-
try {
|
|
78
|
-
let shouldDeleteEdgeValue = false;
|
|
79
|
-
let cacheResponse, lockValue = {
|
|
80
|
-
isLocked: 0,
|
|
81
|
-
isLockedTest: 0,
|
|
82
|
-
lastCheck: Date.now(),
|
|
83
|
-
code: ""
|
|
84
|
-
};
|
|
85
|
-
switch (context.provider) {
|
|
86
|
-
case "cloudflare-cache": {
|
|
87
|
-
cacheResponse = await context.edgeCache.getValue();
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
default:
|
|
91
|
-
throw new Error(`Unsupported provider: ${context.provider}`);
|
|
92
|
-
}
|
|
93
|
-
if (!cacheResponse) {
|
|
94
|
-
return { lockValue: void 0 };
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
const clonedResponse = cacheResponse?.clone();
|
|
98
|
-
lockValue = LockValue.parse(
|
|
99
|
-
clonedResponse ? await clonedResponse.json() : void 0
|
|
100
|
-
);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.error(
|
|
103
|
-
printMessage(
|
|
104
|
-
`Failed to parse ${context.keyName} from edge cache - ${error}`
|
|
105
|
-
)
|
|
106
|
-
);
|
|
107
|
-
shouldDeleteEdgeValue = true;
|
|
108
|
-
}
|
|
109
|
-
return { lockValue, shouldDeleteEdgeValue };
|
|
110
|
-
} catch (e) {
|
|
111
|
-
const message = "Failed to retrieve edge value";
|
|
112
|
-
console.error(
|
|
113
|
-
printMessage(e instanceof Error ? `${message} - ${e.message}` : message)
|
|
114
|
-
);
|
|
115
|
-
return { lockValue: void 0 };
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// src/utils/cloudflare/sync-edge-value.ts
|
|
120
|
-
var APIError = class extends Error {
|
|
121
|
-
constructor(message) {
|
|
122
|
-
super(message);
|
|
123
|
-
this.name = "APIError";
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
var DEFAULT_API_HOSTNAME = "https://api.appwarden.io";
|
|
127
|
-
var syncEdgeValue = async (context) => {
|
|
128
|
-
debug(`syncing with api`);
|
|
129
|
-
try {
|
|
130
|
-
const apiHostname = context.appwardenApiHostname ?? DEFAULT_API_HOSTNAME;
|
|
131
|
-
const response = await fetch(new URL("/v1/status/check", apiHostname), {
|
|
132
|
-
method: "POST",
|
|
133
|
-
headers: { "content-type": "application/json" },
|
|
134
|
-
body: JSON.stringify({
|
|
135
|
-
service: "cloudflare",
|
|
136
|
-
provider: context.provider,
|
|
137
|
-
fqdn: context.requestUrl.hostname,
|
|
138
|
-
appwardenApiToken: context.appwardenApiToken
|
|
139
|
-
})
|
|
140
|
-
});
|
|
141
|
-
if (response.status !== 200) {
|
|
142
|
-
throw new Error(`${response.status} ${response.statusText}`);
|
|
143
|
-
}
|
|
144
|
-
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
145
|
-
const result = await response.json();
|
|
146
|
-
if (result.error) {
|
|
147
|
-
throw new APIError(result.error.message);
|
|
148
|
-
}
|
|
149
|
-
if (!result.content) {
|
|
150
|
-
throw new APIError("no content from api");
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
const parsedValue = LockValue.omit({ lastCheck: true }).parse(
|
|
154
|
-
result.content
|
|
155
|
-
);
|
|
156
|
-
debug(`syncing with api...DONE ${JSON.stringify(parsedValue, null, 2)}`);
|
|
157
|
-
await context.edgeCache.updateValue({
|
|
158
|
-
...parsedValue,
|
|
159
|
-
lastCheck: Date.now()
|
|
160
|
-
});
|
|
161
|
-
} catch (error) {
|
|
162
|
-
throw new APIError(`Failed to parse check endpoint result - ${error}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
} catch (e) {
|
|
166
|
-
const message = "Failed to fetch from check endpoint";
|
|
167
|
-
console.error(
|
|
168
|
-
printMessage(
|
|
169
|
-
e instanceof APIError ? e.message : e instanceof Error ? `${message} - ${e.message}` : message
|
|
170
|
-
)
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/core/check-lock-status.ts
|
|
176
|
-
var createContext = async (config) => {
|
|
177
|
-
const requestUrl = new URL(config.request.url);
|
|
178
|
-
const keyName = APPWARDEN_CACHE_KEY;
|
|
179
|
-
const provider = "cloudflare-cache";
|
|
180
|
-
const edgeCache = store.json(
|
|
181
|
-
{
|
|
182
|
-
serviceOrigin: requestUrl.origin,
|
|
183
|
-
cache: await caches.open("appwarden:lock")
|
|
184
|
-
},
|
|
185
|
-
keyName
|
|
186
|
-
);
|
|
187
|
-
return {
|
|
188
|
-
keyName,
|
|
189
|
-
request: config.request,
|
|
190
|
-
edgeCache,
|
|
191
|
-
requestUrl,
|
|
192
|
-
provider,
|
|
193
|
-
debug: config.debug ?? false,
|
|
194
|
-
lockPageSlug: config.lockPageSlug,
|
|
195
|
-
appwardenApiToken: config.appwardenApiToken,
|
|
196
|
-
appwardenApiHostname: config.appwardenApiHostname,
|
|
197
|
-
waitUntil: config.waitUntil
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
var resolveLockStatus = async (context) => {
|
|
201
|
-
const { lockValue, shouldDeleteEdgeValue } = await getLockValue(context);
|
|
202
|
-
if (shouldDeleteEdgeValue) {
|
|
203
|
-
await deleteEdgeValue(context);
|
|
204
|
-
}
|
|
205
|
-
const isTestRoute = context.requestUrl.pathname === APPWARDEN_TEST_ROUTE;
|
|
206
|
-
const isTestLock = isTestRoute && !MemoryCache.isTestExpired(lockValue) && !!lockValue;
|
|
207
|
-
return {
|
|
208
|
-
isLocked: !!lockValue?.isLocked || isTestLock,
|
|
209
|
-
isTestLock,
|
|
210
|
-
lockValue,
|
|
211
|
-
wasDeleted: shouldDeleteEdgeValue ?? false
|
|
212
|
-
};
|
|
213
|
-
};
|
|
214
|
-
var checkLockStatus = async (config) => {
|
|
215
|
-
const context = await createContext(config);
|
|
216
|
-
let { isLocked, isTestLock, lockValue, wasDeleted } = await resolveLockStatus(context);
|
|
217
|
-
if (MemoryCache.isExpired(lockValue) || wasDeleted) {
|
|
218
|
-
if (!lockValue || wasDeleted || lockValue.isLocked) {
|
|
219
|
-
await syncEdgeValue(context);
|
|
220
|
-
({ isLocked, isTestLock } = await resolveLockStatus(context));
|
|
221
|
-
} else {
|
|
222
|
-
config.waitUntil(syncEdgeValue(context));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return { isLocked, isTestLock };
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
export {
|
|
229
|
-
store,
|
|
230
|
-
getLockValue,
|
|
231
|
-
checkLockStatus
|
|
232
|
-
};
|