@abdssamie/convex-checkpoints 0.1.1 → 0.1.7
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 +28 -35
- package/dist/client/index.d.ts +5 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +23 -2
- package/dist/client/index.js.map +1 -1
- package/package.json +30 -21
- package/src/client/index.ts +32 -2
package/README.md
CHANGED
|
@@ -29,30 +29,7 @@ export default app;
|
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
your own app modules. That keeps auth, permission checks, and business rules
|
|
34
|
-
close to the rest of your application code.
|
|
35
|
-
|
|
36
|
-
Expose checkpoint submissions as Convex mutations by default. Mutations are the
|
|
37
|
-
right fit when recording a checkpoint, updating Convex data, reading Convex
|
|
38
|
-
data, or scheduling follow-up work from a handler.
|
|
39
|
-
|
|
40
|
-
Use an action only when the submit path needs external, non-transactional work,
|
|
41
|
-
such as calling a third-party API, sending an email directly, using Node APIs,
|
|
42
|
-
or doing longer-running processing. For most integrations, keep the checkpoint
|
|
43
|
-
submission in a mutation and schedule an internal action from the checkpoint
|
|
44
|
-
handler:
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
checkpoints.on("user.signup", async (ctx, payload) => {
|
|
48
|
-
await ctx.scheduler.runAfter(0, internal.emails.sendWelcome, {
|
|
49
|
-
userId: payload.userId,
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Actions are not database transactions, so design retries and idempotency
|
|
55
|
-
explicitly when external systems are involved.
|
|
32
|
+
There are two steps: define your checkpoint registry with handlers, then expose submit mutations for your app to call.
|
|
56
33
|
|
|
57
34
|
```ts
|
|
58
35
|
// convex/checkpoints.ts
|
|
@@ -62,10 +39,11 @@ import { v } from "convex/values";
|
|
|
62
39
|
import { ConvexCheckpoints } from "@abdssamie/convex-checkpoints";
|
|
63
40
|
|
|
64
41
|
export const checkpoints = new ConvexCheckpoints<{
|
|
65
|
-
"user.signup": { userId: string };
|
|
42
|
+
"user.signup": { userId: string; email: string };
|
|
66
43
|
"post.created": { userId: string; postId: string; title: string };
|
|
67
44
|
}>(components.convexCheckpoints);
|
|
68
45
|
|
|
46
|
+
// Handlers run when a checkpoint is first recorded (deduplicated by idempotencyKey)
|
|
69
47
|
checkpoints.on("user.signup", async (ctx, payload) => {
|
|
70
48
|
await ctx.scheduler.runAfter(30 * 60 * 1000, internal.emails.welcome, {
|
|
71
49
|
userId: payload.userId,
|
|
@@ -85,6 +63,7 @@ checkpoints.on("post.created", async (ctx, payload) => {
|
|
|
85
63
|
}
|
|
86
64
|
});
|
|
87
65
|
|
|
66
|
+
// Submit mutations — called from your app code
|
|
88
67
|
export const submitPostCreated = mutation({
|
|
89
68
|
args: {
|
|
90
69
|
userId: v.string(),
|
|
@@ -144,34 +123,48 @@ checkpoint ID and skip handler re-execution.
|
|
|
144
123
|
|
|
145
124
|
## HTTP
|
|
146
125
|
|
|
147
|
-
|
|
126
|
+
Prefer mutation wrappers for app-driven checkpoints. They run inside your app's
|
|
127
|
+
Convex functions, so you can authenticate the user, validate the input, write
|
|
128
|
+
app data, and submit the checkpoint in one place.
|
|
129
|
+
|
|
130
|
+
HTTP ingestion is for server-to-server entry points such as webhooks or trusted
|
|
131
|
+
backend jobs. Treat it as a public endpoint: anyone who can reach the URL can
|
|
132
|
+
try to submit a checkpoint unless you verify the request.
|
|
148
133
|
|
|
149
134
|
```ts
|
|
150
135
|
// convex/http.ts
|
|
151
136
|
import { checkpoints } from "./checkpoints.js";
|
|
152
137
|
|
|
153
|
-
export default checkpoints.http("/checkpoints"
|
|
138
|
+
export default checkpoints.http("/checkpoints", {
|
|
139
|
+
token: process.env.CHECKPOINTS_SECRET,
|
|
140
|
+
});
|
|
154
141
|
```
|
|
155
142
|
|
|
143
|
+
Requests must include `Authorization: Bearer <token>`. Keep
|
|
144
|
+
`CHECKPOINTS_SECRET` in server-side environment variables only. If the token
|
|
145
|
+
option is configured but the environment variable is missing or empty, HTTP
|
|
146
|
+
checkpoint submissions are rejected.
|
|
147
|
+
|
|
156
148
|
This registers both `POST /checkpoints` and checkpoint-specific routes such as
|
|
157
149
|
`POST /checkpoints/post.created`.
|
|
158
150
|
|
|
159
|
-
For checkpoint-specific routes, the
|
|
160
|
-
payload:
|
|
151
|
+
For checkpoint-specific routes, the route selects the checkpoint name and the
|
|
152
|
+
request body becomes the payload:
|
|
161
153
|
|
|
162
154
|
```sh
|
|
163
155
|
curl -X POST "$CONVEX_SITE_URL/checkpoints/post.created" \
|
|
164
156
|
-H "Content-Type: application/json" \
|
|
157
|
+
-H "Authorization: Bearer $CHECKPOINTS_SECRET" \
|
|
165
158
|
-d '{"userId":"user1","postId":"post1"}'
|
|
166
159
|
```
|
|
167
160
|
|
|
168
|
-
|
|
169
|
-
|
|
161
|
+
For webhook providers, verify the provider's signature in `authorize`. The
|
|
162
|
+
request is cloned before authorization runs, so signature checks can read the
|
|
163
|
+
body without preventing checkpoint parsing afterward.
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
validators because TypeScript types are not available at runtime.
|
|
165
|
+
Do not put a checkpoint HTTP secret in browser code. If a user action in your
|
|
166
|
+
app should submit a checkpoint, expose a Convex mutation instead and call
|
|
167
|
+
`checkpoints.submit(ctx, ...)` from that mutation.
|
|
175
168
|
|
|
176
169
|
## Tests
|
|
177
170
|
|
package/dist/client/index.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ type SubmitArgs<TCheckpointRegistry extends CheckpointRegistry, TCheckpoint exte
|
|
|
11
11
|
name: TCheckpoint;
|
|
12
12
|
payload: TCheckpointRegistry[TCheckpoint];
|
|
13
13
|
};
|
|
14
|
+
type HttpOptions = {
|
|
15
|
+
token?: string;
|
|
16
|
+
authorize?: (request: Request) => boolean | Promise<boolean>;
|
|
17
|
+
};
|
|
14
18
|
export declare class ConvexCheckpoints<TCheckpointRegistry extends CheckpointRegistry> extends CheckpointDispatcher<TCheckpointRegistry> {
|
|
15
19
|
private component;
|
|
16
20
|
constructor(component: ComponentApi);
|
|
@@ -55,7 +59,7 @@ export declare class ConvexCheckpoints<TCheckpointRegistry extends CheckpointReg
|
|
|
55
59
|
userId?: string;
|
|
56
60
|
}[]>>;
|
|
57
61
|
};
|
|
58
|
-
http(path?: string): import("convex/server").HttpRouter;
|
|
62
|
+
http(path?: string, options?: HttpOptions): import("convex/server").HttpRouter;
|
|
59
63
|
private submitFromArgs;
|
|
60
64
|
}
|
|
61
65
|
export type { CheckpointHandler };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EACL,iBAAiB,IAAI,oBAAoB,EACzC,KAAK,iBAAiB,EACvB,MAAM,sCAAsC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClD,KAAK,cAAc,CAAC,mBAAmB,SAAS,kBAAkB,IAAI,OAAO,CAC3E,MAAM,mBAAmB,EACzB,MAAM,CACP,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAOF,KAAK,UAAU,CACb,mBAAmB,SAAS,kBAAkB,EAC9C,WAAW,SAAS,cAAc,CAAC,mBAAmB,CAAC,IACrD,cAAc,GAAG;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF,qBAAa,iBAAiB,CAC5B,mBAAmB,SAAS,kBAAkB,CAC9C,SAAQ,oBAAoB,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,YAAY;IAI9B,MAAM,CAAC,WAAW,SAAS,cAAc,CAAC,mBAAmB,CAAC,EACzE,GAAG,EAAE,UAAU,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EACrE,IAAI,EAAE,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC;IAS7C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuBH,IAAI,CAAC,IAAI,SAAiB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EACL,iBAAiB,IAAI,oBAAoB,EACzC,KAAK,iBAAiB,EACvB,MAAM,sCAAsC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClD,KAAK,cAAc,CAAC,mBAAmB,SAAS,kBAAkB,IAAI,OAAO,CAC3E,MAAM,mBAAmB,EACzB,MAAM,CACP,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAOF,KAAK,UAAU,CACb,mBAAmB,SAAS,kBAAkB,EAC9C,WAAW,SAAS,cAAc,CAAC,mBAAmB,CAAC,IACrD,cAAc,GAAG;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D,CAAC;AAEF,qBAAa,iBAAiB,CAC5B,mBAAmB,SAAS,kBAAkB,CAC9C,SAAQ,oBAAoB,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,YAAY;IAI9B,MAAM,CAAC,WAAW,SAAS,cAAc,CAAC,mBAAmB,CAAC,EACzE,GAAG,EAAE,UAAU,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EACrE,IAAI,EAAE,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC;IAS7C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuBH,IAAI,CAAC,IAAI,SAAiB,EAAE,OAAO,GAAE,WAAgB;YAgE9C,cAAc;CAc7B;AAED,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
|
package/dist/client/index.js
CHANGED
|
@@ -36,12 +36,15 @@ export class ConvexCheckpoints extends CheckpointDispatcher {
|
|
|
36
36
|
}),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
http(path = "/checkpoints") {
|
|
39
|
+
http(path = "/checkpoints", options = {}) {
|
|
40
40
|
const http = httpRouter();
|
|
41
41
|
http.route({
|
|
42
42
|
path,
|
|
43
43
|
method: "POST",
|
|
44
44
|
handler: httpActionGeneric(async (ctx, request) => {
|
|
45
|
+
if (!(await isAuthorized(request, options))) {
|
|
46
|
+
return json({ error: "unauthorized" }, 401);
|
|
47
|
+
}
|
|
45
48
|
const args = await readSubmitArgsFromBody(request);
|
|
46
49
|
if (args === null) {
|
|
47
50
|
return json({ error: "invalid_checkpoint" }, 400);
|
|
@@ -60,6 +63,9 @@ export class ConvexCheckpoints extends CheckpointDispatcher {
|
|
|
60
63
|
pathPrefix,
|
|
61
64
|
method: "POST",
|
|
62
65
|
handler: httpActionGeneric(async (ctx, request) => {
|
|
66
|
+
if (!(await isAuthorized(request, options))) {
|
|
67
|
+
return json({ error: "unauthorized" }, 401);
|
|
68
|
+
}
|
|
63
69
|
const checkpointName = readCheckpointNameFromPath(request, pathPrefix);
|
|
64
70
|
if (checkpointName === null) {
|
|
65
71
|
return json({ error: "invalid_checkpoint_path" }, 400);
|
|
@@ -87,6 +93,21 @@ export class ConvexCheckpoints extends CheckpointDispatcher {
|
|
|
87
93
|
return result;
|
|
88
94
|
}
|
|
89
95
|
}
|
|
96
|
+
async function isAuthorized(request, options) {
|
|
97
|
+
if ("token" in options) {
|
|
98
|
+
if (typeof options.token !== "string" || options.token.length === 0) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const authorization = request.headers.get("authorization");
|
|
102
|
+
if (authorization !== `Bearer ${options.token}`) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (options.authorize === undefined) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return await options.authorize(request.clone());
|
|
110
|
+
}
|
|
90
111
|
async function readSubmitArgsFromBody(request) {
|
|
91
112
|
const checkpoint = await readJsonObject(request);
|
|
92
113
|
if (checkpoint === null || typeof checkpoint.name !== "string") {
|
|
@@ -171,7 +192,7 @@ function corsHeaders() {
|
|
|
171
192
|
return {
|
|
172
193
|
"Access-Control-Allow-Origin": "*",
|
|
173
194
|
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
174
|
-
"Access-Control-Allow-Headers": "Content-Type",
|
|
195
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
175
196
|
};
|
|
176
197
|
}
|
|
177
198
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,OAAO,EACL,iBAAiB,IAAI,oBAAoB,GAE1C,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abdssamie/convex-checkpoints",
|
|
3
3
|
"description": "A Convex component for event-driven user checkpoints.",
|
|
4
|
-
"repository":
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://gitlab.com/Abdssamie/convex-checkpoints.git"
|
|
7
|
+
},
|
|
5
8
|
"homepage": "https://github.com/abdssamie/convex-checkpoints#readme",
|
|
6
9
|
"bugs": {
|
|
7
10
|
"url": "https://github.com/abdssamie/convex-checkpoints/issues"
|
|
8
11
|
},
|
|
9
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.7",
|
|
10
13
|
"license": "Apache-2.0",
|
|
11
14
|
"keywords": [
|
|
12
15
|
"convex",
|
|
@@ -15,6 +18,25 @@
|
|
|
15
18
|
"checkpoints"
|
|
16
19
|
],
|
|
17
20
|
"type": "module",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "convex dev --start 'npm run dev:build'",
|
|
23
|
+
"dev:frontend": "cd example && vite",
|
|
24
|
+
"dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build:codegen' --initial",
|
|
25
|
+
"predev": "convex init && npm run build:codegen",
|
|
26
|
+
"build": "tsc --project ./tsconfig.build.json",
|
|
27
|
+
"build:codegen": "npx convex codegen --component-dir ./src/component && npm run build",
|
|
28
|
+
"build:clean": "rm -rf dist *.tsbuildinfo && npm run build:codegen",
|
|
29
|
+
"typecheck": "tsc --noEmit && tsc -p example && tsc -p example/convex",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"test": "vitest run --typecheck",
|
|
32
|
+
"test:watch": "vitest --typecheck --clearScreen false",
|
|
33
|
+
"test:debug": "vitest --inspect-brk --no-file-parallelism",
|
|
34
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text",
|
|
35
|
+
"preversion": "npm ci && npm run build:clean && npm run test && npm run lint && npm run typecheck",
|
|
36
|
+
"alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags",
|
|
37
|
+
"release": "npm version patch && npm publish && git push --follow-tags",
|
|
38
|
+
"version": "(npm whoami || npm login) && vim -c 'normal o' -c 'normal o## '$npm_package_version CHANGELOG.md && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
39
|
+
},
|
|
18
40
|
"files": [
|
|
19
41
|
"dist",
|
|
20
42
|
"src"
|
|
@@ -72,23 +94,10 @@
|
|
|
72
94
|
},
|
|
73
95
|
"types": "./dist/client/index.d.ts",
|
|
74
96
|
"module": "./dist/client/index.js",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"build": "tsc --project ./tsconfig.build.json",
|
|
81
|
-
"build:codegen": "npx convex codegen --component-dir ./src/component && npm run build",
|
|
82
|
-
"build:clean": "rm -rf dist *.tsbuildinfo && npm run build:codegen",
|
|
83
|
-
"typecheck": "tsc --noEmit && tsc -p example && tsc -p example/convex",
|
|
84
|
-
"lint": "eslint .",
|
|
85
|
-
"test": "vitest run --typecheck",
|
|
86
|
-
"test:watch": "vitest --typecheck --clearScreen false",
|
|
87
|
-
"test:debug": "vitest --inspect-brk --no-file-parallelism",
|
|
88
|
-
"test:coverage": "vitest run --coverage --coverage.reporter=text",
|
|
89
|
-
"preversion": "npm ci && npm run build:clean && npm run test && npm run lint && npm run typecheck",
|
|
90
|
-
"alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags",
|
|
91
|
-
"release": "npm version patch && npm publish && git push --follow-tags",
|
|
92
|
-
"version": "(npm whoami || npm login) && vim -c 'normal o' -c 'normal o## '$npm_package_version CHANGELOG.md && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
97
|
+
"pnpm": {
|
|
98
|
+
"onlyBuiltDependencies": [
|
|
99
|
+
"esbuild",
|
|
100
|
+
"@tailwindcss/oxide"
|
|
101
|
+
]
|
|
93
102
|
}
|
|
94
|
-
}
|
|
103
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -31,6 +31,11 @@ type SubmitArgs<
|
|
|
31
31
|
payload: TCheckpointRegistry[TCheckpoint];
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
type HttpOptions = {
|
|
35
|
+
token?: string;
|
|
36
|
+
authorize?: (request: Request) => boolean | Promise<boolean>;
|
|
37
|
+
};
|
|
38
|
+
|
|
34
39
|
export class ConvexCheckpoints<
|
|
35
40
|
TCheckpointRegistry extends CheckpointRegistry,
|
|
36
41
|
> extends CheckpointDispatcher<TCheckpointRegistry> {
|
|
@@ -72,12 +77,16 @@ export class ConvexCheckpoints<
|
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
public http(path = "/checkpoints") {
|
|
80
|
+
public http(path = "/checkpoints", options: HttpOptions = {}) {
|
|
76
81
|
const http = httpRouter();
|
|
77
82
|
http.route({
|
|
78
83
|
path,
|
|
79
84
|
method: "POST",
|
|
80
85
|
handler: httpActionGeneric(async (ctx, request) => {
|
|
86
|
+
if (!(await isAuthorized(request, options))) {
|
|
87
|
+
return json({ error: "unauthorized" }, 401);
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
const args = await readSubmitArgsFromBody(request);
|
|
82
91
|
if (args === null) {
|
|
83
92
|
return json({ error: "invalid_checkpoint" }, 400);
|
|
@@ -102,6 +111,10 @@ export class ConvexCheckpoints<
|
|
|
102
111
|
pathPrefix,
|
|
103
112
|
method: "POST",
|
|
104
113
|
handler: httpActionGeneric(async (ctx, request) => {
|
|
114
|
+
if (!(await isAuthorized(request, options))) {
|
|
115
|
+
return json({ error: "unauthorized" }, 401);
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
const checkpointName = readCheckpointNameFromPath(request, pathPrefix);
|
|
106
119
|
if (checkpointName === null) {
|
|
107
120
|
return json({ error: "invalid_checkpoint_path" }, 400);
|
|
@@ -146,6 +159,23 @@ export class ConvexCheckpoints<
|
|
|
146
159
|
|
|
147
160
|
export type { CheckpointHandler };
|
|
148
161
|
|
|
162
|
+
async function isAuthorized(request: Request, options: HttpOptions) {
|
|
163
|
+
if ("token" in options) {
|
|
164
|
+
if (typeof options.token !== "string" || options.token.length === 0) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
const authorization = request.headers.get("authorization");
|
|
168
|
+
if (authorization !== `Bearer ${options.token}`) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (options.authorize === undefined) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return await options.authorize(request.clone());
|
|
177
|
+
}
|
|
178
|
+
|
|
149
179
|
async function readSubmitArgsFromBody(
|
|
150
180
|
request: Request,
|
|
151
181
|
): Promise<UntypedSubmitArgs | null> {
|
|
@@ -259,6 +289,6 @@ function corsHeaders() {
|
|
|
259
289
|
return {
|
|
260
290
|
"Access-Control-Allow-Origin": "*",
|
|
261
291
|
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
262
|
-
"Access-Control-Allow-Headers": "Content-Type",
|
|
292
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
263
293
|
};
|
|
264
294
|
}
|