Skip to content

Commit 32275ed

Browse files
committed
r
1 parent 7c31138 commit 32275ed

File tree

12 files changed

+206
-8
lines changed

12 files changed

+206
-8
lines changed

.changeset/pre.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"tame-otters-shake",
5050
"warm-books-compete",
5151
"witty-chefs-camp",
52-
"witty-rockets-melt"
52+
"witty-rockets-melt",
53+
"young-camels-play"
5354
]
5455
}

.changeset/young-camels-play.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"basehub": patch
3+
---
4+
5+
auth webhooks

packages/basehub/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# basehub
22

3+
## 8.0.0-canary.41
4+
5+
### Patch Changes
6+
7+
- auth webhooks
8+
39
## 8.0.0-canary.40
410

511
### Patch Changes

packages/basehub/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "basehub",
33
"description": "A very fast Headless CMS.",
44
"author": "JB <[email protected]>",
5-
"version": "8.0.0-canary.40",
5+
"version": "8.0.0-canary.41",
66
"license": "MIT",
77
"repository": "basehub-ai/basehub",
88
"bugs": "https://github.com/basehub-ai/basehub/issues",
@@ -26,6 +26,7 @@
2626
"src/next/toolbar",
2727
"src/react/pump",
2828
"src/events",
29+
"src/workflows",
2930
"react-svg.js",
3031
"react-svg.d.ts",
3132
"react-rich-text.js",
@@ -38,6 +39,8 @@
3839
"api-transaction.d.ts",
3940
"events.js",
4041
"events.d.ts",
42+
"workflows.js",
43+
"workflows.d.ts",
4144
"search.js",
4245
"search.d.ts",
4346
"next-image.js",
@@ -52,7 +55,7 @@
5255
"build:client": "tsup --config tsup-client.config.ts"
5356
},
5457
"dependencies": {
55-
"@basehub/genql": "9.0.0-canary.9",
58+
"@basehub/genql": "9.0.0-canary.10",
5659
"@basehub/mutation-api-helpers": "2.0.7",
5760
"@radix-ui/react-slot": "^1.1.0",
5861
"@shikijs/transformers": "1.17.7",

packages/basehub/src/bin/main.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ import type { Language } from './react-code-block';
320320
const reactPumpOutDir = path.join(basehubOutputPath, "react-pump");
321321
const nextToolbarOutDir = path.join(basehubOutputPath, "next-toolbar");
322322
const analyticsOutDir = path.join(basehubOutputPath, "events");
323+
const workflowsOutDir = path.join(basehubOutputPath, "workflows");
323324

324325
await esbuild.build({
325326
entryPoints: [generatedMainExportPath],
@@ -414,13 +415,18 @@ import type { Language } from './react-code-block';
414415
path.join(basehubModulePath, "dts", "src", "events"),
415416
analyticsOutDir
416417
);
418+
copyDirSync(
419+
path.join(basehubModulePath, "dts", "src", "workflows"),
420+
workflowsOutDir
421+
);
417422

418423
if (args["--debug"]) {
419424
console.log(`[basehub] copied dts for react pump to: ${reactPumpOutDir}`);
420425
console.log(
421426
`[basehub] copied dts for next toolbar to: ${nextToolbarOutDir}`
422427
);
423428
console.log(`[basehub] copied dts for events to: ${analyticsOutDir}`);
429+
console.log(`[basehub] copied dts for workflows to: ${workflowsOutDir}`);
424430
}
425431

426432
appendGeneratedCodeBanner(basehubOutputPath, args["--banner"]);
@@ -486,6 +492,11 @@ import type { Language } from './react-code-block';
486492
);
487493
const analyticsIndexJsPath = path.join(basehubModulePath, "events.js");
488494
const analyticsIndexDtsPath = path.join(basehubModulePath, "events.d.ts");
495+
const workflowsIndexJsPath = path.join(basehubModulePath, "workflows.js");
496+
const workflowsIndexDtsPath = path.join(
497+
basehubModulePath,
498+
"workflows.d.ts"
499+
);
489500
fs.writeFileSync(
490501
indexJsPath,
491502
ensureCrossPlatformTsImport(
@@ -558,6 +569,24 @@ import type { Language } from './react-code-block';
558569
)}";`
559570
)
560571
);
572+
fs.writeFileSync(
573+
workflowsIndexJsPath,
574+
ensureCrossPlatformTsImport(
575+
`export * from "${path.relative(
576+
basehubModulePath,
577+
path.join(workflowsOutDir, "index.js")
578+
)}";`
579+
)
580+
);
581+
fs.writeFileSync(
582+
workflowsIndexDtsPath,
583+
ensureCrossPlatformTsImport(
584+
`export * from "${path.relative(
585+
basehubModulePath,
586+
path.join(workflowsOutDir, "index.d.ts")
587+
)}";`
588+
)
589+
);
561590

562591
if (args["--debug"]) {
563592
console.log(
@@ -572,6 +601,9 @@ import type { Language } from './react-code-block';
572601
console.log(
573602
`[basehub] aliased events index.js and index.d.ts to: ${analyticsOutDir}`
574603
);
604+
console.log(
605+
`[basehub] aliased workflows index.js and index.d.ts to: ${workflowsOutDir}`
606+
);
575607
}
576608
}
577609

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./primitive";
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/* eslint-disable turbo/no-undeclared-env-vars */
2+
import {
3+
// @ts-ignore
4+
Scalars,
5+
// @ts-ignore
6+
// eslint-disable-next-line import/no-unresolved
7+
} from "../schema";
8+
import crypto from "crypto";
9+
10+
/* -------------------------------------------------------------------------------------------------
11+
* Client
12+
* -----------------------------------------------------------------------------------------------*/
13+
14+
type KeysStartingWith<Obj, Prefix extends string> = {
15+
[K in keyof Obj]: K extends `${Prefix}${string}` ? K : never;
16+
}[keyof Obj];
17+
18+
type ExtractWorkflowKey<T extends string> = T extends `${infer Base}:${string}`
19+
? Base
20+
: T;
21+
22+
// Get all event key types (bshb_event_*)
23+
export type WorkflowKeys = KeysStartingWith<Scalars, "bshb_workflow">;
24+
25+
// Map from event key to its schema type
26+
type WorkflowSchemaMap = {
27+
// @ts-ignore
28+
[K in WorkflowKeys]: Scalars[`schema_${K}`];
29+
};
30+
31+
export const authenticateWebhook = async <
32+
Key extends `${WorkflowKeys}:${string}`,
33+
>({
34+
secret,
35+
body,
36+
signature,
37+
}: {
38+
/**
39+
* The body of the incoming webhook request
40+
* Can be:
41+
* - Parsed JSON from request.json()
42+
* - Raw string from request.text()
43+
* - ReadableStream from request.body
44+
*/
45+
body: unknown;
46+
/**
47+
* The signature of the incoming webhook request—you get this via request.headers["x-basehub-webhook-signature"]
48+
* This should be a hex-encoded HMAC SHA-256 hash of the request body
49+
*/
50+
signature: string;
51+
/**
52+
* The secret used for verifying the incoming webhook request—you get this via the BaseHub API
53+
* This secret should never be exposed in requests or responses
54+
*/
55+
secret: Key;
56+
}): Promise<
57+
| { success: true; payload: WorkflowSchemaMap[ExtractWorkflowKey<Key>] }
58+
| { success: false; error: string }
59+
> => {
60+
try {
61+
// Handle different body types
62+
let rawBody: string;
63+
let parsedBody: unknown;
64+
65+
if (body instanceof ReadableStream) {
66+
// Convert stream to text
67+
const reader = body.getReader();
68+
const chunks = [];
69+
while (true) {
70+
const { done, value } = await reader.read();
71+
if (done) break;
72+
chunks.push(value);
73+
}
74+
const bodyText = new TextDecoder().decode(
75+
new Uint8Array(chunks.flatMap((chunk) => Array.from(chunk)))
76+
);
77+
rawBody = bodyText;
78+
parsedBody = JSON.parse(bodyText);
79+
} else if (typeof body === "string") {
80+
rawBody = body;
81+
parsedBody = JSON.parse(body);
82+
} else {
83+
// Already parsed JSON
84+
rawBody = JSON.stringify(body);
85+
parsedBody = body;
86+
}
87+
88+
if (typeof parsedBody !== "object" || parsedBody === null) {
89+
return { success: false, error: "Invalid body" };
90+
}
91+
92+
const encoder = new TextEncoder();
93+
const bodyData = encoder.encode(rawBody);
94+
const secretData = encoder.encode(secret);
95+
96+
const key = await crypto.subtle.importKey(
97+
"raw",
98+
secretData,
99+
{ name: "HMAC", hash: "SHA-256" },
100+
false,
101+
["sign"]
102+
);
103+
104+
const signed = await crypto.subtle.sign("HMAC", key, bodyData);
105+
const calculatedSignature = Array.from(new Uint8Array(signed))
106+
.map((b) => b.toString(16).padStart(2, "0"))
107+
.join("");
108+
109+
if (signature.length !== calculatedSignature.length) {
110+
return { success: false, error: "Invalid signature" };
111+
}
112+
113+
let mismatch = 0;
114+
for (let i = 0; i < signature.length; i++) {
115+
mismatch |= signature.charCodeAt(i) ^ calculatedSignature.charCodeAt(i);
116+
}
117+
118+
if (mismatch !== 0) {
119+
return { success: false, error: "Invalid signature" };
120+
}
121+
122+
return {
123+
success: true,
124+
payload: parsedBody as WorkflowSchemaMap[ExtractWorkflowKey<Key>],
125+
};
126+
} catch (error) {
127+
return {
128+
success: false,
129+
error:
130+
error instanceof Error
131+
? error.message
132+
: "Signature verification failed",
133+
};
134+
}
135+
};

packages/basehub/workflows.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* eslint-disable import/no-unresolved */
2+
export * from "./dist/generated-client/workflows";

packages/basehub/workflows.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
throw new Error(
2+
`\`workflows\` not found. Make sure to run \`npx basehub\` in order to generate it.
3+
4+
If the error persist, please raise an issue at https://github.com/basehub-ai/basehub
5+
`
6+
);

playground/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# playground
22

3+
## 0.0.164-canary.41
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
9+
310
## 0.0.164-canary.40
411

512
### Patch Changes

0 commit comments

Comments
 (0)