@shipfox/api-integration-webhook 5.0.0 → 7.1.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +32 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/webhook-processor.d.ts +13 -0
- package/dist/core/webhook-processor.d.ts.map +1 -0
- package/dist/core/webhook-processor.js +110 -0
- package/dist/core/webhook-processor.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/inbound.d.ts +2 -0
- package/dist/presentation/routes/inbound.d.ts.map +1 -1
- package/dist/presentation/routes/inbound.js +72 -42
- package/dist/presentation/routes/inbound.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -15
- package/src/constants.ts +6 -0
- package/src/core/webhook-processor.test.ts +87 -0
- package/src/core/webhook-processor.ts +128 -0
- package/src/index.ts +9 -0
- package/src/presentation/routes/inbound.test.ts +8 -3
- package/src/presentation/routes/inbound.ts +94 -44
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 7 files with swc (417.6ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @shipfox/api-integration-webhook
|
|
2
2
|
|
|
3
|
+
## 7.1.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [ac42c96]
|
|
8
|
+
- Updated dependencies [6ce08c0]
|
|
9
|
+
- @shipfox/node-fastify@0.3.0
|
|
10
|
+
- @shipfox/node-opentelemetry@0.6.0
|
|
11
|
+
- @shipfox/api-auth-context@7.1.0
|
|
12
|
+
|
|
13
|
+
## 6.0.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- f262539: Adds a composed webhook processor and optional provider-neutral delivery source for hosted API runtimes.
|
|
18
|
+
- e3b6338: Adds the shared processor for generic webhook deliveries.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- f73da5d: Enforces bounded API context imports and routes inter-module consumers through producer contracts.
|
|
23
|
+
- Updated dependencies [0bb82a4]
|
|
24
|
+
- Updated dependencies [f262539]
|
|
25
|
+
- Updated dependencies [3bb4e26]
|
|
26
|
+
- Updated dependencies [8bdc149]
|
|
27
|
+
- Updated dependencies [b00ed29]
|
|
28
|
+
- Updated dependencies [8aa7cd3]
|
|
29
|
+
- Updated dependencies [4604a06]
|
|
30
|
+
- @shipfox/api-integration-core-dto@6.0.0
|
|
31
|
+
- @shipfox/api-auth-context@6.0.0
|
|
32
|
+
- @shipfox/node-fastify@0.2.4
|
|
33
|
+
- @shipfox/api-integration-webhook-dto@6.0.0
|
|
34
|
+
|
|
3
35
|
## 5.0.0
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const WEBHOOK_INBOUND_BODY_LIMIT: number;
|
|
2
|
+
export declare const WEBHOOK_ACCEPTED_CONTENT_TYPES: readonly ["application/json", "application/x-www-form-urlencoded", "text/plain"];
|
|
2
3
|
export declare const WEBHOOK_FORWARDED_HEADERS: readonly ["content-type", "user-agent", "x-delivery-id", "x-request-id"];
|
|
3
4
|
export declare function redactHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string | string[]>;
|
|
4
5
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D,eAAO,MAAM,yBAAyB,0EAK5B,CAAC;AAIX,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAOnC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D,eAAO,MAAM,8BAA8B,kFAIjC,CAAC;AAEX,eAAO,MAAM,yBAAyB,0EAK5B,CAAC;AAIX,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAOnC"}
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;
|
|
2
|
+
export const WEBHOOK_ACCEPTED_CONTENT_TYPES = [
|
|
3
|
+
'application/json',
|
|
4
|
+
'application/x-www-form-urlencoded',
|
|
5
|
+
'text/plain'
|
|
6
|
+
];
|
|
2
7
|
export const WEBHOOK_FORWARDED_HEADERS = [
|
|
3
8
|
'content-type',
|
|
4
9
|
'user-agent',
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;\n\nexport const WEBHOOK_FORWARDED_HEADERS = [\n 'content-type',\n 'user-agent',\n 'x-delivery-id',\n 'x-request-id',\n] as const;\n\nconst forwardedHeaderSet = new Set<string>(WEBHOOK_FORWARDED_HEADERS);\n\nexport function redactHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string | string[]> {\n const redacted: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n redacted[name] = forwardedHeaderSet.has(name.toLowerCase()) ? value : '[redacted]';\n }\n return redacted;\n}\n"],"names":["WEBHOOK_INBOUND_BODY_LIMIT","WEBHOOK_FORWARDED_HEADERS","forwardedHeaderSet","Set","redactHeaders","headers","redacted","name","value","Object","entries","undefined","has","toLowerCase"],"mappings":"AAAA,OAAO,MAAMA,6BAA6B,IAAI,OAAO,KAAK;AAE1D,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;CACD,CAAU;AAEX,MAAMC,qBAAqB,IAAIC,IAAYF;AAE3C,OAAO,SAASG,cACdC,OAAsD;IAEtD,MAAMC,WAA8C,CAAC;IACrD,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACL,SAAU;QACnD,IAAIG,UAAUG,WAAW;QACzBL,QAAQ,CAACC,KAAK,GAAGL,mBAAmBU,GAAG,CAACL,KAAKM,WAAW,MAAML,QAAQ;IACxE;IACA,OAAOF;AACT"}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;\n\nexport const WEBHOOK_ACCEPTED_CONTENT_TYPES = [\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'text/plain',\n] as const;\n\nexport const WEBHOOK_FORWARDED_HEADERS = [\n 'content-type',\n 'user-agent',\n 'x-delivery-id',\n 'x-request-id',\n] as const;\n\nconst forwardedHeaderSet = new Set<string>(WEBHOOK_FORWARDED_HEADERS);\n\nexport function redactHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string | string[]> {\n const redacted: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n redacted[name] = forwardedHeaderSet.has(name.toLowerCase()) ? value : '[redacted]';\n }\n return redacted;\n}\n"],"names":["WEBHOOK_INBOUND_BODY_LIMIT","WEBHOOK_ACCEPTED_CONTENT_TYPES","WEBHOOK_FORWARDED_HEADERS","forwardedHeaderSet","Set","redactHeaders","headers","redacted","name","value","Object","entries","undefined","has","toLowerCase"],"mappings":"AAAA,OAAO,MAAMA,6BAA6B,IAAI,OAAO,KAAK;AAE1D,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;CACD,CAAU;AAEX,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;CACD,CAAU;AAEX,MAAMC,qBAAqB,IAAIC,IAAYF;AAE3C,OAAO,SAASG,cACdC,OAAsD;IAEtD,MAAMC,WAA8C,CAAC;IACrD,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACL,SAAU;QACnD,IAAIG,UAAUG,WAAW;QACzBL,QAAQ,CAACC,KAAK,GAAGL,mBAAmBU,GAAG,CAACL,KAAKM,WAAW,MAAML,QAAQ;IACxE;IACA,OAAOF;AACT"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type GetIntegrationConnectionByIdFn, type IntegrationConnection, type IntegrationTx, type PublishIntegrationEventReceivedFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
export interface CreateGenericWebhookProcessorOptions {
|
|
3
|
+
coreDb: () => {
|
|
4
|
+
transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
|
|
5
|
+
};
|
|
6
|
+
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
7
|
+
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
8
|
+
}
|
|
9
|
+
export interface GenericWebhookProcessor {
|
|
10
|
+
process(request: StoredWebhookRequest, connection?: IntegrationConnection | undefined): Promise<WebhookProcessingResult>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createGenericWebhookProcessor(options: CreateGenericWebhookProcessorOptions): GenericWebhookProcessor;
|
|
13
|
+
//# sourceMappingURL=webhook-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-processor.d.ts","sourceRoot":"","sources":["../../src/core/webhook-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAO3C,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;CACpE;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CACL,OAAO,EAAE,oBAAoB,EAC7B,UAAU,CAAC,EAAE,qBAAqB,GAAG,SAAS,GAC7C,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,uBAAuB,CAIzB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT } from '@shipfox/api-integration-webhook-dto';
|
|
4
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
5
|
+
import { redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES } from '#constants.js';
|
|
6
|
+
const [JSON_CONTENT_TYPE, FORM_CONTENT_TYPE, TEXT_CONTENT_TYPE] = WEBHOOK_ACCEPTED_CONTENT_TYPES;
|
|
7
|
+
export function createGenericWebhookProcessor(options) {
|
|
8
|
+
return {
|
|
9
|
+
process: (request, connection)=>processWebhookRequest(options, request, connection)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function processWebhookRequest(options, request, knownConnection) {
|
|
13
|
+
if (request.route_id !== 'webhook.connection') {
|
|
14
|
+
throw new Error(`Generic webhook processor cannot process ${request.route_id} requests`);
|
|
15
|
+
}
|
|
16
|
+
const connection = knownConnection ?? await options.getIntegrationConnectionById(request.path_parameters.connection_id);
|
|
17
|
+
if (!connection || connection.provider !== WEBHOOK_PROVIDER || connection.lifecycleStatus !== 'active') {
|
|
18
|
+
return {
|
|
19
|
+
outcome: 'discarded',
|
|
20
|
+
reason: 'connection_unavailable'
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const deliveryId = request.headers['x-delivery-id'] ?? request.request_id;
|
|
24
|
+
const body = parseWebhookBody(request);
|
|
25
|
+
if (!body.success) {
|
|
26
|
+
logger().warn({
|
|
27
|
+
deliveryId,
|
|
28
|
+
err: body.error
|
|
29
|
+
}, 'generic webhook payload parsing failed');
|
|
30
|
+
return {
|
|
31
|
+
outcome: 'discarded',
|
|
32
|
+
reason: 'malformed_payload',
|
|
33
|
+
deliveryId
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
await options.coreDb().transaction(async (tx)=>{
|
|
37
|
+
await options.publishIntegrationEventReceived({
|
|
38
|
+
tx,
|
|
39
|
+
event: {
|
|
40
|
+
provider: WEBHOOK_PROVIDER,
|
|
41
|
+
source: connection.slug,
|
|
42
|
+
event: WEBHOOK_RECEIVED_EVENT,
|
|
43
|
+
workspaceId: connection.workspaceId,
|
|
44
|
+
connectionId: connection.id,
|
|
45
|
+
connectionName: connection.displayName,
|
|
46
|
+
deliveryId,
|
|
47
|
+
receivedAt: request.received_at,
|
|
48
|
+
payload: {
|
|
49
|
+
method: request.method,
|
|
50
|
+
headers: redactHeaders(request.headers),
|
|
51
|
+
query: parseWebhookQuery(request.raw_query_string),
|
|
52
|
+
body: body.value
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
outcome: 'processed',
|
|
59
|
+
deliveryId
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function parseWebhookQuery(rawQueryString) {
|
|
63
|
+
const query = {};
|
|
64
|
+
for (const [name, value] of new URLSearchParams(rawQueryString)){
|
|
65
|
+
const existingValue = query[name];
|
|
66
|
+
if (existingValue === undefined) {
|
|
67
|
+
query[name] = value;
|
|
68
|
+
} else if (Array.isArray(existingValue)) {
|
|
69
|
+
existingValue.push(value);
|
|
70
|
+
} else {
|
|
71
|
+
query[name] = [
|
|
72
|
+
existingValue,
|
|
73
|
+
value
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return query;
|
|
78
|
+
}
|
|
79
|
+
function parseWebhookBody(request) {
|
|
80
|
+
const body = Buffer.from(decodeWebhookBody(request.body));
|
|
81
|
+
const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();
|
|
82
|
+
if (contentType === JSON_CONTENT_TYPE) {
|
|
83
|
+
try {
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
value: JSON.parse(body.toString('utf8'))
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (contentType === FORM_CONTENT_TYPE) {
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
value: Object.fromEntries(new URLSearchParams(body.toString('utf8')))
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (contentType === TEXT_CONTENT_TYPE) return {
|
|
102
|
+
success: true,
|
|
103
|
+
value: body.toString('utf8')
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
success: false
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=webhook-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook-processor.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n decodeWebhookBody,\n type GetIntegrationConnectionByIdFn,\n type IntegrationConnection,\n type IntegrationTx,\n type PublishIntegrationEventReceivedFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT} from '@shipfox/api-integration-webhook-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES} from '#constants.js';\n\nconst [JSON_CONTENT_TYPE, FORM_CONTENT_TYPE, TEXT_CONTENT_TYPE] = WEBHOOK_ACCEPTED_CONTENT_TYPES;\n\nexport interface CreateGenericWebhookProcessorOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n}\n\nexport interface GenericWebhookProcessor {\n process(\n request: StoredWebhookRequest,\n connection?: IntegrationConnection | undefined,\n ): Promise<WebhookProcessingResult>;\n}\n\nexport function createGenericWebhookProcessor(\n options: CreateGenericWebhookProcessorOptions,\n): GenericWebhookProcessor {\n return {\n process: (request, connection) => processWebhookRequest(options, request, connection),\n };\n}\n\nasync function processWebhookRequest(\n options: CreateGenericWebhookProcessorOptions,\n request: StoredWebhookRequest,\n knownConnection?: IntegrationConnection | undefined,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'webhook.connection') {\n throw new Error(`Generic webhook processor cannot process ${request.route_id} requests`);\n }\n\n const connection =\n knownConnection ??\n (await options.getIntegrationConnectionById(request.path_parameters.connection_id));\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n return {outcome: 'discarded', reason: 'connection_unavailable'};\n }\n\n const deliveryId = request.headers['x-delivery-id'] ?? request.request_id;\n const body = parseWebhookBody(request);\n if (!body.success) {\n logger().warn({deliveryId, err: body.error}, 'generic webhook payload parsing failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n\n await options.coreDb().transaction(async (tx) => {\n await options.publishIntegrationEventReceived({\n tx,\n event: {\n provider: WEBHOOK_PROVIDER,\n source: connection.slug,\n event: WEBHOOK_RECEIVED_EVENT,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n connectionName: connection.displayName,\n deliveryId,\n receivedAt: request.received_at,\n payload: {\n method: request.method,\n headers: redactHeaders(request.headers),\n query: parseWebhookQuery(request.raw_query_string),\n body: body.value,\n },\n },\n });\n });\n\n return {outcome: 'processed', deliveryId};\n}\n\nfunction parseWebhookQuery(rawQueryString: string): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [name, value] of new URLSearchParams(rawQueryString)) {\n const existingValue = query[name];\n if (existingValue === undefined) {\n query[name] = value;\n } else if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n query[name] = [existingValue, value];\n }\n }\n\n return query;\n}\n\nfunction parseWebhookBody(\n request: StoredWebhookRequest,\n): {success: true; value: unknown} | {success: false; error?: unknown} {\n const body = Buffer.from(decodeWebhookBody(request.body));\n const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();\n\n if (contentType === JSON_CONTENT_TYPE) {\n try {\n return {success: true, value: JSON.parse(body.toString('utf8'))};\n } catch (error) {\n return {success: false, error};\n }\n }\n if (contentType === FORM_CONTENT_TYPE) {\n return {success: true, value: Object.fromEntries(new URLSearchParams(body.toString('utf8')))};\n }\n if (contentType === TEXT_CONTENT_TYPE) return {success: true, value: body.toString('utf8')};\n\n return {success: false};\n}\n"],"names":["Buffer","decodeWebhookBody","WEBHOOK_PROVIDER","WEBHOOK_RECEIVED_EVENT","logger","redactHeaders","WEBHOOK_ACCEPTED_CONTENT_TYPES","JSON_CONTENT_TYPE","FORM_CONTENT_TYPE","TEXT_CONTENT_TYPE","createGenericWebhookProcessor","options","process","request","connection","processWebhookRequest","knownConnection","route_id","Error","getIntegrationConnectionById","path_parameters","connection_id","provider","lifecycleStatus","outcome","reason","deliveryId","headers","request_id","body","parseWebhookBody","success","warn","err","error","coreDb","transaction","tx","publishIntegrationEventReceived","event","source","slug","workspaceId","connectionId","id","connectionName","displayName","receivedAt","received_at","payload","method","query","parseWebhookQuery","raw_query_string","value","rawQueryString","name","URLSearchParams","existingValue","undefined","Array","isArray","push","from","contentType","split","trim","toLowerCase","JSON","parse","toString","Object","fromEntries"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CreateIntegrationConnectionFn, DeleteIntegrationConnectionFn, GetIntegrationConnectionByIdFn, IntegrationConnection, IntegrationTx, PublishIntegrationEventReceivedFn, UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-core-dto';
|
|
2
2
|
export { redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
|
|
3
|
+
export type { CreateGenericWebhookProcessorOptions, GenericWebhookProcessor, } from '#core/webhook-processor.js';
|
|
4
|
+
export { createGenericWebhookProcessor } from '#core/webhook-processor.js';
|
|
3
5
|
export interface CreateWebhookIntegrationProviderOptions {
|
|
4
6
|
coreDb: () => {
|
|
5
7
|
transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
|
|
@@ -18,5 +20,9 @@ export declare function createWebhookIntegrationProvider(options: CreateWebhookI
|
|
|
18
20
|
provider: "webhook";
|
|
19
21
|
displayName: string;
|
|
20
22
|
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
23
|
+
webhookProcessors: {
|
|
24
|
+
routeIds: readonly ["webhook.connection"];
|
|
25
|
+
processor: import("#core/webhook-processor.js").GenericWebhookProcessor;
|
|
26
|
+
}[];
|
|
21
27
|
};
|
|
22
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,qBAAqB,EACrB,aAAa,EACb,iCAAiC,EACjC,4CAA4C,EAC7C,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,qBAAqB,EACrB,aAAa,EACb,iCAAiC,EACjC,4CAA4C,EAC7C,MAAM,mCAAmC,CAAC;AAO3C,OAAO,EAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,EAAC,MAAM,eAAe,CAAC;AACnG,YAAY,EACV,oCAAoC,EACpC,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAEzE,MAAM,WAAW,uCAAuC;IACtD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,0BAA0B,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAChG,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,0CAA0C,EAAE,4CAA4C,CAAC;IACzF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,uCAAuC;;;;;;;;EAyBhG"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { WEBHOOK_PROVIDER } from '@shipfox/api-integration-webhook-dto';
|
|
2
2
|
import { config } from '#config.js';
|
|
3
|
+
import { createGenericWebhookProcessor } from '#core/webhook-processor.js';
|
|
3
4
|
import { createWebhookConnectionRoutes } from '#presentation/routes/connections.js';
|
|
4
5
|
import { createWebhookInboundRoutes } from '#presentation/routes/inbound.js';
|
|
5
6
|
export { redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
|
|
7
|
+
export { createGenericWebhookProcessor } from '#core/webhook-processor.js';
|
|
6
8
|
export function createWebhookIntegrationProvider(options) {
|
|
7
9
|
const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;
|
|
10
|
+
const webhookProcessor = createGenericWebhookProcessor(options);
|
|
8
11
|
return {
|
|
9
12
|
provider: WEBHOOK_PROVIDER,
|
|
10
13
|
displayName: 'Webhook',
|
|
@@ -20,8 +23,17 @@ export function createWebhookIntegrationProvider(options) {
|
|
|
20
23
|
createWebhookInboundRoutes({
|
|
21
24
|
coreDb: options.coreDb,
|
|
22
25
|
getIntegrationConnectionById: options.getIntegrationConnectionById,
|
|
23
|
-
publishIntegrationEventReceived: options.publishIntegrationEventReceived
|
|
26
|
+
publishIntegrationEventReceived: options.publishIntegrationEventReceived,
|
|
27
|
+
processor: webhookProcessor
|
|
24
28
|
})
|
|
29
|
+
],
|
|
30
|
+
webhookProcessors: [
|
|
31
|
+
{
|
|
32
|
+
routeIds: [
|
|
33
|
+
'webhook.connection'
|
|
34
|
+
],
|
|
35
|
+
processor: webhookProcessor
|
|
36
|
+
}
|
|
25
37
|
]
|
|
26
38
|
};
|
|
27
39
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n CreateIntegrationConnectionFn,\n DeleteIntegrationConnectionFn,\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {config} from '#config.js';\nimport {createWebhookConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createWebhookInboundRoutes} from '#presentation/routes/inbound.js';\n\nexport {redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\n\nexport interface CreateWebhookIntegrationProviderOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n baseUrl?: string | undefined;\n}\n\nexport function createWebhookIntegrationProvider(options: CreateWebhookIntegrationProviderOptions) {\n const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;\n return {\n provider: WEBHOOK_PROVIDER,\n displayName: 'Webhook',\n routes: [\n createWebhookConnectionRoutes({\n baseUrl,\n createIntegrationConnection: options.createIntegrationConnection,\n listIntegrationConnections: options.listIntegrationConnections,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus:\n options.updateIntegrationConnectionLifecycleStatus,\n deleteIntegrationConnection: options.deleteIntegrationConnection,\n }),\n createWebhookInboundRoutes({\n coreDb: options.coreDb,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n }),\n ],\n };\n}\n"],"names":["WEBHOOK_PROVIDER","config","createWebhookConnectionRoutes","createWebhookInboundRoutes","redactHeaders","WEBHOOK_FORWARDED_HEADERS","WEBHOOK_INBOUND_BODY_LIMIT","createWebhookIntegrationProvider","options","baseUrl","WEBHOOK_PUBLIC_URL","provider","displayName","routes","createIntegrationConnection","listIntegrationConnections","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","deleteIntegrationConnection","coreDb","publishIntegrationEventReceived"],"mappings":"AASA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,0BAA0B,QAAO,kCAAkC;AAE3E,SAAQC,aAAa,EAAEC,yBAAyB,EAAEC,0BAA0B,QAAO,gBAAgB;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n CreateIntegrationConnectionFn,\n DeleteIntegrationConnectionFn,\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {config} from '#config.js';\nimport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\nimport {createWebhookConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createWebhookInboundRoutes} from '#presentation/routes/inbound.js';\n\nexport {redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nexport type {\n CreateGenericWebhookProcessorOptions,\n GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\n\nexport interface CreateWebhookIntegrationProviderOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n baseUrl?: string | undefined;\n}\n\nexport function createWebhookIntegrationProvider(options: CreateWebhookIntegrationProviderOptions) {\n const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;\n const webhookProcessor = createGenericWebhookProcessor(options);\n return {\n provider: WEBHOOK_PROVIDER,\n displayName: 'Webhook',\n routes: [\n createWebhookConnectionRoutes({\n baseUrl,\n createIntegrationConnection: options.createIntegrationConnection,\n listIntegrationConnections: options.listIntegrationConnections,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus:\n options.updateIntegrationConnectionLifecycleStatus,\n deleteIntegrationConnection: options.deleteIntegrationConnection,\n }),\n createWebhookInboundRoutes({\n coreDb: options.coreDb,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n processor: webhookProcessor,\n }),\n ],\n webhookProcessors: [{routeIds: ['webhook.connection'] as const, processor: webhookProcessor}],\n };\n}\n"],"names":["WEBHOOK_PROVIDER","config","createGenericWebhookProcessor","createWebhookConnectionRoutes","createWebhookInboundRoutes","redactHeaders","WEBHOOK_FORWARDED_HEADERS","WEBHOOK_INBOUND_BODY_LIMIT","createWebhookIntegrationProvider","options","baseUrl","WEBHOOK_PUBLIC_URL","webhookProcessor","provider","displayName","routes","createIntegrationConnection","listIntegrationConnections","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","deleteIntegrationConnection","coreDb","publishIntegrationEventReceived","processor","webhookProcessors","routeIds"],"mappings":"AASA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,6BAA6B,QAAO,6BAA6B;AACzE,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,0BAA0B,QAAO,kCAAkC;AAE3E,SAAQC,aAAa,EAAEC,yBAAyB,EAAEC,0BAA0B,QAAO,gBAAgB;AAKnG,SAAQL,6BAA6B,QAAO,6BAA6B;AAezE,OAAO,SAASM,iCAAiCC,OAAgD;IAC/F,MAAMC,UAAUD,QAAQC,OAAO,IAAIT,OAAOU,kBAAkB;IAC5D,MAAMC,mBAAmBV,8BAA8BO;IACvD,OAAO;QACLI,UAAUb;QACVc,aAAa;QACbC,QAAQ;YACNZ,8BAA8B;gBAC5BO;gBACAM,6BAA6BP,QAAQO,2BAA2B;gBAChEC,4BAA4BR,QAAQQ,0BAA0B;gBAC9DC,8BAA8BT,QAAQS,4BAA4B;gBAClEC,4CACEV,QAAQU,0CAA0C;gBACpDC,6BAA6BX,QAAQW,2BAA2B;YAClE;YACAhB,2BAA2B;gBACzBiB,QAAQZ,QAAQY,MAAM;gBACtBH,8BAA8BT,QAAQS,4BAA4B;gBAClEI,iCAAiCb,QAAQa,+BAA+B;gBACxEC,WAAWX;YACb;SACD;QACDY,mBAAmB;YAAC;gBAACC,UAAU;oBAAC;iBAAqB;gBAAWF,WAAWX;YAAgB;SAAE;IAC/F;AACF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { GetIntegrationConnectionByIdFn, IntegrationTx, PublishIntegrationEventReceivedFn } from '@shipfox/api-integration-core-dto';
|
|
2
2
|
import { type RouteGroup } from '@shipfox/node-fastify';
|
|
3
|
+
import { type GenericWebhookProcessor } from '#core/webhook-processor.js';
|
|
3
4
|
export interface CreateWebhookInboundRoutesOptions {
|
|
4
5
|
coreDb: () => {
|
|
5
6
|
transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
|
|
6
7
|
};
|
|
7
8
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
8
9
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
10
|
+
processor?: GenericWebhookProcessor | undefined;
|
|
9
11
|
}
|
|
10
12
|
export declare function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup;
|
|
11
13
|
//# sourceMappingURL=inbound.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/inbound.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/inbound.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,aAAa,EACb,iCAAiC,EAClC,MAAM,mCAAmC,CAAC;AAO3C,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKhF,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAkBpC,MAAM,WAAW,iCAAiC;IAChD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,SAAS,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACjD;AA6BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,UAAU,CA8DjG"}
|
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import
|
|
3
|
-
import { WEBHOOK_PROVIDER
|
|
2
|
+
import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { WEBHOOK_PROVIDER } from '@shipfox/api-integration-webhook-dto';
|
|
4
4
|
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
5
5
|
import fp from 'fastify-plugin';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import {
|
|
7
|
+
import { WEBHOOK_ACCEPTED_CONTENT_TYPES, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
|
|
8
|
+
import { createGenericWebhookProcessor } from '#core/webhook-processor.js';
|
|
8
9
|
const MAX_DELIVERY_ID_HEADER_LENGTH = 200;
|
|
9
|
-
const acceptedContentTypes = new Set(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT
|
|
22
|
-
}, (_request, body, done)=>{
|
|
23
|
-
done(null, Object.fromEntries(new URLSearchParams(body.toString('utf8'))));
|
|
24
|
-
});
|
|
10
|
+
const acceptedContentTypes = new Set(WEBHOOK_ACCEPTED_CONTENT_TYPES);
|
|
11
|
+
const rawWebhookBodyPlugin = fp((app)=>{
|
|
12
|
+
app.removeAllContentTypeParsers();
|
|
13
|
+
for (const contentType of acceptedContentTypes){
|
|
14
|
+
app.addContentTypeParser(contentType, {
|
|
15
|
+
parseAs: 'buffer',
|
|
16
|
+
bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT
|
|
17
|
+
}, (_request, body, done)=>{
|
|
18
|
+
done(null, body);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return Promise.resolve();
|
|
25
22
|
});
|
|
26
23
|
const inboundParamsSchema = z.object({
|
|
27
24
|
connectionId: z.string().uuid()
|
|
@@ -43,7 +40,11 @@ function deliveryIdFor(header) {
|
|
|
43
40
|
}
|
|
44
41
|
return value;
|
|
45
42
|
}
|
|
43
|
+
function hasDeliveryId(header) {
|
|
44
|
+
return Boolean(Array.isArray(header) ? header[0] : header);
|
|
45
|
+
}
|
|
46
46
|
export function createWebhookInboundRoutes(options) {
|
|
47
|
+
const processor = options.processor ?? createGenericWebhookProcessor(options);
|
|
47
48
|
const inboundRoute = defineRoute({
|
|
48
49
|
method: 'POST',
|
|
49
50
|
path: '/:connectionId',
|
|
@@ -73,29 +74,20 @@ export function createWebhookInboundRoutes(options) {
|
|
|
73
74
|
status: 404
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
await
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
deliveryId,
|
|
89
|
-
receivedAt,
|
|
90
|
-
payload: {
|
|
91
|
-
method: request.method,
|
|
92
|
-
headers: redactHeaders(request.headers),
|
|
93
|
-
query: request.query,
|
|
94
|
-
body: request.body
|
|
95
|
-
}
|
|
96
|
-
}
|
|
77
|
+
const deliveryHeader = request.headers['x-delivery-id'];
|
|
78
|
+
const deliveryId = deliveryIdFor(deliveryHeader);
|
|
79
|
+
const result = await processor.process(createGenericStoredWebhookRequest({
|
|
80
|
+
body: request.body,
|
|
81
|
+
connectionId,
|
|
82
|
+
headers: request.headers,
|
|
83
|
+
rawQueryString: request.raw.url?.split('?')[1] ?? '',
|
|
84
|
+
requestId: hasDeliveryId(deliveryHeader) ? randomUUID() : deliveryId
|
|
85
|
+
}, new Date().toISOString()), connection);
|
|
86
|
+
if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {
|
|
87
|
+
throw new ClientError('Malformed webhook payload', 'malformed-payload', {
|
|
88
|
+
status: 400
|
|
97
89
|
});
|
|
98
|
-
}
|
|
90
|
+
}
|
|
99
91
|
reply.code(202);
|
|
100
92
|
return {
|
|
101
93
|
delivery_id: deliveryId
|
|
@@ -106,12 +98,50 @@ export function createWebhookInboundRoutes(options) {
|
|
|
106
98
|
prefix: '/webhook',
|
|
107
99
|
auth: [],
|
|
108
100
|
plugins: [
|
|
109
|
-
|
|
101
|
+
rawWebhookBodyPlugin
|
|
110
102
|
],
|
|
111
103
|
routes: [
|
|
112
104
|
inboundRoute
|
|
113
105
|
]
|
|
114
106
|
};
|
|
115
107
|
}
|
|
108
|
+
function createGenericStoredWebhookRequest(input, receivedAt) {
|
|
109
|
+
if (!(input.body instanceof Uint8Array)) {
|
|
110
|
+
throw new ClientError('Expected raw webhook body', 'invalid-webhook-request', {
|
|
111
|
+
status: 400
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {
|
|
115
|
+
throw new ClientError('Webhook request body is too large', 'body-too-large', {
|
|
116
|
+
status: 413
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return createStoredWebhookRequest({
|
|
121
|
+
requestId: input.requestId,
|
|
122
|
+
routeId: 'webhook.connection',
|
|
123
|
+
receivedAt,
|
|
124
|
+
rawQueryString: input.rawQueryString,
|
|
125
|
+
headers: webhookHeaders(input.headers),
|
|
126
|
+
body: input.body,
|
|
127
|
+
connectionId: input.connectionId
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {
|
|
131
|
+
cause: error
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function webhookHeaders(headers) {
|
|
136
|
+
return Object.fromEntries(Object.entries(headers).flatMap(([name, value])=>{
|
|
137
|
+
if (value === undefined) return [];
|
|
138
|
+
return [
|
|
139
|
+
[
|
|
140
|
+
name.toLowerCase(),
|
|
141
|
+
Array.isArray(value) ? value.join(', ') : value
|
|
142
|
+
]
|
|
143
|
+
];
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
116
146
|
|
|
117
147
|
//# sourceMappingURL=inbound.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/inbound.ts"],"sourcesContent":["import type {Buffer} from 'node:buffer';\nimport {randomUUID} from 'node:crypto';\nimport formBodyPlugin from '@fastify/formbody';\nimport type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyPluginAsync} from 'fastify';\nimport fp from 'fastify-plugin';\nimport {z} from 'zod';\nimport {redactHeaders, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\n\nconst MAX_DELIVERY_ID_HEADER_LENGTH = 200;\nconst acceptedContentTypes = new Set([\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'text/plain',\n]);\nconst FORM_URLENCODED_CONTENT_TYPE_RE = /^application\\/x-www-form-urlencoded(?:;.*)?$/i;\n\nconst formBodyRoutePlugin: FastifyPluginAsync = fp(async (app) => {\n await app.register(formBodyPlugin, {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT});\n\n app.addContentTypeParser(\n FORM_URLENCODED_CONTENT_TYPE_RE,\n {parseAs: 'buffer', bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n (_request, body: Buffer, done) => {\n done(null, Object.fromEntries(new URLSearchParams(body.toString('utf8'))));\n },\n );\n});\n\nexport interface CreateWebhookInboundRoutesOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n}\n\nconst inboundParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nconst inboundQuerySchema = z.record(z.string(), z.unknown());\n\nconst inboundAcceptedResponseSchema = z.object({\n delivery_id: z.string(),\n});\n\nfunction contentType(requestContentType: string | undefined): string | undefined {\n return requestContentType?.split(';', 1)[0]?.trim().toLowerCase();\n}\n\nfunction deliveryIdFor(header: string | string[] | undefined): string {\n const value = Array.isArray(header) ? header[0] : header;\n if (!value) return randomUUID();\n if (value.length > MAX_DELIVERY_ID_HEADER_LENGTH) {\n throw new ClientError('Delivery ID header is too long', 'delivery-id-too-long', {status: 400});\n }\n return value;\n}\n\nexport function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup {\n const inboundRoute = defineRoute({\n method: 'POST',\n path: '/:connectionId',\n auth: [],\n description: 'Generic webhook receiver.',\n options: {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n schema: {\n params: inboundParamsSchema,\n querystring: inboundQuerySchema,\n response: {\n 202: inboundAcceptedResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const mediaType = contentType(request.headers['content-type']);\n if (!mediaType || !acceptedContentTypes.has(mediaType)) {\n throw new ClientError('Unsupported content type', 'unsupported-media-type', {status: 415});\n }\n\n const {connectionId} = request.params;\n const connection = await options.getIntegrationConnectionById(connectionId);\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n const deliveryId = deliveryIdFor(request.headers['x-delivery-id']);\n const receivedAt = new Date().toISOString();\n await options.coreDb().transaction(async (tx) => {\n await options.publishIntegrationEventReceived({\n tx,\n event: {\n provider: WEBHOOK_PROVIDER,\n source: connection.slug,\n event: WEBHOOK_RECEIVED_EVENT,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n connectionName: connection.displayName,\n deliveryId,\n receivedAt,\n payload: {\n method: request.method,\n headers: redactHeaders(request.headers),\n query: request.query,\n body: request.body,\n },\n },\n });\n });\n\n reply.code(202);\n return {delivery_id: deliveryId};\n },\n });\n\n return {\n prefix: '/webhook',\n auth: [],\n plugins: [formBodyRoutePlugin],\n routes: [inboundRoute],\n };\n}\n"],"names":["randomUUID","formBodyPlugin","WEBHOOK_PROVIDER","WEBHOOK_RECEIVED_EVENT","ClientError","defineRoute","fp","z","redactHeaders","WEBHOOK_INBOUND_BODY_LIMIT","MAX_DELIVERY_ID_HEADER_LENGTH","acceptedContentTypes","Set","FORM_URLENCODED_CONTENT_TYPE_RE","formBodyRoutePlugin","app","register","bodyLimit","addContentTypeParser","parseAs","_request","body","done","Object","fromEntries","URLSearchParams","toString","inboundParamsSchema","object","connectionId","string","uuid","inboundQuerySchema","record","unknown","inboundAcceptedResponseSchema","delivery_id","contentType","requestContentType","split","trim","toLowerCase","deliveryIdFor","header","value","Array","isArray","length","status","createWebhookInboundRoutes","options","inboundRoute","method","path","auth","description","schema","params","querystring","response","handler","request","reply","mediaType","headers","has","connection","getIntegrationConnectionById","provider","lifecycleStatus","deliveryId","receivedAt","Date","toISOString","coreDb","transaction","tx","publishIntegrationEventReceived","event","source","slug","workspaceId","id","connectionName","displayName","payload","query","code","prefix","plugins","routes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/inbound.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n createStoredWebhookRequest,\n type StoredWebhookRequest,\n WEBHOOK_MAX_RAW_BODY_BYTES,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyPluginAsync} from 'fastify';\nimport fp from 'fastify-plugin';\nimport {z} from 'zod';\nimport {WEBHOOK_ACCEPTED_CONTENT_TYPES, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nimport {\n createGenericWebhookProcessor,\n type GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\n\nconst MAX_DELIVERY_ID_HEADER_LENGTH = 200;\nconst acceptedContentTypes = new Set<string>(WEBHOOK_ACCEPTED_CONTENT_TYPES);\nconst rawWebhookBodyPlugin: FastifyPluginAsync = fp((app) => {\n app.removeAllContentTypeParsers();\n for (const contentType of acceptedContentTypes) {\n app.addContentTypeParser(\n contentType,\n {parseAs: 'buffer', bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n (_request, body, done) => {\n done(null, body);\n },\n );\n }\n return Promise.resolve();\n});\n\nexport interface CreateWebhookInboundRoutesOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n processor?: GenericWebhookProcessor | undefined;\n}\n\nconst inboundParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nconst inboundQuerySchema = z.record(z.string(), z.unknown());\n\nconst inboundAcceptedResponseSchema = z.object({\n delivery_id: z.string(),\n});\n\nfunction contentType(requestContentType: string | undefined): string | undefined {\n return requestContentType?.split(';', 1)[0]?.trim().toLowerCase();\n}\n\nfunction deliveryIdFor(header: string | string[] | undefined): string {\n const value = Array.isArray(header) ? header[0] : header;\n if (!value) return randomUUID();\n if (value.length > MAX_DELIVERY_ID_HEADER_LENGTH) {\n throw new ClientError('Delivery ID header is too long', 'delivery-id-too-long', {status: 400});\n }\n return value;\n}\n\nfunction hasDeliveryId(header: string | string[] | undefined): boolean {\n return Boolean(Array.isArray(header) ? header[0] : header);\n}\n\nexport function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup {\n const processor = options.processor ?? createGenericWebhookProcessor(options);\n const inboundRoute = defineRoute({\n method: 'POST',\n path: '/:connectionId',\n auth: [],\n description: 'Generic webhook receiver.',\n options: {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n schema: {\n params: inboundParamsSchema,\n querystring: inboundQuerySchema,\n response: {\n 202: inboundAcceptedResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const mediaType = contentType(request.headers['content-type']);\n if (!mediaType || !acceptedContentTypes.has(mediaType)) {\n throw new ClientError('Unsupported content type', 'unsupported-media-type', {status: 415});\n }\n\n const {connectionId} = request.params;\n const connection = await options.getIntegrationConnectionById(connectionId);\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n const deliveryHeader = request.headers['x-delivery-id'];\n const deliveryId = deliveryIdFor(deliveryHeader);\n const result = await processor.process(\n createGenericStoredWebhookRequest(\n {\n body: request.body,\n connectionId,\n headers: request.headers,\n rawQueryString: request.raw.url?.split('?')[1] ?? '',\n requestId: hasDeliveryId(deliveryHeader) ? randomUUID() : deliveryId,\n },\n new Date().toISOString(),\n ),\n connection,\n );\n\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n throw new ClientError('Malformed webhook payload', 'malformed-payload', {status: 400});\n }\n\n reply.code(202);\n return {delivery_id: deliveryId};\n },\n });\n\n return {\n prefix: '/webhook',\n auth: [],\n plugins: [rawWebhookBodyPlugin],\n routes: [inboundRoute],\n };\n}\n\nfunction createGenericStoredWebhookRequest(\n input: {\n body: unknown;\n connectionId: string;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n requestId: string;\n },\n receivedAt: string,\n): StoredWebhookRequest {\n if (!(input.body instanceof Uint8Array)) {\n throw new ClientError('Expected raw webhook body', 'invalid-webhook-request', {status: 400});\n }\n if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {\n throw new ClientError('Webhook request body is too large', 'body-too-large', {status: 413});\n }\n\n try {\n return createStoredWebhookRequest({\n requestId: input.requestId,\n routeId: 'webhook.connection',\n receivedAt,\n rawQueryString: input.rawQueryString,\n headers: webhookHeaders(input.headers),\n body: input.body,\n connectionId: input.connectionId,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction webhookHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).flatMap(([name, value]) => {\n if (value === undefined) return [];\n return [[name.toLowerCase(), Array.isArray(value) ? value.join(', ') : value]];\n }),\n );\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","WEBHOOK_PROVIDER","ClientError","defineRoute","fp","z","WEBHOOK_ACCEPTED_CONTENT_TYPES","WEBHOOK_INBOUND_BODY_LIMIT","createGenericWebhookProcessor","MAX_DELIVERY_ID_HEADER_LENGTH","acceptedContentTypes","Set","rawWebhookBodyPlugin","app","removeAllContentTypeParsers","contentType","addContentTypeParser","parseAs","bodyLimit","_request","body","done","Promise","resolve","inboundParamsSchema","object","connectionId","string","uuid","inboundQuerySchema","record","unknown","inboundAcceptedResponseSchema","delivery_id","requestContentType","split","trim","toLowerCase","deliveryIdFor","header","value","Array","isArray","length","status","hasDeliveryId","Boolean","createWebhookInboundRoutes","options","processor","inboundRoute","method","path","auth","description","schema","params","querystring","response","handler","request","reply","mediaType","headers","has","connection","getIntegrationConnectionById","provider","lifecycleStatus","deliveryHeader","deliveryId","result","process","createGenericStoredWebhookRequest","rawQueryString","raw","url","requestId","Date","toISOString","outcome","reason","code","prefix","plugins","routes","input","receivedAt","Uint8Array","byteLength","routeId","webhookHeaders","error","cause","Object","fromEntries","entries","flatMap","name","undefined","join"],"mappings":"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"}
|