@shipfox/api-integration-gitea 4.0.0 → 6.0.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 +41 -0
- 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 +84 -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/webhooks.d.ts +2 -0
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js +71 -55
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +18 -26
- package/src/core/webhook-processor.test.ts +83 -0
- package/src/core/webhook-processor.ts +90 -0
- package/src/index.ts +9 -0
- package/src/presentation/routes/webhooks.ts +73 -49
- 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: 18 files with swc (383.26ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @shipfox/api-integration-gitea
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f262539: Adds a composed webhook processor and optional provider-neutral delivery source for hosted API runtimes.
|
|
8
|
+
- a869cfd: Adds shared stored-request processors for GitHub and Gitea webhook reception.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- f73da5d: Enforces bounded API context imports and routes inter-module consumers through producer contracts.
|
|
13
|
+
- Updated dependencies [0bb82a4]
|
|
14
|
+
- Updated dependencies [7ac43a4]
|
|
15
|
+
- Updated dependencies [f262539]
|
|
16
|
+
- Updated dependencies [3bb4e26]
|
|
17
|
+
- Updated dependencies [8bdc149]
|
|
18
|
+
- Updated dependencies [b00ed29]
|
|
19
|
+
- Updated dependencies [8aa7cd3]
|
|
20
|
+
- Updated dependencies [4604a06]
|
|
21
|
+
- @shipfox/api-integration-core-dto@6.0.0
|
|
22
|
+
- @shipfox/node-drizzle@0.3.2
|
|
23
|
+
- @shipfox/api-auth-context@6.0.0
|
|
24
|
+
- @shipfox/node-fastify@0.2.4
|
|
25
|
+
- @shipfox/api-integration-gitea-dto@6.0.0
|
|
26
|
+
|
|
27
|
+
## 5.0.0
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- bb037af: Resolves workspace packages from source during development while published consumers continue to use compiled output.
|
|
32
|
+
- Updated dependencies [2875241]
|
|
33
|
+
- Updated dependencies [bb037af]
|
|
34
|
+
- Updated dependencies [fb70438]
|
|
35
|
+
- @shipfox/api-integration-core-dto@5.0.0
|
|
36
|
+
- @shipfox/api-auth-context@5.0.0
|
|
37
|
+
- @shipfox/api-integration-gitea-dto@5.0.0
|
|
38
|
+
- @shipfox/config@1.2.2
|
|
39
|
+
- @shipfox/node-drizzle@0.3.1
|
|
40
|
+
- @shipfox/node-fastify@0.2.3
|
|
41
|
+
- @shipfox/node-opentelemetry@0.5.2
|
|
42
|
+
- @shipfox/node-postgres@0.4.2
|
|
43
|
+
|
|
3
44
|
## 4.0.0
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type GetIntegrationConnectionByIdFn, type PublishSourcePushFn, type RecordDeliveryOnlyFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
3
|
+
export interface CreateGiteaWebhookProcessorOptions {
|
|
4
|
+
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
5
|
+
publishSourcePush: PublishSourcePushFn;
|
|
6
|
+
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
7
|
+
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
8
|
+
}
|
|
9
|
+
export interface GiteaWebhookProcessor {
|
|
10
|
+
process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createGiteaWebhookProcessor(options: CreateGiteaWebhookProcessorOptions): GiteaWebhookProcessor;
|
|
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,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAY9D,MAAM,WAAW,kCAAkC;IACjD,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,qBAAqB,CAEvB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { verifyHexHmacSignature } from '@shipfox/node-fastify';
|
|
4
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
5
|
+
import { config } from '#config.js';
|
|
6
|
+
import { GiteaWebhookMalformedJsonError, GiteaWebhookMalformedPushPayloadError, handleGiteaWebhook } from '#core/webhook.js';
|
|
7
|
+
const DELIVERY_HEADER = 'x-gitea-delivery';
|
|
8
|
+
const EVENT_HEADER = 'x-gitea-event';
|
|
9
|
+
const SIGNATURE_HEADER = 'x-gitea-signature';
|
|
10
|
+
export function createGiteaWebhookProcessor(options) {
|
|
11
|
+
return {
|
|
12
|
+
process: (request)=>processGiteaWebhookRequest(options, request)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function processGiteaWebhookRequest(options, request) {
|
|
16
|
+
if (request.route_id !== 'gitea') {
|
|
17
|
+
throw new Error(`Gitea processor cannot process ${request.route_id} requests`);
|
|
18
|
+
}
|
|
19
|
+
const deliveryId = request.headers[DELIVERY_HEADER];
|
|
20
|
+
const event = request.headers[EVENT_HEADER];
|
|
21
|
+
const signature = request.headers[SIGNATURE_HEADER];
|
|
22
|
+
if (!deliveryId || !event || !signature) {
|
|
23
|
+
return {
|
|
24
|
+
outcome: 'discarded',
|
|
25
|
+
reason: 'missing_required_input'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const rawBody = Buffer.from(decodeWebhookBody(request.body));
|
|
29
|
+
if (!verifyHexHmacSignature({
|
|
30
|
+
rawBody,
|
|
31
|
+
signature,
|
|
32
|
+
secret: config.GITEA_WEBHOOK_SECRET
|
|
33
|
+
})) {
|
|
34
|
+
return {
|
|
35
|
+
outcome: 'discarded',
|
|
36
|
+
reason: 'invalid_signature',
|
|
37
|
+
deliveryId
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const result = await options.coreDb().transaction(async (tx)=>handleGiteaWebhook({
|
|
42
|
+
tx,
|
|
43
|
+
deliveryId,
|
|
44
|
+
event,
|
|
45
|
+
rawBody: rawBody.toString('utf8'),
|
|
46
|
+
publishSourcePush: options.publishSourcePush,
|
|
47
|
+
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
48
|
+
getIntegrationConnectionById: options.getIntegrationConnectionById
|
|
49
|
+
}));
|
|
50
|
+
return result.outcome === 'duplicate' ? {
|
|
51
|
+
outcome: 'duplicate',
|
|
52
|
+
deliveryId
|
|
53
|
+
} : {
|
|
54
|
+
outcome: 'processed',
|
|
55
|
+
deliveryId
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof GiteaWebhookMalformedJsonError) {
|
|
59
|
+
logger().warn({
|
|
60
|
+
deliveryId,
|
|
61
|
+
err: error
|
|
62
|
+
}, 'gitea webhook payload JSON parse failed');
|
|
63
|
+
return {
|
|
64
|
+
outcome: 'discarded',
|
|
65
|
+
reason: 'malformed_payload',
|
|
66
|
+
deliveryId
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (error instanceof GiteaWebhookMalformedPushPayloadError) {
|
|
70
|
+
logger().warn({
|
|
71
|
+
deliveryId,
|
|
72
|
+
issues: error.issues
|
|
73
|
+
}, 'gitea webhook push payload failed schema validation');
|
|
74
|
+
return {
|
|
75
|
+
outcome: 'discarded',
|
|
76
|
+
reason: 'unsupported_event',
|
|
77
|
+
deliveryId
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//# 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 PublishSourcePushFn,\n type RecordDeliveryOnlyFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {verifyHexHmacSignature} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {config} from '#config.js';\nimport {\n GiteaWebhookMalformedJsonError,\n GiteaWebhookMalformedPushPayloadError,\n handleGiteaWebhook,\n} from '#core/webhook.js';\n\nconst DELIVERY_HEADER = 'x-gitea-delivery';\nconst EVENT_HEADER = 'x-gitea-event';\nconst SIGNATURE_HEADER = 'x-gitea-signature';\n\nexport interface CreateGiteaWebhookProcessorOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport interface GiteaWebhookProcessor {\n process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;\n}\n\nexport function createGiteaWebhookProcessor(\n options: CreateGiteaWebhookProcessorOptions,\n): GiteaWebhookProcessor {\n return {process: (request) => processGiteaWebhookRequest(options, request)};\n}\n\nasync function processGiteaWebhookRequest(\n options: CreateGiteaWebhookProcessorOptions,\n request: StoredWebhookRequest,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'gitea') {\n throw new Error(`Gitea processor cannot process ${request.route_id} requests`);\n }\n\n const deliveryId = request.headers[DELIVERY_HEADER];\n const event = request.headers[EVENT_HEADER];\n const signature = request.headers[SIGNATURE_HEADER];\n if (!deliveryId || !event || !signature) {\n return {outcome: 'discarded', reason: 'missing_required_input'};\n }\n\n const rawBody = Buffer.from(decodeWebhookBody(request.body));\n if (!verifyHexHmacSignature({rawBody, signature, secret: config.GITEA_WEBHOOK_SECRET})) {\n return {outcome: 'discarded', reason: 'invalid_signature', deliveryId};\n }\n\n try {\n const result = await options.coreDb().transaction(async (tx) =>\n handleGiteaWebhook({\n tx,\n deliveryId,\n event,\n rawBody: rawBody.toString('utf8'),\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n }),\n );\n return result.outcome === 'duplicate'\n ? {outcome: 'duplicate', deliveryId}\n : {outcome: 'processed', deliveryId};\n } catch (error) {\n if (error instanceof GiteaWebhookMalformedJsonError) {\n logger().warn({deliveryId, err: error}, 'gitea webhook payload JSON parse failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n if (error instanceof GiteaWebhookMalformedPushPayloadError) {\n logger().warn(\n {deliveryId, issues: error.issues},\n 'gitea webhook push payload failed schema validation',\n );\n return {outcome: 'discarded', reason: 'unsupported_event', deliveryId};\n }\n throw error;\n }\n}\n"],"names":["Buffer","decodeWebhookBody","verifyHexHmacSignature","logger","config","GiteaWebhookMalformedJsonError","GiteaWebhookMalformedPushPayloadError","handleGiteaWebhook","DELIVERY_HEADER","EVENT_HEADER","SIGNATURE_HEADER","createGiteaWebhookProcessor","options","process","request","processGiteaWebhookRequest","route_id","Error","deliveryId","headers","event","signature","outcome","reason","rawBody","from","body","secret","GITEA_WEBHOOK_SECRET","result","coreDb","transaction","tx","toString","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById","error","warn","err","issues"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAc;AACnC,SACEC,iBAAiB,QAMZ,oCAAoC;AAC3C,SAAQC,sBAAsB,QAAO,wBAAwB;AAC7D,SAAQC,MAAM,QAAO,8BAA8B;AAEnD,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,8BAA8B,EAC9BC,qCAAqC,EACrCC,kBAAkB,QACb,mBAAmB;AAE1B,MAAMC,kBAAkB;AACxB,MAAMC,eAAe;AACrB,MAAMC,mBAAmB;AAazB,OAAO,SAASC,4BACdC,OAA2C;IAE3C,OAAO;QAACC,SAAS,CAACC,UAAYC,2BAA2BH,SAASE;IAAQ;AAC5E;AAEA,eAAeC,2BACbH,OAA2C,EAC3CE,OAA6B;IAE7B,IAAIA,QAAQE,QAAQ,KAAK,SAAS;QAChC,MAAM,IAAIC,MAAM,CAAC,+BAA+B,EAAEH,QAAQE,QAAQ,CAAC,SAAS,CAAC;IAC/E;IAEA,MAAME,aAAaJ,QAAQK,OAAO,CAACX,gBAAgB;IACnD,MAAMY,QAAQN,QAAQK,OAAO,CAACV,aAAa;IAC3C,MAAMY,YAAYP,QAAQK,OAAO,CAACT,iBAAiB;IACnD,IAAI,CAACQ,cAAc,CAACE,SAAS,CAACC,WAAW;QACvC,OAAO;YAACC,SAAS;YAAaC,QAAQ;QAAwB;IAChE;IAEA,MAAMC,UAAUxB,OAAOyB,IAAI,CAACxB,kBAAkBa,QAAQY,IAAI;IAC1D,IAAI,CAACxB,uBAAuB;QAACsB;QAASH;QAAWM,QAAQvB,OAAOwB,oBAAoB;IAAA,IAAI;QACtF,OAAO;YAACN,SAAS;YAAaC,QAAQ;YAAqBL;QAAU;IACvE;IAEA,IAAI;QACF,MAAMW,SAAS,MAAMjB,QAAQkB,MAAM,GAAGC,WAAW,CAAC,OAAOC,KACvDzB,mBAAmB;gBACjByB;gBACAd;gBACAE;gBACAI,SAASA,QAAQS,QAAQ,CAAC;gBAC1BC,mBAAmBtB,QAAQsB,iBAAiB;gBAC5CC,oBAAoBvB,QAAQuB,kBAAkB;gBAC9CC,8BAA8BxB,QAAQwB,4BAA4B;YACpE;QAEF,OAAOP,OAAOP,OAAO,KAAK,cACtB;YAACA,SAAS;YAAaJ;QAAU,IACjC;YAACI,SAAS;YAAaJ;QAAU;IACvC,EAAE,OAAOmB,OAAO;QACd,IAAIA,iBAAiBhC,gCAAgC;YACnDF,SAASmC,IAAI,CAAC;gBAACpB;gBAAYqB,KAAKF;YAAK,GAAG;YACxC,OAAO;gBAACf,SAAS;gBAAaC,QAAQ;gBAAqBL;YAAU;QACvE;QACA,IAAImB,iBAAiB/B,uCAAuC;YAC1DH,SAASmC,IAAI,CACX;gBAACpB;gBAAYsB,QAAQH,MAAMG,MAAM;YAAA,GACjC;YAEF,OAAO;gBAAClB,SAAS;gBAAaC,QAAQ;gBAAqBL;YAAU;QACvE;QACA,MAAMmB;IACR;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type { ConnectGiteaConnectionInput } from '#core/connect.js';
|
|
|
11
11
|
export { handleGiteaConnect } from '#core/connect.js';
|
|
12
12
|
export { GiteaIntegrationProviderError, GiteaOrgAlreadyLinkedError, GiteaOrganizationNotFoundError, } from '#core/errors.js';
|
|
13
13
|
export { GiteaSourceControlProvider } from '#core/source-control.js';
|
|
14
|
+
export type { CreateGiteaWebhookProcessorOptions, GiteaWebhookProcessor, } from '#core/webhook-processor.js';
|
|
15
|
+
export { createGiteaWebhookProcessor } from '#core/webhook-processor.js';
|
|
14
16
|
export type { GiteaConnection, UpsertGiteaConnectionParams } from '#db/connections.js';
|
|
15
17
|
export { getGiteaConnectionByConnectionId, getGiteaConnectionByOrg, upsertGiteaConnection, } from '#db/connections.js';
|
|
16
18
|
export { closeDb, db, migrationsPath };
|
|
@@ -35,5 +37,9 @@ export declare function createGiteaIntegrationProvider(options: CreateGiteaInteg
|
|
|
35
37
|
externalAccountId: string;
|
|
36
38
|
}): Promise<string | undefined>;
|
|
37
39
|
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
40
|
+
webhookProcessors: {
|
|
41
|
+
routeIds: readonly ["gitea"];
|
|
42
|
+
processor: import("#core/webhook-processor.js").GiteaWebhookProcessor;
|
|
43
|
+
}[];
|
|
38
44
|
};
|
|
39
45
|
//# 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,8BAA8B,EAC9B,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,8BAA8B,EAC9B,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AAEnE,OAAO,EAAC,OAAO,EAAE,EAAE,EAAC,MAAM,WAAW,CAAC;AACtC,OAAO,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAIjD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAC,oBAAoB,EAAC,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAC,2BAA2B,EAAC,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAC,kBAAkB,EAAC,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AACnE,YAAY,EACV,kCAAkC,EAClC,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,2BAA2B,EAAC,MAAM,4BAA4B,CAAC;AACvE,YAAY,EAAC,eAAe,EAAE,2BAA2B,EAAC,MAAM,oBAAoB,CAAC;AACrF,OAAO,EACL,gCAAgC,EAChC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,cAAc,EAAC,CAAC;AAErC,MAAM,WAAW,qCAAqC;IACpD,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,GAAG,EAAE,MAAM,CAAC;KACb,KAAK,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CACtB,KAAK,EAAE,2BAA2B,KAC/B,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qCAAqC;;;;;;sCAUvD;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;;;;;;EAmB9F"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { giteaProviderKind } from '@shipfox/api-integration-gitea-dto';
|
|
|
2
2
|
import { createGiteaApiClient } from '#api/client.js';
|
|
3
3
|
import { giteaConnectionExternalUrl } from '#core/connection-url.js';
|
|
4
4
|
import { GiteaSourceControlProvider } from '#core/source-control.js';
|
|
5
|
+
import { createGiteaWebhookProcessor } from '#core/webhook-processor.js';
|
|
5
6
|
import { closeDb, db } from '#db/db.js';
|
|
6
7
|
import { migrationsPath } from '#db/migrations.js';
|
|
7
8
|
import { createGiteaConnectionRoutes } from '#presentation/routes/connections.js';
|
|
@@ -10,10 +11,12 @@ export { createGiteaApiClient } from '#api/client.js';
|
|
|
10
11
|
export { handleGiteaConnect } from '#core/connect.js';
|
|
11
12
|
export { GiteaIntegrationProviderError, GiteaOrgAlreadyLinkedError, GiteaOrganizationNotFoundError } from '#core/errors.js';
|
|
12
13
|
export { GiteaSourceControlProvider } from '#core/source-control.js';
|
|
14
|
+
export { createGiteaWebhookProcessor } from '#core/webhook-processor.js';
|
|
13
15
|
export { getGiteaConnectionByConnectionId, getGiteaConnectionByOrg, upsertGiteaConnection } from '#db/connections.js';
|
|
14
16
|
export { closeDb, db, migrationsPath };
|
|
15
17
|
export function createGiteaIntegrationProvider(options) {
|
|
16
18
|
const gitea = options.gitea ?? createGiteaApiClient();
|
|
19
|
+
const webhookProcessor = createGiteaWebhookProcessor(options);
|
|
17
20
|
return {
|
|
18
21
|
provider: giteaProviderKind,
|
|
19
22
|
displayName: 'Gitea',
|
|
@@ -33,8 +36,17 @@ export function createGiteaIntegrationProvider(options) {
|
|
|
33
36
|
coreDb: options.coreDb,
|
|
34
37
|
publishSourcePush: options.publishSourcePush,
|
|
35
38
|
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
36
|
-
getIntegrationConnectionById: options.getIntegrationConnectionById
|
|
39
|
+
getIntegrationConnectionById: options.getIntegrationConnectionById,
|
|
40
|
+
processor: webhookProcessor
|
|
37
41
|
})
|
|
42
|
+
],
|
|
43
|
+
webhookProcessors: [
|
|
44
|
+
{
|
|
45
|
+
routeIds: [
|
|
46
|
+
'gitea'
|
|
47
|
+
],
|
|
48
|
+
processor: webhookProcessor
|
|
49
|
+
}
|
|
38
50
|
]
|
|
39
51
|
};
|
|
40
52
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport {giteaProviderKind} from '@shipfox/api-integration-gitea-dto';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createGiteaApiClient, type GiteaApiClient} from '#api/client.js';\nimport type {ConnectGiteaConnectionInput} from '#core/connect.js';\nimport {giteaConnectionExternalUrl} from '#core/connection-url.js';\nimport {GiteaSourceControlProvider} from '#core/source-control.js';\nimport {closeDb, db} from '#db/db.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {createGiteaConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createGiteaWebhookRoutes} from '#presentation/routes/webhooks.js';\n\nexport type {\n GiteaApiClient,\n GiteaFileContent,\n GiteaRepository,\n GiteaRepositoryPage,\n GiteaTree,\n GiteaTreeBlob,\n} from '#api/client.js';\nexport {createGiteaApiClient} from '#api/client.js';\nexport type {ConnectGiteaConnectionInput} from '#core/connect.js';\nexport {handleGiteaConnect} from '#core/connect.js';\nexport {\n GiteaIntegrationProviderError,\n GiteaOrgAlreadyLinkedError,\n GiteaOrganizationNotFoundError,\n} from '#core/errors.js';\nexport {GiteaSourceControlProvider} from '#core/source-control.js';\nexport type {GiteaConnection, UpsertGiteaConnectionParams} from '#db/connections.js';\nexport {\n getGiteaConnectionByConnectionId,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n} from '#db/connections.js';\nexport {closeDb, db, migrationsPath};\n\nexport interface CreateGiteaIntegrationProviderOptions {\n gitea?: GiteaApiClient | undefined;\n getExistingGiteaConnection: (input: {\n org: string;\n }) => Promise<IntegrationConnection<'gitea'> | undefined>;\n connectGiteaConnection: (\n input: ConnectGiteaConnectionInput,\n ) => Promise<IntegrationConnection<'gitea'>>;\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport function createGiteaIntegrationProvider(options: CreateGiteaIntegrationProviderOptions) {\n const gitea = options.gitea ?? createGiteaApiClient();\n\n return {\n provider: giteaProviderKind,\n displayName: 'Gitea',\n adapters: {\n source_control: new GiteaSourceControlProvider(gitea),\n },\n connectionExternalUrl(connection: {externalAccountId: string}): Promise<string | undefined> {\n return Promise.resolve(giteaConnectionExternalUrl(connection.externalAccountId));\n },\n routes: [\n createGiteaConnectionRoutes({\n gitea,\n getExistingGiteaConnection: options.getExistingGiteaConnection,\n connectGiteaConnection: options.connectGiteaConnection,\n }),\n createGiteaWebhookRoutes({\n coreDb: options.coreDb,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n }),\n ],\n };\n}\n"],"names":["giteaProviderKind","createGiteaApiClient","giteaConnectionExternalUrl","GiteaSourceControlProvider","closeDb","db","migrationsPath","createGiteaConnectionRoutes","createGiteaWebhookRoutes","handleGiteaConnect","GiteaIntegrationProviderError","GiteaOrgAlreadyLinkedError","GiteaOrganizationNotFoundError","getGiteaConnectionByConnectionId","getGiteaConnectionByOrg","upsertGiteaConnection","createGiteaIntegrationProvider","options","gitea","provider","displayName","adapters","source_control","connectionExternalUrl","connection","Promise","resolve","externalAccountId","routes","getExistingGiteaConnection","connectGiteaConnection","coreDb","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById"],"mappings":"AAMA,SAAQA,iBAAiB,QAAO,qCAAqC;AAErE,SAAQC,oBAAoB,QAA4B,iBAAiB;AAEzE,SAAQC,0BAA0B,QAAO,0BAA0B;AACnE,SAAQC,0BAA0B,QAAO,0BAA0B;AACnE,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAAQC,2BAA2B,QAAO,sCAAsC;AAChF,SAAQC,wBAAwB,QAAO,mCAAmC;AAU1E,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport {giteaProviderKind} from '@shipfox/api-integration-gitea-dto';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createGiteaApiClient, type GiteaApiClient} from '#api/client.js';\nimport type {ConnectGiteaConnectionInput} from '#core/connect.js';\nimport {giteaConnectionExternalUrl} from '#core/connection-url.js';\nimport {GiteaSourceControlProvider} from '#core/source-control.js';\nimport {createGiteaWebhookProcessor} from '#core/webhook-processor.js';\nimport {closeDb, db} from '#db/db.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {createGiteaConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createGiteaWebhookRoutes} from '#presentation/routes/webhooks.js';\n\nexport type {\n GiteaApiClient,\n GiteaFileContent,\n GiteaRepository,\n GiteaRepositoryPage,\n GiteaTree,\n GiteaTreeBlob,\n} from '#api/client.js';\nexport {createGiteaApiClient} from '#api/client.js';\nexport type {ConnectGiteaConnectionInput} from '#core/connect.js';\nexport {handleGiteaConnect} from '#core/connect.js';\nexport {\n GiteaIntegrationProviderError,\n GiteaOrgAlreadyLinkedError,\n GiteaOrganizationNotFoundError,\n} from '#core/errors.js';\nexport {GiteaSourceControlProvider} from '#core/source-control.js';\nexport type {\n CreateGiteaWebhookProcessorOptions,\n GiteaWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createGiteaWebhookProcessor} from '#core/webhook-processor.js';\nexport type {GiteaConnection, UpsertGiteaConnectionParams} from '#db/connections.js';\nexport {\n getGiteaConnectionByConnectionId,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n} from '#db/connections.js';\nexport {closeDb, db, migrationsPath};\n\nexport interface CreateGiteaIntegrationProviderOptions {\n gitea?: GiteaApiClient | undefined;\n getExistingGiteaConnection: (input: {\n org: string;\n }) => Promise<IntegrationConnection<'gitea'> | undefined>;\n connectGiteaConnection: (\n input: ConnectGiteaConnectionInput,\n ) => Promise<IntegrationConnection<'gitea'>>;\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport function createGiteaIntegrationProvider(options: CreateGiteaIntegrationProviderOptions) {\n const gitea = options.gitea ?? createGiteaApiClient();\n const webhookProcessor = createGiteaWebhookProcessor(options);\n\n return {\n provider: giteaProviderKind,\n displayName: 'Gitea',\n adapters: {\n source_control: new GiteaSourceControlProvider(gitea),\n },\n connectionExternalUrl(connection: {externalAccountId: string}): Promise<string | undefined> {\n return Promise.resolve(giteaConnectionExternalUrl(connection.externalAccountId));\n },\n routes: [\n createGiteaConnectionRoutes({\n gitea,\n getExistingGiteaConnection: options.getExistingGiteaConnection,\n connectGiteaConnection: options.connectGiteaConnection,\n }),\n createGiteaWebhookRoutes({\n coreDb: options.coreDb,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n processor: webhookProcessor,\n }),\n ],\n webhookProcessors: [{routeIds: ['gitea'] as const, processor: webhookProcessor}],\n };\n}\n"],"names":["giteaProviderKind","createGiteaApiClient","giteaConnectionExternalUrl","GiteaSourceControlProvider","createGiteaWebhookProcessor","closeDb","db","migrationsPath","createGiteaConnectionRoutes","createGiteaWebhookRoutes","handleGiteaConnect","GiteaIntegrationProviderError","GiteaOrgAlreadyLinkedError","GiteaOrganizationNotFoundError","getGiteaConnectionByConnectionId","getGiteaConnectionByOrg","upsertGiteaConnection","createGiteaIntegrationProvider","options","gitea","webhookProcessor","provider","displayName","adapters","source_control","connectionExternalUrl","connection","Promise","resolve","externalAccountId","routes","getExistingGiteaConnection","connectGiteaConnection","coreDb","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById","processor","webhookProcessors","routeIds"],"mappings":"AAMA,SAAQA,iBAAiB,QAAO,qCAAqC;AAErE,SAAQC,oBAAoB,QAA4B,iBAAiB;AAEzE,SAAQC,0BAA0B,QAAO,0BAA0B;AACnE,SAAQC,0BAA0B,QAAO,0BAA0B;AACnE,SAAQC,2BAA2B,QAAO,6BAA6B;AACvE,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAAQC,2BAA2B,QAAO,sCAAsC;AAChF,SAAQC,wBAAwB,QAAO,mCAAmC;AAU1E,SAAQR,oBAAoB,QAAO,iBAAiB;AAEpD,SAAQS,kBAAkB,QAAO,mBAAmB;AACpD,SACEC,6BAA6B,EAC7BC,0BAA0B,EAC1BC,8BAA8B,QACzB,kBAAkB;AACzB,SAAQV,0BAA0B,QAAO,0BAA0B;AAKnE,SAAQC,2BAA2B,QAAO,6BAA6B;AAEvE,SACEU,gCAAgC,EAChCC,uBAAuB,EACvBC,qBAAqB,QAChB,qBAAqB;AAC5B,SAAQX,OAAO,EAAEC,EAAE,EAAEC,cAAc,GAAE;AAgBrC,OAAO,SAASU,+BAA+BC,OAA8C;IAC3F,MAAMC,QAAQD,QAAQC,KAAK,IAAIlB;IAC/B,MAAMmB,mBAAmBhB,4BAA4Bc;IAErD,OAAO;QACLG,UAAUrB;QACVsB,aAAa;QACbC,UAAU;YACRC,gBAAgB,IAAIrB,2BAA2BgB;QACjD;QACAM,uBAAsBC,UAAuC;YAC3D,OAAOC,QAAQC,OAAO,CAAC1B,2BAA2BwB,WAAWG,iBAAiB;QAChF;QACAC,QAAQ;YACNtB,4BAA4B;gBAC1BW;gBACAY,4BAA4Bb,QAAQa,0BAA0B;gBAC9DC,wBAAwBd,QAAQc,sBAAsB;YACxD;YACAvB,yBAAyB;gBACvBwB,QAAQf,QAAQe,MAAM;gBACtBC,mBAAmBhB,QAAQgB,iBAAiB;gBAC5CC,oBAAoBjB,QAAQiB,kBAAkB;gBAC9CC,8BAA8BlB,QAAQkB,4BAA4B;gBAClEC,WAAWjB;YACb;SACD;QACDkB,mBAAmB;YAAC;gBAACC,UAAU;oBAAC;iBAAQ;gBAAWF,WAAWjB;YAAgB;SAAE;IAClF;AACF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { GetIntegrationConnectionByIdFn, PublishSourcePushFn, RecordDeliveryOnlyFn } from '@shipfox/api-integration-core-dto';
|
|
2
2
|
import { type RouteGroup } from '@shipfox/node-fastify';
|
|
3
3
|
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import { type GiteaWebhookProcessor } from '#core/webhook-processor.js';
|
|
4
5
|
export interface CreateGiteaWebhookRoutesOptions {
|
|
5
6
|
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
6
7
|
publishSourcePush: PublishSourcePushFn;
|
|
7
8
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
8
9
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
10
|
+
processor?: GiteaWebhookProcessor | undefined;
|
|
9
11
|
}
|
|
10
12
|
export declare function createGiteaWebhookRoutes(options: CreateGiteaWebhookRoutesOptions): RouteGroup;
|
|
11
13
|
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,mCAAmC,CAAC;AAK3C,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAA8B,KAAK,qBAAqB,EAAC,MAAM,4BAA4B,CAAC;AAMnG,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,SAAS,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,UAAU,CA+C7F"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { GiteaWebhookMalformedJsonError, GiteaWebhookMalformedPushPayloadError, handleGiteaWebhook } from '#core/webhook.js';
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { ClientError, defineRoute, rawBodyPlugin, WEBHOOK_BODY_LIMIT } from '@shipfox/node-fastify';
|
|
4
|
+
import { createGiteaWebhookProcessor } from '#core/webhook-processor.js';
|
|
6
5
|
const SIGNATURE_HEADER = 'x-gitea-signature';
|
|
7
6
|
const EVENT_HEADER = 'x-gitea-event';
|
|
8
7
|
const DELIVERY_HEADER = 'x-gitea-delivery';
|
|
9
8
|
export function createGiteaWebhookRoutes(options) {
|
|
9
|
+
const processor = options.processor ?? createGiteaWebhookProcessor(options);
|
|
10
10
|
const pushRoute = defineRoute({
|
|
11
11
|
method: 'POST',
|
|
12
12
|
path: '/',
|
|
@@ -37,61 +37,18 @@ export function createGiteaWebhookRoutes(options) {
|
|
|
37
37
|
error: 'missing X-Gitea-Event header'
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
if (!Buffer.isBuffer(body)) {
|
|
40
|
+
if (!(request.body instanceof Uint8Array)) {
|
|
42
41
|
reply.code(400);
|
|
43
42
|
return {
|
|
44
43
|
error: 'expected raw JSON body'
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
reply.code(401);
|
|
54
|
-
return {
|
|
55
|
-
error: 'invalid signature'
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
await options.coreDb().transaction(async (tx)=>{
|
|
60
|
-
await handleGiteaWebhook({
|
|
61
|
-
tx,
|
|
62
|
-
deliveryId,
|
|
63
|
-
event,
|
|
64
|
-
rawBody,
|
|
65
|
-
publishSourcePush: options.publishSourcePush,
|
|
66
|
-
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
67
|
-
getIntegrationConnectionById: options.getIntegrationConnectionById
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
} catch (error) {
|
|
71
|
-
if (error instanceof GiteaWebhookMalformedJsonError) {
|
|
72
|
-
logger().warn({
|
|
73
|
-
deliveryId,
|
|
74
|
-
err: error
|
|
75
|
-
}, 'gitea webhook payload JSON parse failed');
|
|
76
|
-
reply.code(400);
|
|
77
|
-
return {
|
|
78
|
-
error: 'malformed JSON'
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
if (error instanceof GiteaWebhookMalformedPushPayloadError) {
|
|
82
|
-
logger().warn({
|
|
83
|
-
deliveryId,
|
|
84
|
-
issues: error.issues
|
|
85
|
-
}, 'gitea webhook push payload failed schema validation');
|
|
86
|
-
reply.code(400);
|
|
87
|
-
return {
|
|
88
|
-
error: 'malformed push payload'
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
reply.code(204);
|
|
94
|
-
return null;
|
|
46
|
+
const result = await processor.process(createGiteaStoredWebhookRequest({
|
|
47
|
+
body: request.body,
|
|
48
|
+
headers: request.headers,
|
|
49
|
+
rawQueryString: request.raw.url?.split('?')[1] ?? ''
|
|
50
|
+
}));
|
|
51
|
+
return sendGiteaWebhookResponse(reply, result);
|
|
95
52
|
}
|
|
96
53
|
});
|
|
97
54
|
return {
|
|
@@ -105,5 +62,64 @@ export function createGiteaWebhookRoutes(options) {
|
|
|
105
62
|
]
|
|
106
63
|
};
|
|
107
64
|
}
|
|
65
|
+
function createGiteaStoredWebhookRequest(input) {
|
|
66
|
+
if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {
|
|
67
|
+
throw new ClientError('Webhook request body is too large', 'body-too-large', {
|
|
68
|
+
status: 413
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return createStoredWebhookRequest({
|
|
73
|
+
requestId: randomUUID(),
|
|
74
|
+
routeId: 'gitea',
|
|
75
|
+
receivedAt: new Date().toISOString(),
|
|
76
|
+
rawQueryString: input.rawQueryString,
|
|
77
|
+
headers: giteaWebhookHeaders(input.headers),
|
|
78
|
+
body: input.body
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {
|
|
82
|
+
cause: error
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function giteaWebhookHeaders(headers) {
|
|
87
|
+
return Object.fromEntries([
|
|
88
|
+
'content-type',
|
|
89
|
+
DELIVERY_HEADER,
|
|
90
|
+
EVENT_HEADER,
|
|
91
|
+
SIGNATURE_HEADER
|
|
92
|
+
].flatMap((name)=>{
|
|
93
|
+
const value = headers[name];
|
|
94
|
+
return typeof value === 'string' ? [
|
|
95
|
+
[
|
|
96
|
+
name,
|
|
97
|
+
value
|
|
98
|
+
]
|
|
99
|
+
] : [];
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
function sendGiteaWebhookResponse(reply, result) {
|
|
103
|
+
if (result.outcome === 'discarded' && result.reason === 'invalid_signature') {
|
|
104
|
+
reply.code(401);
|
|
105
|
+
return {
|
|
106
|
+
error: 'invalid signature'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {
|
|
110
|
+
reply.code(400);
|
|
111
|
+
return {
|
|
112
|
+
error: 'malformed JSON'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (result.outcome === 'discarded' && result.reason === 'unsupported_event') {
|
|
116
|
+
reply.code(400);
|
|
117
|
+
return {
|
|
118
|
+
error: 'malformed push payload'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
reply.code(204);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
108
124
|
|
|
109
125
|
//# sourceMappingURL=webhooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/webhooks.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/webhooks.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n GetIntegrationConnectionByIdFn,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n StoredWebhookRequest,\n WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {\n createStoredWebhookRequest,\n WEBHOOK_MAX_RAW_BODY_BYTES,\n} from '@shipfox/api-integration-core-dto';\nimport {\n ClientError,\n defineRoute,\n type RouteGroup,\n rawBodyPlugin,\n WEBHOOK_BODY_LIMIT,\n} from '@shipfox/node-fastify';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createGiteaWebhookProcessor, type GiteaWebhookProcessor} from '#core/webhook-processor.js';\n\nconst SIGNATURE_HEADER = 'x-gitea-signature';\nconst EVENT_HEADER = 'x-gitea-event';\nconst DELIVERY_HEADER = 'x-gitea-delivery';\n\nexport interface CreateGiteaWebhookRoutesOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n processor?: GiteaWebhookProcessor | undefined;\n}\n\nexport function createGiteaWebhookRoutes(options: CreateGiteaWebhookRoutesOptions): RouteGroup {\n const processor = options.processor ?? createGiteaWebhookProcessor(options);\n const pushRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'Gitea org webhook receiver.',\n options: {bodyLimit: WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const deliveryId = request.headers[DELIVERY_HEADER];\n const signature = request.headers[SIGNATURE_HEADER];\n const event = request.headers[EVENT_HEADER];\n\n if (typeof deliveryId !== 'string' || !deliveryId) {\n reply.code(400);\n return {error: 'missing X-Gitea-Delivery header'};\n }\n if (typeof signature !== 'string' || !signature) {\n reply.code(401);\n return {error: 'missing X-Gitea-Signature header'};\n }\n if (typeof event !== 'string' || !event) {\n reply.code(400);\n return {error: 'missing X-Gitea-Event header'};\n }\n\n if (!(request.body instanceof Uint8Array)) {\n reply.code(400);\n return {error: 'expected raw JSON body'};\n }\n const result = await processor.process(\n createGiteaStoredWebhookRequest({\n body: request.body,\n headers: request.headers,\n rawQueryString: request.raw.url?.split('?')[1] ?? '',\n }),\n );\n return sendGiteaWebhookResponse(reply, result);\n },\n });\n\n return {\n prefix: '/webhooks/integrations/gitea',\n auth: [],\n plugins: [rawBodyPlugin],\n routes: [pushRoute],\n };\n}\n\nfunction createGiteaStoredWebhookRequest(input: {\n body: Uint8Array;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n}): StoredWebhookRequest {\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 try {\n return createStoredWebhookRequest({\n requestId: randomUUID(),\n routeId: 'gitea',\n receivedAt: new Date().toISOString(),\n rawQueryString: input.rawQueryString,\n headers: giteaWebhookHeaders(input.headers),\n body: input.body,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction giteaWebhookHeaders(headers: Record<string, string | string[] | undefined>) {\n return Object.fromEntries(\n ['content-type', DELIVERY_HEADER, EVENT_HEADER, SIGNATURE_HEADER].flatMap((name) => {\n const value = headers[name];\n return typeof value === 'string' ? [[name, value]] : [];\n }),\n );\n}\n\nfunction sendGiteaWebhookResponse(\n reply: {code(statusCode: number): void},\n result: WebhookProcessingResult,\n) {\n if (result.outcome === 'discarded' && result.reason === 'invalid_signature') {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n reply.code(400);\n return {error: 'malformed JSON'};\n }\n if (result.outcome === 'discarded' && result.reason === 'unsupported_event') {\n reply.code(400);\n return {error: 'malformed push payload'};\n }\n reply.code(204);\n return null;\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","ClientError","defineRoute","rawBodyPlugin","WEBHOOK_BODY_LIMIT","createGiteaWebhookProcessor","SIGNATURE_HEADER","EVENT_HEADER","DELIVERY_HEADER","createGiteaWebhookRoutes","options","processor","pushRoute","method","path","auth","description","bodyLimit","handler","request","reply","deliveryId","headers","signature","event","code","error","body","Uint8Array","result","process","createGiteaStoredWebhookRequest","rawQueryString","raw","url","split","sendGiteaWebhookResponse","prefix","plugins","routes","input","byteLength","status","requestId","routeId","receivedAt","Date","toISOString","giteaWebhookHeaders","cause","Object","fromEntries","flatMap","name","value","outcome","reason"],"mappings":"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"}
|