@shipfox/api-integration-github 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 +52 -0
- package/dist/core/webhook-processor.d.ts +18 -0
- package/dist/core/webhook-processor.d.ts.map +1 -0
- package/dist/core/webhook-processor.js +83 -0
- package/dist/core/webhook-processor.js.map +1 -0
- package/dist/core/webhook.d.ts +7 -6
- package/dist/core/webhook.d.ts.map +1 -1
- package/dist/core/webhook.js +12 -28
- package/dist/core/webhook.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -6
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/install.d.ts +6 -1
- package/dist/presentation/routes/install.d.ts.map +1 -1
- package/dist/presentation/routes/install.js +5 -3
- package/dist/presentation/routes/install.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 +73 -56
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +19 -27
- package/src/core/webhook-processor.test.ts +166 -0
- package/src/core/webhook-processor.ts +97 -0
- package/src/core/webhook.test.ts +16 -16
- package/src/core/webhook.ts +29 -44
- package/src/index.test.ts +50 -0
- package/src/index.ts +24 -8
- package/src/presentation/routes/install.test.ts +2 -6
- package/src/presentation/routes/install.ts +16 -2
- package/src/presentation/routes/webhooks.ts +81 -47
- 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: 29 files with swc (450.82ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# @shipfox/api-integration-github
|
|
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
|
+
- 3bb4e26: Fixes composed webhook processing and exposes Slack URL-verification responses through the shared contract.
|
|
13
|
+
- f73da5d: Enforces bounded API context imports and routes inter-module consumers through producer contracts.
|
|
14
|
+
- 0649d62: Keeps GitHub credential cleanup and Sentry installation lifecycle handling safe across duplicate, concurrent, and reordered webhook delivery.
|
|
15
|
+
- 326f4c0: Exposes Workspaces inter-module operations and moves Auth and OAuth providers onto injected clients.
|
|
16
|
+
- Updated dependencies [0bb82a4]
|
|
17
|
+
- Updated dependencies [7366f04]
|
|
18
|
+
- Updated dependencies [7ac43a4]
|
|
19
|
+
- Updated dependencies [f262539]
|
|
20
|
+
- Updated dependencies [3bb4e26]
|
|
21
|
+
- Updated dependencies [c2db8c3]
|
|
22
|
+
- Updated dependencies [8bdc149]
|
|
23
|
+
- Updated dependencies [f73da5d]
|
|
24
|
+
- Updated dependencies [6bdf24b]
|
|
25
|
+
- Updated dependencies [b00ed29]
|
|
26
|
+
- Updated dependencies [8aa7cd3]
|
|
27
|
+
- Updated dependencies [326f4c0]
|
|
28
|
+
- Updated dependencies [1820feb]
|
|
29
|
+
- Updated dependencies [4604a06]
|
|
30
|
+
- @shipfox/api-integration-core-dto@6.0.0
|
|
31
|
+
- @shipfox/api-workspaces@6.0.0
|
|
32
|
+
- @shipfox/node-drizzle@0.3.2
|
|
33
|
+
- @shipfox/api-auth-context@6.0.0
|
|
34
|
+
- @shipfox/node-fastify@0.2.4
|
|
35
|
+
- @shipfox/api-integration-github-dto@6.0.0
|
|
36
|
+
|
|
37
|
+
## 5.0.0
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- bb037af: Resolves workspace packages from source during development while published consumers continue to use compiled output.
|
|
42
|
+
- Updated dependencies [2875241]
|
|
43
|
+
- Updated dependencies [bb037af]
|
|
44
|
+
- Updated dependencies [fb70438]
|
|
45
|
+
- @shipfox/api-integration-core-dto@5.0.0
|
|
46
|
+
- @shipfox/api-auth-context@5.0.0
|
|
47
|
+
- @shipfox/api-integration-github-dto@5.0.0
|
|
48
|
+
- @shipfox/api-workspaces@5.0.0
|
|
49
|
+
- @shipfox/config@1.2.2
|
|
50
|
+
- @shipfox/node-drizzle@0.3.1
|
|
51
|
+
- @shipfox/node-fastify@0.2.3
|
|
52
|
+
- @shipfox/node-opentelemetry@0.5.2
|
|
53
|
+
- @shipfox/node-postgres@0.4.2
|
|
54
|
+
|
|
3
55
|
## 4.0.0
|
|
4
56
|
|
|
5
57
|
### Patch Changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type GetIntegrationConnectionByIdFn, type PublishIntegrationEventReceivedFn, 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 CreateGithubWebhookProcessorOptions {
|
|
4
|
+
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
5
|
+
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
6
|
+
publishSourcePush: PublishSourcePushFn;
|
|
7
|
+
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
8
|
+
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
9
|
+
deleteInstallationTokenSecret?: ((params: {
|
|
10
|
+
workspaceId: string;
|
|
11
|
+
installationId: number;
|
|
12
|
+
}) => Promise<unknown>) | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface GithubWebhookProcessor {
|
|
15
|
+
process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createGithubWebhookProcessor(options: CreateGithubWebhookProcessorOptions): GithubWebhookProcessor;
|
|
18
|
+
//# 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":"AAEA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,iCAAiC,EACtC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAQ9D,MAAM,WAAW,mCAAmC;IAClD,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,6BAA6B,CAAC,EAC1B,CAAC,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAC7E,SAAS,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,sBAAsB,CAGxB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { Webhooks } from '@octokit/webhooks';
|
|
3
|
+
import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
|
|
4
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
5
|
+
import { config } from '#config.js';
|
|
6
|
+
import { handleGithubEvent } from '#core/webhook.js';
|
|
7
|
+
const DELIVERY_HEADER = 'x-github-delivery';
|
|
8
|
+
const EVENT_HEADER = 'x-github-event';
|
|
9
|
+
const SIGNATURE_HEADER = 'x-hub-signature-256';
|
|
10
|
+
export function createGithubWebhookProcessor(options) {
|
|
11
|
+
const webhooks = new Webhooks({
|
|
12
|
+
secret: config.GITHUB_APP_WEBHOOK_SECRET
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
process: (request)=>processGithubWebhookRequest(options, webhooks, request)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function processGithubWebhookRequest(options, webhooks, request) {
|
|
19
|
+
if (request.route_id !== 'github') {
|
|
20
|
+
throw new Error(`GitHub processor cannot process ${request.route_id} requests`);
|
|
21
|
+
}
|
|
22
|
+
const deliveryId = request.headers[DELIVERY_HEADER];
|
|
23
|
+
const event = request.headers[EVENT_HEADER];
|
|
24
|
+
const signature = request.headers[SIGNATURE_HEADER];
|
|
25
|
+
if (!deliveryId || !event || !signature) {
|
|
26
|
+
return {
|
|
27
|
+
outcome: 'discarded',
|
|
28
|
+
reason: 'missing_required_input'
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const rawBody = Buffer.from(decodeWebhookBody(request.body));
|
|
32
|
+
let verified;
|
|
33
|
+
try {
|
|
34
|
+
verified = await webhooks.verify(rawBody.toString('utf8'), signature);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger().warn({
|
|
37
|
+
deliveryId,
|
|
38
|
+
err: error
|
|
39
|
+
}, 'github webhook signature verification threw');
|
|
40
|
+
verified = false;
|
|
41
|
+
}
|
|
42
|
+
if (!verified) return {
|
|
43
|
+
outcome: 'discarded',
|
|
44
|
+
reason: 'invalid_signature',
|
|
45
|
+
deliveryId
|
|
46
|
+
};
|
|
47
|
+
let payload;
|
|
48
|
+
try {
|
|
49
|
+
payload = JSON.parse(rawBody.toString('utf8'));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
logger().warn({
|
|
52
|
+
deliveryId,
|
|
53
|
+
err: error
|
|
54
|
+
}, 'github webhook payload JSON parse failed');
|
|
55
|
+
return {
|
|
56
|
+
outcome: 'discarded',
|
|
57
|
+
reason: 'malformed_payload',
|
|
58
|
+
deliveryId
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const result = await options.coreDb().transaction(async (tx)=>handleGithubEvent({
|
|
62
|
+
tx,
|
|
63
|
+
deliveryId,
|
|
64
|
+
event,
|
|
65
|
+
payload,
|
|
66
|
+
publishIntegrationEventReceived: options.publishIntegrationEventReceived,
|
|
67
|
+
publishSourcePush: options.publishSourcePush,
|
|
68
|
+
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
69
|
+
getIntegrationConnectionById: options.getIntegrationConnectionById
|
|
70
|
+
}));
|
|
71
|
+
if (result.installationTokenCleanup && options.deleteInstallationTokenSecret) {
|
|
72
|
+
await options.deleteInstallationTokenSecret(result.installationTokenCleanup);
|
|
73
|
+
}
|
|
74
|
+
return result.outcome.startsWith('duplicate') ? {
|
|
75
|
+
outcome: 'duplicate',
|
|
76
|
+
deliveryId
|
|
77
|
+
} : {
|
|
78
|
+
outcome: 'processed',
|
|
79
|
+
deliveryId
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=webhook-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook-processor.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {Webhooks} from '@octokit/webhooks';\nimport {\n decodeWebhookBody,\n type GetIntegrationConnectionByIdFn,\n type PublishIntegrationEventReceivedFn,\n type PublishSourcePushFn,\n type RecordDeliveryOnlyFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {config} from '#config.js';\nimport {handleGithubEvent} from '#core/webhook.js';\n\nconst DELIVERY_HEADER = 'x-github-delivery';\nconst EVENT_HEADER = 'x-github-event';\nconst SIGNATURE_HEADER = 'x-hub-signature-256';\n\nexport interface CreateGithubWebhookProcessorOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n deleteInstallationTokenSecret?:\n | ((params: {workspaceId: string; installationId: number}) => Promise<unknown>)\n | undefined;\n}\n\nexport interface GithubWebhookProcessor {\n process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;\n}\n\nexport function createGithubWebhookProcessor(\n options: CreateGithubWebhookProcessorOptions,\n): GithubWebhookProcessor {\n const webhooks = new Webhooks({secret: config.GITHUB_APP_WEBHOOK_SECRET});\n return {process: (request) => processGithubWebhookRequest(options, webhooks, request)};\n}\n\nasync function processGithubWebhookRequest(\n options: CreateGithubWebhookProcessorOptions,\n webhooks: Webhooks,\n request: StoredWebhookRequest,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'github') {\n throw new Error(`GitHub 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 let verified: boolean;\n try {\n verified = await webhooks.verify(rawBody.toString('utf8'), signature);\n } catch (error) {\n logger().warn({deliveryId, err: error}, 'github webhook signature verification threw');\n verified = false;\n }\n if (!verified) return {outcome: 'discarded', reason: 'invalid_signature', deliveryId};\n\n let payload: unknown;\n try {\n payload = JSON.parse(rawBody.toString('utf8'));\n } catch (error) {\n logger().warn({deliveryId, err: error}, 'github webhook payload JSON parse failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n\n const result = await options.coreDb().transaction(async (tx) =>\n handleGithubEvent({\n tx,\n deliveryId,\n event,\n payload,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n }),\n );\n\n if (result.installationTokenCleanup && options.deleteInstallationTokenSecret) {\n await options.deleteInstallationTokenSecret(result.installationTokenCleanup);\n }\n\n return result.outcome.startsWith('duplicate')\n ? {outcome: 'duplicate', deliveryId}\n : {outcome: 'processed', deliveryId};\n}\n"],"names":["Buffer","Webhooks","decodeWebhookBody","logger","config","handleGithubEvent","DELIVERY_HEADER","EVENT_HEADER","SIGNATURE_HEADER","createGithubWebhookProcessor","options","webhooks","secret","GITHUB_APP_WEBHOOK_SECRET","process","request","processGithubWebhookRequest","route_id","Error","deliveryId","headers","event","signature","outcome","reason","rawBody","from","body","verified","verify","toString","error","warn","err","payload","JSON","parse","result","coreDb","transaction","tx","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById","installationTokenCleanup","deleteInstallationTokenSecret","startsWith"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAc;AACnC,SAAQC,QAAQ,QAAO,oBAAoB;AAC3C,SACEC,iBAAiB,QAOZ,oCAAoC;AAC3C,SAAQC,MAAM,QAAO,8BAA8B;AAEnD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,QAAO,mBAAmB;AAEnD,MAAMC,kBAAkB;AACxB,MAAMC,eAAe;AACrB,MAAMC,mBAAmB;AAiBzB,OAAO,SAASC,6BACdC,OAA4C;IAE5C,MAAMC,WAAW,IAAIV,SAAS;QAACW,QAAQR,OAAOS,yBAAyB;IAAA;IACvE,OAAO;QAACC,SAAS,CAACC,UAAYC,4BAA4BN,SAASC,UAAUI;IAAQ;AACvF;AAEA,eAAeC,4BACbN,OAA4C,EAC5CC,QAAkB,EAClBI,OAA6B;IAE7B,IAAIA,QAAQE,QAAQ,KAAK,UAAU;QACjC,MAAM,IAAIC,MAAM,CAAC,gCAAgC,EAAEH,QAAQE,QAAQ,CAAC,SAAS,CAAC;IAChF;IAEA,MAAME,aAAaJ,QAAQK,OAAO,CAACd,gBAAgB;IACnD,MAAMe,QAAQN,QAAQK,OAAO,CAACb,aAAa;IAC3C,MAAMe,YAAYP,QAAQK,OAAO,CAACZ,iBAAiB;IACnD,IAAI,CAACW,cAAc,CAACE,SAAS,CAACC,WAAW;QACvC,OAAO;YAACC,SAAS;YAAaC,QAAQ;QAAwB;IAChE;IAEA,MAAMC,UAAUzB,OAAO0B,IAAI,CAACxB,kBAAkBa,QAAQY,IAAI;IAC1D,IAAIC;IACJ,IAAI;QACFA,WAAW,MAAMjB,SAASkB,MAAM,CAACJ,QAAQK,QAAQ,CAAC,SAASR;IAC7D,EAAE,OAAOS,OAAO;QACd5B,SAAS6B,IAAI,CAAC;YAACb;YAAYc,KAAKF;QAAK,GAAG;QACxCH,WAAW;IACb;IACA,IAAI,CAACA,UAAU,OAAO;QAACL,SAAS;QAAaC,QAAQ;QAAqBL;IAAU;IAEpF,IAAIe;IACJ,IAAI;QACFA,UAAUC,KAAKC,KAAK,CAACX,QAAQK,QAAQ,CAAC;IACxC,EAAE,OAAOC,OAAO;QACd5B,SAAS6B,IAAI,CAAC;YAACb;YAAYc,KAAKF;QAAK,GAAG;QACxC,OAAO;YAACR,SAAS;YAAaC,QAAQ;YAAqBL;QAAU;IACvE;IAEA,MAAMkB,SAAS,MAAM3B,QAAQ4B,MAAM,GAAGC,WAAW,CAAC,OAAOC,KACvDnC,kBAAkB;YAChBmC;YACArB;YACAE;YACAa;YACAO,iCAAiC/B,QAAQ+B,+BAA+B;YACxEC,mBAAmBhC,QAAQgC,iBAAiB;YAC5CC,oBAAoBjC,QAAQiC,kBAAkB;YAC9CC,8BAA8BlC,QAAQkC,4BAA4B;QACpE;IAGF,IAAIP,OAAOQ,wBAAwB,IAAInC,QAAQoC,6BAA6B,EAAE;QAC5E,MAAMpC,QAAQoC,6BAA6B,CAACT,OAAOQ,wBAAwB;IAC7E;IAEA,OAAOR,OAAOd,OAAO,CAACwB,UAAU,CAAC,eAC7B;QAACxB,SAAS;QAAaJ;IAAU,IACjC;QAACI,SAAS;QAAaJ;IAAU;AACvC"}
|
package/dist/core/webhook.d.ts
CHANGED
|
@@ -8,13 +8,14 @@ export interface HandleGithubEventParams {
|
|
|
8
8
|
publishSourcePush: PublishSourcePushFn;
|
|
9
9
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
10
10
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
11
|
-
deleteInstallationTokenSecret?: ((params: {
|
|
12
|
-
workspaceId: string;
|
|
13
|
-
installationId: number;
|
|
14
|
-
}) => Promise<unknown>) | undefined;
|
|
15
11
|
}
|
|
16
12
|
export type HandleGithubEventOutcome = 'published' | 'duplicate' | 'published-envelope' | 'duplicate-envelope' | 'published-push-envelope-only' | 'duplicate-push-envelope-only' | 'unknown-installation' | 'missing-connection' | 'inactive-connection' | 'no-installation-id';
|
|
17
|
-
export
|
|
13
|
+
export interface HandleGithubEventResult {
|
|
18
14
|
outcome: HandleGithubEventOutcome;
|
|
19
|
-
|
|
15
|
+
installationTokenCleanup?: {
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
installationId: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare function handleGithubEvent(params: HandleGithubEventParams): Promise<HandleGithubEventResult>;
|
|
20
21
|
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/core/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAE1B,MAAM,mCAAmC,CAAC;AAe3C,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,aAAa,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/core/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAE1B,MAAM,mCAAmC,CAAC;AAe3C,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,aAAa,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,MAAM,MAAM,wBAAwB,GAChC,WAAW,GACX,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,8BAA8B,GAC9B,8BAA8B,GAC9B,sBAAsB,GACtB,oBAAoB,GACpB,qBAAqB,GACrB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,wBAAwB,CAAC;IAClC,wBAAwB,CAAC,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,GAAG,SAAS,CAAC;CACtF;AAMD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAqHlC"}
|
package/dist/core/webhook.js
CHANGED
|
@@ -78,9 +78,9 @@ export async function handleGithubEvent(params) {
|
|
|
78
78
|
provider: GITHUB_SOURCE,
|
|
79
79
|
deliveryId: params.deliveryId
|
|
80
80
|
});
|
|
81
|
-
return {
|
|
81
|
+
return withInstallationTokenCleanup({
|
|
82
82
|
outcome: 'inactive-connection'
|
|
83
|
-
};
|
|
83
|
+
}, params.event, action, connection.workspaceId, installationId);
|
|
84
84
|
}
|
|
85
85
|
if (params.event === 'push') {
|
|
86
86
|
const validated = githubPushPayloadSchema.safeParse(params.payload);
|
|
@@ -114,36 +114,20 @@ export async function handleGithubEvent(params) {
|
|
|
114
114
|
connection,
|
|
115
115
|
event: eventName
|
|
116
116
|
});
|
|
117
|
-
|
|
118
|
-
await deleteInstallationTokenSecretBestEffort({
|
|
119
|
-
deleteInstallationTokenSecret: params.deleteInstallationTokenSecret,
|
|
120
|
-
event: params.event,
|
|
121
|
-
action,
|
|
122
|
-
deliveryId: params.deliveryId,
|
|
123
|
-
workspaceId: connection.workspaceId,
|
|
124
|
-
installationId
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
117
|
+
return withInstallationTokenCleanup(result, params.event, action, connection.workspaceId, installationId);
|
|
128
118
|
}
|
|
129
119
|
function shouldDeleteInstallationTokenSecret(event, action) {
|
|
130
120
|
return event === 'installation' && (action === 'deleted' || action === 'suspend');
|
|
131
121
|
}
|
|
132
|
-
|
|
133
|
-
if (!shouldDeleteInstallationTokenSecret(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
deliveryId: params.deliveryId,
|
|
142
|
-
installationId: params.installationId,
|
|
143
|
-
workspaceId: params.workspaceId,
|
|
144
|
-
error
|
|
145
|
-
}, 'github webhook installation token cleanup failed');
|
|
146
|
-
}
|
|
122
|
+
function withInstallationTokenCleanup(result, event, action, workspaceId, installationId) {
|
|
123
|
+
if (!shouldDeleteInstallationTokenSecret(event, action)) return result;
|
|
124
|
+
return {
|
|
125
|
+
...result,
|
|
126
|
+
installationTokenCleanup: {
|
|
127
|
+
workspaceId,
|
|
128
|
+
installationId
|
|
129
|
+
}
|
|
130
|
+
};
|
|
147
131
|
}
|
|
148
132
|
async function publishGithubPush(params) {
|
|
149
133
|
if (isBranchDeletion(params.eventPayload.after)) {
|
package/dist/core/webhook.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/webhook.ts"],"sourcesContent":["import {\n buildProviderRepositoryId,\n type GetIntegrationConnectionByIdFn,\n type IntegrationTx,\n type PublishIntegrationEventReceivedFn,\n type PublishSourcePushFn,\n type RecordDeliveryOnlyFn,\n type SourcePushPayload,\n} from '@shipfox/api-integration-core-dto';\nimport {\n type GithubPushPayloadDto,\n githubPushPayloadSchema,\n githubWebhookActionSchema,\n githubWebhookInstallationSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {getGithubInstallationByInstallationId} from '#db/installations.js';\n\nconst REFS_HEADS_PREFIX = 'refs/heads/';\nconst GITHUB_SOURCE = 'github';\n// GitHub sends a `push` webhook for a branch deletion with `after` set to this all-zero SHA.\nconst DELETED_BRANCH_SHA = '0'.repeat(40);\n\nexport interface HandleGithubEventParams {\n tx: IntegrationTx;\n deliveryId: string;\n event: string;\n payload: unknown;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n deleteInstallationTokenSecret?:\n | ((params: {workspaceId: string; installationId: number}) => Promise<unknown>)\n | undefined;\n}\n\nexport type HandleGithubEventOutcome =\n | 'published'\n | 'duplicate'\n | 'published-envelope'\n | 'duplicate-envelope'\n | 'published-push-envelope-only'\n | 'duplicate-push-envelope-only'\n | 'unknown-installation'\n | 'missing-connection'\n | 'inactive-connection'\n | 'no-installation-id';\n\nfunction isBranchDeletion(after: string): boolean {\n return after === DELETED_BRANCH_SHA;\n}\n\nexport async function handleGithubEvent(\n params: HandleGithubEventParams,\n): Promise<{outcome: HandleGithubEventOutcome}> {\n const actionEnvelope = githubWebhookActionSchema.safeParse(params.payload);\n const action = actionEnvelope.success ? actionEnvelope.data.action : undefined;\n const installationEnvelope = githubWebhookInstallationSchema.safeParse(params.payload);\n const installationId = installationEnvelope.success\n ? installationEnvelope.data.installation?.id\n : undefined;\n if (installationId === undefined) {\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'no-installation-id'};\n }\n\n const installation = await getGithubInstallationByInstallationId(String(installationId), {\n tx: params.tx,\n });\n if (!installation) {\n logger().warn(\n {deliveryId: params.deliveryId, installationId},\n 'github webhook: unknown installation, dropping',\n );\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'unknown-installation'};\n }\n\n const connection = await params.getIntegrationConnectionById(installation.connectionId, {\n tx: params.tx,\n });\n if (!connection) {\n logger().warn(\n {deliveryId: params.deliveryId, installationId, connectionId: installation.connectionId},\n 'github webhook: installation has no connection, dropping',\n );\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'missing-connection'};\n }\n\n if (connection.lifecycleStatus !== 'active') {\n const logContext = {\n deliveryId: params.deliveryId,\n installationId,\n connectionId: connection.id,\n workspaceId: connection.workspaceId,\n lifecycleStatus: connection.lifecycleStatus,\n };\n // `disabled` is an expected steady state; only `error` is anomalous.\n if (connection.lifecycleStatus === 'error') {\n logger().warn(logContext, 'github webhook: connection in error state, dropping');\n } else {\n logger().info(logContext, 'github webhook: connection disabled, dropping');\n }\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'inactive-connection'};\n }\n\n if (params.event === 'push') {\n const validated = githubPushPayloadSchema.safeParse(params.payload);\n if (!validated.success) {\n logger().warn(\n {deliveryId: params.deliveryId, issues: validated.error.issues},\n 'github webhook push payload failed schema validation; publishing generic envelope only',\n );\n return publishGithubEnvelopeOnly({\n tx: params.tx,\n deliveryId: params.deliveryId,\n payload: params.payload,\n publishIntegrationEventReceived: params.publishIntegrationEventReceived,\n connection,\n event: 'push',\n });\n }\n\n return publishGithubPush({\n ...params,\n eventPayload: validated.data,\n rawPayload: params.payload,\n connection,\n });\n }\n\n const eventName = action ? `${params.event}.${action}` : params.event;\n const result = await publishGithubEnvelopeOnly({\n tx: params.tx,\n deliveryId: params.deliveryId,\n payload: params.payload,\n publishIntegrationEventReceived: params.publishIntegrationEventReceived,\n connection,\n event: eventName,\n });\n if (result.outcome === 'published-envelope') {\n await deleteInstallationTokenSecretBestEffort({\n deleteInstallationTokenSecret: params.deleteInstallationTokenSecret,\n event: params.event,\n action,\n deliveryId: params.deliveryId,\n workspaceId: connection.workspaceId,\n installationId,\n });\n }\n return result;\n}\n\nfunction shouldDeleteInstallationTokenSecret(event: string, action: string | undefined): boolean {\n return event === 'installation' && (action === 'deleted' || action === 'suspend');\n}\n\nasync function deleteInstallationTokenSecretBestEffort(params: {\n deleteInstallationTokenSecret:\n | ((params: {workspaceId: string; installationId: number}) => Promise<unknown>)\n | undefined;\n event: string;\n action: string | undefined;\n deliveryId: string;\n workspaceId: string;\n installationId: number;\n}): Promise<void> {\n if (!shouldDeleteInstallationTokenSecret(params.event, params.action)) return;\n\n try {\n await params.deleteInstallationTokenSecret?.({\n workspaceId: params.workspaceId,\n installationId: params.installationId,\n });\n } catch (error) {\n logger().warn(\n {\n deliveryId: params.deliveryId,\n installationId: params.installationId,\n workspaceId: params.workspaceId,\n error,\n },\n 'github webhook installation token cleanup failed',\n );\n }\n}\n\nasync function publishGithubPush(params: {\n tx: IntegrationTx;\n deliveryId: string;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n eventPayload: GithubPushPayloadDto;\n rawPayload: unknown;\n connection: {\n id: string;\n workspaceId: string;\n slug: string;\n displayName: string;\n };\n}): Promise<{outcome: HandleGithubEventOutcome}> {\n if (isBranchDeletion(params.eventPayload.after)) {\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n event: 'push',\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: params.rawPayload,\n },\n });\n return {\n outcome: result.published ? 'published-push-envelope-only' : 'duplicate-push-envelope-only',\n };\n }\n\n const ref = stripRefsHeads(params.eventPayload.ref);\n const defaultBranch = params.eventPayload.repository.default_branch;\n const push: SourcePushPayload = {\n externalRepositoryId: buildProviderRepositoryId(\n GITHUB_SOURCE,\n String(params.eventPayload.repository.id),\n ),\n ref,\n headCommitSha: params.eventPayload.after,\n defaultBranch,\n isDefaultBranch: ref === defaultBranch,\n };\n const result = await params.publishSourcePush({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n rawPayload: params.rawPayload,\n push,\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nasync function publishGithubEnvelopeOnly(params: {\n tx: IntegrationTx;\n deliveryId: string;\n payload: unknown;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n connection: {id: string; workspaceId: string; slug: string; displayName: string};\n event: string;\n}): Promise<{outcome: HandleGithubEventOutcome}> {\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n event: params.event,\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: params.payload,\n },\n });\n return {outcome: result.published ? 'published-envelope' : 'duplicate-envelope'};\n}\n\nfunction stripRefsHeads(ref: string): string {\n return ref.startsWith(REFS_HEADS_PREFIX) ? ref.slice(REFS_HEADS_PREFIX.length) : ref;\n}\n"],"names":["buildProviderRepositoryId","githubPushPayloadSchema","githubWebhookActionSchema","githubWebhookInstallationSchema","logger","getGithubInstallationByInstallationId","REFS_HEADS_PREFIX","GITHUB_SOURCE","DELETED_BRANCH_SHA","repeat","isBranchDeletion","after","handleGithubEvent","params","actionEnvelope","safeParse","payload","action","success","data","undefined","installationEnvelope","installationId","installation","id","recordDeliveryOnly","tx","provider","deliveryId","outcome","String","warn","connection","getIntegrationConnectionById","connectionId","lifecycleStatus","logContext","workspaceId","info","event","validated","issues","error","publishGithubEnvelopeOnly","publishIntegrationEventReceived","publishGithubPush","eventPayload","rawPayload","eventName","result","deleteInstallationTokenSecretBestEffort","deleteInstallationTokenSecret","shouldDeleteInstallationTokenSecret","source","slug","connectionName","displayName","receivedAt","Date","toISOString","published","ref","stripRefsHeads","defaultBranch","repository","default_branch","push","externalRepositoryId","headCommitSha","isDefaultBranch","publishSourcePush","startsWith","slice","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook.ts"],"sourcesContent":["import {\n buildProviderRepositoryId,\n type GetIntegrationConnectionByIdFn,\n type IntegrationTx,\n type PublishIntegrationEventReceivedFn,\n type PublishSourcePushFn,\n type RecordDeliveryOnlyFn,\n type SourcePushPayload,\n} from '@shipfox/api-integration-core-dto';\nimport {\n type GithubPushPayloadDto,\n githubPushPayloadSchema,\n githubWebhookActionSchema,\n githubWebhookInstallationSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {getGithubInstallationByInstallationId} from '#db/installations.js';\n\nconst REFS_HEADS_PREFIX = 'refs/heads/';\nconst GITHUB_SOURCE = 'github';\n// GitHub sends a `push` webhook for a branch deletion with `after` set to this all-zero SHA.\nconst DELETED_BRANCH_SHA = '0'.repeat(40);\n\nexport interface HandleGithubEventParams {\n tx: IntegrationTx;\n deliveryId: string;\n event: string;\n payload: unknown;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport type HandleGithubEventOutcome =\n | 'published'\n | 'duplicate'\n | 'published-envelope'\n | 'duplicate-envelope'\n | 'published-push-envelope-only'\n | 'duplicate-push-envelope-only'\n | 'unknown-installation'\n | 'missing-connection'\n | 'inactive-connection'\n | 'no-installation-id';\n\nexport interface HandleGithubEventResult {\n outcome: HandleGithubEventOutcome;\n installationTokenCleanup?: {workspaceId: string; installationId: number} | undefined;\n}\n\nfunction isBranchDeletion(after: string): boolean {\n return after === DELETED_BRANCH_SHA;\n}\n\nexport async function handleGithubEvent(\n params: HandleGithubEventParams,\n): Promise<HandleGithubEventResult> {\n const actionEnvelope = githubWebhookActionSchema.safeParse(params.payload);\n const action = actionEnvelope.success ? actionEnvelope.data.action : undefined;\n const installationEnvelope = githubWebhookInstallationSchema.safeParse(params.payload);\n const installationId = installationEnvelope.success\n ? installationEnvelope.data.installation?.id\n : undefined;\n if (installationId === undefined) {\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'no-installation-id'};\n }\n\n const installation = await getGithubInstallationByInstallationId(String(installationId), {\n tx: params.tx,\n });\n if (!installation) {\n logger().warn(\n {deliveryId: params.deliveryId, installationId},\n 'github webhook: unknown installation, dropping',\n );\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'unknown-installation'};\n }\n\n const connection = await params.getIntegrationConnectionById(installation.connectionId, {\n tx: params.tx,\n });\n if (!connection) {\n logger().warn(\n {deliveryId: params.deliveryId, installationId, connectionId: installation.connectionId},\n 'github webhook: installation has no connection, dropping',\n );\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return {outcome: 'missing-connection'};\n }\n\n if (connection.lifecycleStatus !== 'active') {\n const logContext = {\n deliveryId: params.deliveryId,\n installationId,\n connectionId: connection.id,\n workspaceId: connection.workspaceId,\n lifecycleStatus: connection.lifecycleStatus,\n };\n // `disabled` is an expected steady state; only `error` is anomalous.\n if (connection.lifecycleStatus === 'error') {\n logger().warn(logContext, 'github webhook: connection in error state, dropping');\n } else {\n logger().info(logContext, 'github webhook: connection disabled, dropping');\n }\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n deliveryId: params.deliveryId,\n });\n return withInstallationTokenCleanup(\n {outcome: 'inactive-connection'},\n params.event,\n action,\n connection.workspaceId,\n installationId,\n );\n }\n\n if (params.event === 'push') {\n const validated = githubPushPayloadSchema.safeParse(params.payload);\n if (!validated.success) {\n logger().warn(\n {deliveryId: params.deliveryId, issues: validated.error.issues},\n 'github webhook push payload failed schema validation; publishing generic envelope only',\n );\n return publishGithubEnvelopeOnly({\n tx: params.tx,\n deliveryId: params.deliveryId,\n payload: params.payload,\n publishIntegrationEventReceived: params.publishIntegrationEventReceived,\n connection,\n event: 'push',\n });\n }\n\n return publishGithubPush({\n ...params,\n eventPayload: validated.data,\n rawPayload: params.payload,\n connection,\n });\n }\n\n const eventName = action ? `${params.event}.${action}` : params.event;\n const result = await publishGithubEnvelopeOnly({\n tx: params.tx,\n deliveryId: params.deliveryId,\n payload: params.payload,\n publishIntegrationEventReceived: params.publishIntegrationEventReceived,\n connection,\n event: eventName,\n });\n return withInstallationTokenCleanup(\n result,\n params.event,\n action,\n connection.workspaceId,\n installationId,\n );\n}\n\nfunction shouldDeleteInstallationTokenSecret(event: string, action: string | undefined): boolean {\n return event === 'installation' && (action === 'deleted' || action === 'suspend');\n}\n\nfunction withInstallationTokenCleanup(\n result: HandleGithubEventResult,\n event: string,\n action: string | undefined,\n workspaceId: string,\n installationId: number,\n): HandleGithubEventResult {\n if (!shouldDeleteInstallationTokenSecret(event, action)) return result;\n return {...result, installationTokenCleanup: {workspaceId, installationId}};\n}\n\nasync function publishGithubPush(params: {\n tx: IntegrationTx;\n deliveryId: string;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n eventPayload: GithubPushPayloadDto;\n rawPayload: unknown;\n connection: {\n id: string;\n workspaceId: string;\n slug: string;\n displayName: string;\n };\n}): Promise<{outcome: HandleGithubEventOutcome}> {\n if (isBranchDeletion(params.eventPayload.after)) {\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n event: 'push',\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: params.rawPayload,\n },\n });\n return {\n outcome: result.published ? 'published-push-envelope-only' : 'duplicate-push-envelope-only',\n };\n }\n\n const ref = stripRefsHeads(params.eventPayload.ref);\n const defaultBranch = params.eventPayload.repository.default_branch;\n const push: SourcePushPayload = {\n externalRepositoryId: buildProviderRepositoryId(\n GITHUB_SOURCE,\n String(params.eventPayload.repository.id),\n ),\n ref,\n headCommitSha: params.eventPayload.after,\n defaultBranch,\n isDefaultBranch: ref === defaultBranch,\n };\n const result = await params.publishSourcePush({\n tx: params.tx,\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n rawPayload: params.rawPayload,\n push,\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nasync function publishGithubEnvelopeOnly(params: {\n tx: IntegrationTx;\n deliveryId: string;\n payload: unknown;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n connection: {id: string; workspaceId: string; slug: string; displayName: string};\n event: string;\n}): Promise<{outcome: HandleGithubEventOutcome}> {\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: GITHUB_SOURCE,\n source: params.connection.slug,\n event: params.event,\n workspaceId: params.connection.workspaceId,\n connectionId: params.connection.id,\n connectionName: params.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: params.payload,\n },\n });\n return {outcome: result.published ? 'published-envelope' : 'duplicate-envelope'};\n}\n\nfunction stripRefsHeads(ref: string): string {\n return ref.startsWith(REFS_HEADS_PREFIX) ? ref.slice(REFS_HEADS_PREFIX.length) : ref;\n}\n"],"names":["buildProviderRepositoryId","githubPushPayloadSchema","githubWebhookActionSchema","githubWebhookInstallationSchema","logger","getGithubInstallationByInstallationId","REFS_HEADS_PREFIX","GITHUB_SOURCE","DELETED_BRANCH_SHA","repeat","isBranchDeletion","after","handleGithubEvent","params","actionEnvelope","safeParse","payload","action","success","data","undefined","installationEnvelope","installationId","installation","id","recordDeliveryOnly","tx","provider","deliveryId","outcome","String","warn","connection","getIntegrationConnectionById","connectionId","lifecycleStatus","logContext","workspaceId","info","withInstallationTokenCleanup","event","validated","issues","error","publishGithubEnvelopeOnly","publishIntegrationEventReceived","publishGithubPush","eventPayload","rawPayload","eventName","result","shouldDeleteInstallationTokenSecret","installationTokenCleanup","source","slug","connectionName","displayName","receivedAt","Date","toISOString","published","ref","stripRefsHeads","defaultBranch","repository","default_branch","push","externalRepositoryId","headCommitSha","isDefaultBranch","publishSourcePush","startsWith","slice","length"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export { handleGithubCallback } from '#core/install.js';
|
|
|
19
19
|
export { signGithubInstallState, verifyGithubInstallState } from '#core/state.js';
|
|
20
20
|
export type { HandleGithubEventOutcome } from '#core/webhook.js';
|
|
21
21
|
export { handleGithubEvent } from '#core/webhook.js';
|
|
22
|
+
export type { CreateGithubWebhookProcessorOptions, GithubWebhookProcessor, } from '#core/webhook-processor.js';
|
|
23
|
+
export { createGithubWebhookProcessor } from '#core/webhook-processor.js';
|
|
22
24
|
export type { GithubInstallation, UpsertGithubInstallationParams } from '#db/installations.js';
|
|
23
25
|
export { getGithubInstallationByConnectionId, getGithubInstallationByInstallationId, upsertGithubInstallation, } from '#db/installations.js';
|
|
24
26
|
export { type CreateGithubE2eRoutesOptions, closeDb, createGithubE2eRoutes, db, migrationsPath };
|
|
@@ -49,5 +51,9 @@ export declare function createGithubIntegrationProvider(options: CreateGithubInt
|
|
|
49
51
|
id: string;
|
|
50
52
|
}): Promise<string | undefined>;
|
|
51
53
|
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
54
|
+
webhookProcessors: {
|
|
55
|
+
routeIds: readonly ["github"];
|
|
56
|
+
processor: import("#core/webhook-processor.js").GithubWebhookProcessor;
|
|
57
|
+
}[];
|
|
52
58
|
};
|
|
53
59
|
//# 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,iCAAiC,EACjC,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAC,+BAA+B,EAAC,MAAM,qCAAqC,CAAC;AAEzF,OAAO,EAAC,wBAAwB,EAAC,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAC,2BAA2B,EAAC,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createGithubApiClient } from '#api/client.js';
|
|
|
2
2
|
import { deleteGithubInstallationTokenSecret } from '#api/installation-token-provider.js';
|
|
3
3
|
import { GithubAgentToolsProvider } from '#core/agent-tools.js';
|
|
4
4
|
import { GithubSourceControlProvider } from '#core/source-control.js';
|
|
5
|
+
import { createGithubWebhookProcessor } from '#core/webhook-processor.js';
|
|
5
6
|
import { closeDb, db } from '#db/db.js';
|
|
6
7
|
import { getGithubInstallationByConnectionId } from '#db/installations.js';
|
|
7
8
|
import { migrationsPath } from '#db/migrations.js';
|
|
@@ -15,12 +16,22 @@ export { GithubIntegrationProviderError } from '#core/errors.js';
|
|
|
15
16
|
export { handleGithubCallback } from '#core/install.js';
|
|
16
17
|
export { signGithubInstallState, verifyGithubInstallState } from '#core/state.js';
|
|
17
18
|
export { handleGithubEvent } from '#core/webhook.js';
|
|
19
|
+
export { createGithubWebhookProcessor } from '#core/webhook-processor.js';
|
|
18
20
|
export { getGithubInstallationByConnectionId, getGithubInstallationByInstallationId, upsertGithubInstallation } from '#db/installations.js';
|
|
19
21
|
export { closeDb, createGithubE2eRoutes, db, migrationsPath };
|
|
20
22
|
export function createGithubIntegrationProvider(options) {
|
|
21
23
|
const github = options.github ?? createGithubApiClient();
|
|
22
24
|
const getInstallationByConnectionId = options.getGithubInstallationByConnectionId ?? getGithubInstallationByConnectionId;
|
|
23
25
|
const deleteSecrets = options.deleteSecrets;
|
|
26
|
+
const deleteInstallationTokenSecret = deleteSecrets ? (params)=>deleteGithubInstallationTokenSecret({
|
|
27
|
+
workspaceId: params.workspaceId,
|
|
28
|
+
installationId: params.installationId,
|
|
29
|
+
deleteSecrets
|
|
30
|
+
}) : undefined;
|
|
31
|
+
const webhookProcessor = createGithubWebhookProcessor({
|
|
32
|
+
...options,
|
|
33
|
+
deleteInstallationTokenSecret
|
|
34
|
+
});
|
|
24
35
|
return {
|
|
25
36
|
provider: 'github',
|
|
26
37
|
displayName: 'GitHub',
|
|
@@ -45,7 +56,10 @@ export function createGithubIntegrationProvider(options) {
|
|
|
45
56
|
createGithubIntegrationRoutes({
|
|
46
57
|
github,
|
|
47
58
|
getExistingGithubConnection: options.getExistingGithubConnection,
|
|
48
|
-
connectGithubInstallation: options.connectGithubInstallation
|
|
59
|
+
connectGithubInstallation: options.connectGithubInstallation,
|
|
60
|
+
...options.requireActiveWorkspaceMembership ? {
|
|
61
|
+
requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
|
|
62
|
+
} : {}
|
|
49
63
|
}),
|
|
50
64
|
createGithubWebhookRoutes({
|
|
51
65
|
coreDb: options.coreDb,
|
|
@@ -53,12 +67,17 @@ export function createGithubIntegrationProvider(options) {
|
|
|
53
67
|
publishSourcePush: options.publishSourcePush,
|
|
54
68
|
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
55
69
|
getIntegrationConnectionById: options.getIntegrationConnectionById,
|
|
56
|
-
deleteInstallationTokenSecret
|
|
57
|
-
|
|
58
|
-
installationId: params.installationId,
|
|
59
|
-
deleteSecrets
|
|
60
|
-
}) : undefined
|
|
70
|
+
deleteInstallationTokenSecret,
|
|
71
|
+
processor: webhookProcessor
|
|
61
72
|
})
|
|
73
|
+
],
|
|
74
|
+
webhookProcessors: [
|
|
75
|
+
{
|
|
76
|
+
routeIds: [
|
|
77
|
+
'github'
|
|
78
|
+
],
|
|
79
|
+
processor: webhookProcessor
|
|
80
|
+
}
|
|
62
81
|
]
|
|
63
82
|
};
|
|
64
83
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n PublishIntegrationEventReceivedFn,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createGithubApiClient, type GithubApiClient} from '#api/client.js';\nimport type {GithubInstallationTokenProvider} from '#api/installation-token-provider.js';\nimport {deleteGithubInstallationTokenSecret} from '#api/installation-token-provider.js';\nimport {GithubAgentToolsProvider} from '#core/agent-tools.js';\nimport {GithubSourceControlProvider} from '#core/source-control.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateGithubE2eRoutesOptions,\n createGithubE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nimport {\n type CreateGithubIntegrationRoutesOptions,\n createGithubIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {createGithubWebhookRoutes} from '#presentation/routes/webhooks.js';\n\nexport type {GithubApiClient} from '#api/client.js';\nexport {\n encodeInstallationTokenEnvelope,\n githubInstallationTokenNamespace,\n} from '#api/installation-token-envelope.js';\nexport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nexport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolCategory,\n type GithubAgentToolId,\n type GithubAgentToolPermission,\n type GithubAgentToolPermissionAccess,\n type GithubAgentToolRequiredPermission,\n type GithubAgentToolRequiredScope,\n GithubAgentToolsProvider,\n githubAgentToolCatalog,\n} from '#core/agent-tools.js';\nexport {GithubIntegrationProviderError} from '#core/errors.js';\nexport type {ConnectGithubInstallationInput} from '#core/install.js';\nexport {handleGithubCallback} from '#core/install.js';\nexport {signGithubInstallState, verifyGithubInstallState} from '#core/state.js';\nexport type {HandleGithubEventOutcome} from '#core/webhook.js';\nexport {handleGithubEvent} from '#core/webhook.js';\nexport type {GithubInstallation, UpsertGithubInstallationParams} from '#db/installations.js';\nexport {\n getGithubInstallationByConnectionId,\n getGithubInstallationByInstallationId,\n upsertGithubInstallation,\n} from '#db/installations.js';\nexport {type CreateGithubE2eRoutesOptions, closeDb, createGithubE2eRoutes, db, migrationsPath};\n\nexport interface CreateGithubIntegrationProviderOptions\n extends Omit<CreateGithubIntegrationRoutesOptions, 'github'> {\n github?: GithubApiClient | undefined;\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n getGithubInstallationByConnectionId?: typeof getGithubInstallationByConnectionId | undefined;\n deleteSecrets?:\n | ((params: {workspaceId: string; namespace: string}) => Promise<number>)\n | undefined;\n agentTools?: {tokenProvider: GithubInstallationTokenProvider} | undefined;\n}\n\nexport function createGithubIntegrationProvider(options: CreateGithubIntegrationProviderOptions) {\n const github = options.github ?? createGithubApiClient();\n const getInstallationByConnectionId =\n options.getGithubInstallationByConnectionId ?? getGithubInstallationByConnectionId;\n const deleteSecrets = options.deleteSecrets;\n\n return {\n provider: 'github' as const,\n displayName: 'GitHub',\n adapters: {\n source_control: new GithubSourceControlProvider(github),\n agent_tools: new GithubAgentToolsProvider({\n getInstallationByConnectionId: getInstallationByConnectionId,\n tokenProvider: options.agentTools?.tokenProvider,\n }),\n },\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation) return undefined;\n const installationId = encodeURIComponent(installation.installationId);\n if (installation.accountType === 'Organization') {\n const login = encodeURIComponent(installation.accountLogin);\n return `https://github.com/organizations/${login}/settings/installations/${installationId}`;\n }\n return `https://github.com/settings/installations/${installationId}`;\n },\n routes: [\n createGithubIntegrationRoutes({\n github,\n getExistingGithubConnection: options.getExistingGithubConnection,\n connectGithubInstallation: options.connectGithubInstallation,\n }),\n createGithubWebhookRoutes({\n coreDb: options.coreDb,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n deleteInstallationTokenSecret
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n PublishIntegrationEventReceivedFn,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createGithubApiClient, type GithubApiClient} from '#api/client.js';\nimport type {GithubInstallationTokenProvider} from '#api/installation-token-provider.js';\nimport {deleteGithubInstallationTokenSecret} from '#api/installation-token-provider.js';\nimport {GithubAgentToolsProvider} from '#core/agent-tools.js';\nimport {GithubSourceControlProvider} from '#core/source-control.js';\nimport {createGithubWebhookProcessor} from '#core/webhook-processor.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateGithubE2eRoutesOptions,\n createGithubE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nimport {\n type CreateGithubIntegrationRoutesOptions,\n createGithubIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {createGithubWebhookRoutes} from '#presentation/routes/webhooks.js';\n\nexport type {GithubApiClient} from '#api/client.js';\nexport {\n encodeInstallationTokenEnvelope,\n githubInstallationTokenNamespace,\n} from '#api/installation-token-envelope.js';\nexport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nexport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolCategory,\n type GithubAgentToolId,\n type GithubAgentToolPermission,\n type GithubAgentToolPermissionAccess,\n type GithubAgentToolRequiredPermission,\n type GithubAgentToolRequiredScope,\n GithubAgentToolsProvider,\n githubAgentToolCatalog,\n} from '#core/agent-tools.js';\nexport {GithubIntegrationProviderError} from '#core/errors.js';\nexport type {ConnectGithubInstallationInput} from '#core/install.js';\nexport {handleGithubCallback} from '#core/install.js';\nexport {signGithubInstallState, verifyGithubInstallState} from '#core/state.js';\nexport type {HandleGithubEventOutcome} from '#core/webhook.js';\nexport {handleGithubEvent} from '#core/webhook.js';\nexport type {\n CreateGithubWebhookProcessorOptions,\n GithubWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createGithubWebhookProcessor} from '#core/webhook-processor.js';\nexport type {GithubInstallation, UpsertGithubInstallationParams} from '#db/installations.js';\nexport {\n getGithubInstallationByConnectionId,\n getGithubInstallationByInstallationId,\n upsertGithubInstallation,\n} from '#db/installations.js';\nexport {type CreateGithubE2eRoutesOptions, closeDb, createGithubE2eRoutes, db, migrationsPath};\n\nexport interface CreateGithubIntegrationProviderOptions\n extends Omit<CreateGithubIntegrationRoutesOptions, 'github'> {\n github?: GithubApiClient | undefined;\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n getGithubInstallationByConnectionId?: typeof getGithubInstallationByConnectionId | undefined;\n deleteSecrets?:\n | ((params: {workspaceId: string; namespace: string}) => Promise<number>)\n | undefined;\n agentTools?: {tokenProvider: GithubInstallationTokenProvider} | undefined;\n}\n\nexport function createGithubIntegrationProvider(options: CreateGithubIntegrationProviderOptions) {\n const github = options.github ?? createGithubApiClient();\n const getInstallationByConnectionId =\n options.getGithubInstallationByConnectionId ?? getGithubInstallationByConnectionId;\n const deleteSecrets = options.deleteSecrets;\n const deleteInstallationTokenSecret = deleteSecrets\n ? (params: {workspaceId: string; installationId: number}) =>\n deleteGithubInstallationTokenSecret({\n workspaceId: params.workspaceId,\n installationId: params.installationId,\n deleteSecrets,\n })\n : undefined;\n const webhookProcessor = createGithubWebhookProcessor({\n ...options,\n deleteInstallationTokenSecret,\n });\n\n return {\n provider: 'github' as const,\n displayName: 'GitHub',\n adapters: {\n source_control: new GithubSourceControlProvider(github),\n agent_tools: new GithubAgentToolsProvider({\n getInstallationByConnectionId: getInstallationByConnectionId,\n tokenProvider: options.agentTools?.tokenProvider,\n }),\n },\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation) return undefined;\n const installationId = encodeURIComponent(installation.installationId);\n if (installation.accountType === 'Organization') {\n const login = encodeURIComponent(installation.accountLogin);\n return `https://github.com/organizations/${login}/settings/installations/${installationId}`;\n }\n return `https://github.com/settings/installations/${installationId}`;\n },\n routes: [\n createGithubIntegrationRoutes({\n github,\n getExistingGithubConnection: options.getExistingGithubConnection,\n connectGithubInstallation: options.connectGithubInstallation,\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n }),\n createGithubWebhookRoutes({\n coreDb: options.coreDb,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n deleteInstallationTokenSecret,\n processor: webhookProcessor,\n }),\n ],\n webhookProcessors: [{routeIds: ['github'] as const, processor: webhookProcessor}],\n };\n}\n"],"names":["createGithubApiClient","deleteGithubInstallationTokenSecret","GithubAgentToolsProvider","GithubSourceControlProvider","createGithubWebhookProcessor","closeDb","db","getGithubInstallationByConnectionId","migrationsPath","createGithubE2eRoutes","createGithubIntegrationRoutes","createGithubWebhookRoutes","encodeInstallationTokenEnvelope","githubInstallationTokenNamespace","createGithubInstallationTokenProvider","githubAgentToolCatalog","GithubIntegrationProviderError","handleGithubCallback","signGithubInstallState","verifyGithubInstallState","handleGithubEvent","getGithubInstallationByInstallationId","upsertGithubInstallation","createGithubIntegrationProvider","options","github","getInstallationByConnectionId","deleteSecrets","deleteInstallationTokenSecret","params","workspaceId","installationId","undefined","webhookProcessor","provider","displayName","adapters","source_control","agent_tools","tokenProvider","agentTools","connectionExternalUrl","connection","installation","id","encodeURIComponent","accountType","login","accountLogin","routes","getExistingGithubConnection","connectGithubInstallation","requireActiveWorkspaceMembership","coreDb","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById","processor","webhookProcessors","routeIds"],"mappings":"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"}
|
|
@@ -8,6 +8,11 @@ export interface CreateGithubIntegrationRoutesOptions {
|
|
|
8
8
|
installationId: string;
|
|
9
9
|
}) => Promise<IntegrationConnection<'github'> | undefined>;
|
|
10
10
|
connectGithubInstallation: (input: ConnectGithubInstallationInput) => Promise<IntegrationConnection<'github'>>;
|
|
11
|
+
requireActiveWorkspaceMembership?: (input: {
|
|
12
|
+
workspaceId: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;
|
|
15
|
+
}) => Promise<unknown>;
|
|
11
16
|
}
|
|
12
|
-
export declare function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation, }: CreateGithubIntegrationRoutesOptions): RouteGroup;
|
|
17
|
+
export declare function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation, requireActiveWorkspaceMembership, }: CreateGithubIntegrationRoutesOptions): RouteGroup;
|
|
13
18
|
//# sourceMappingURL=install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAO7E,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAC,KAAK,8BAA8B,EAAuB,MAAM,kBAAkB,CAAC;AAK3F,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,eAAe,CAAC;IACxB,2BAA2B,EAAE,CAAC,KAAK,EAAE;QACnC,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC;IAC3D,yBAAyB,EAAE,CACzB,KAAK,EAAE,8BAA8B,KAClC,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,MAAM,EACN,2BAA2B,EAC3B,yBAAyB,EACzB,gCAAgC,GACjC,EAAE,oCAAoC,GAAG,UAAU,CA8DnD"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { AUTH_USER, requireUserContext, requireWorkspaceAccess } from '@shipfox/api-auth-context';
|
|
2
2
|
import { createGithubInstallBodySchema, createGithubInstallResponseSchema, githubCallbackQuerySchema, githubCallbackResponseSchema } from '@shipfox/api-integration-github-dto';
|
|
3
|
-
import { requireWorkspaceMembership } from '@shipfox/api-workspaces';
|
|
4
3
|
import { defineRoute } from '@shipfox/node-fastify';
|
|
5
4
|
import { config } from '#config.js';
|
|
6
5
|
import { handleGithubCallback } from '#core/install.js';
|
|
7
6
|
import { signGithubInstallState } from '#core/state.js';
|
|
8
7
|
import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
|
|
9
8
|
import { githubRouteErrorHandler } from './errors.js';
|
|
10
|
-
export function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation }) {
|
|
9
|
+
export function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation, requireActiveWorkspaceMembership }) {
|
|
11
10
|
const createInstallRoute = defineRoute({
|
|
12
11
|
method: 'POST',
|
|
13
12
|
path: '/install',
|
|
@@ -58,7 +57,7 @@ export function createGithubIntegrationRoutes({ github, getExistingGithubConnect
|
|
|
58
57
|
state: request.query.state,
|
|
59
58
|
sessionUserId: actor.userId,
|
|
60
59
|
sessionMemberships: actor.memberships,
|
|
61
|
-
requireWorkspaceMembership,
|
|
60
|
+
requireWorkspaceMembership: requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,
|
|
62
61
|
getExistingGithubConnection,
|
|
63
62
|
connectGithubInstallation
|
|
64
63
|
});
|
|
@@ -73,5 +72,8 @@ export function createGithubIntegrationRoutes({ github, getExistingGithubConnect
|
|
|
73
72
|
]
|
|
74
73
|
};
|
|
75
74
|
}
|
|
75
|
+
function unavailableWorkspaceMembershipCheck(_input) {
|
|
76
|
+
return Promise.reject(new Error('Workspaces inter-module client is not configured'));
|
|
77
|
+
}
|
|
76
78
|
|
|
77
79
|
//# sourceMappingURL=install.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createGithubInstallBodySchema,\n createGithubInstallResponseSchema,\n githubCallbackQuerySchema,\n githubCallbackResponseSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createGithubInstallBodySchema,\n createGithubInstallResponseSchema,\n githubCallbackQuerySchema,\n githubCallbackResponseSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {GithubApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {type ConnectGithubInstallationInput, handleGithubCallback} from '#core/install.js';\nimport {signGithubInstallState} from '#core/state.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {githubRouteErrorHandler} from './errors.js';\n\nexport interface CreateGithubIntegrationRoutesOptions {\n github: GithubApiClient;\n getExistingGithubConnection: (input: {\n installationId: string;\n }) => Promise<IntegrationConnection<'github'> | undefined>;\n connectGithubInstallation: (\n input: ConnectGithubInstallationInput,\n ) => Promise<IntegrationConnection<'github'>>;\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createGithubIntegrationRoutes({\n github,\n getExistingGithubConnection,\n connectGithubInstallation,\n requireActiveWorkspaceMembership,\n}: CreateGithubIntegrationRoutesOptions): RouteGroup {\n const createInstallRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a GitHub App installation URL for a workspace.',\n schema: {\n body: createGithubInstallBodySchema,\n response: {\n 200: createGithubInstallResponseSchema,\n },\n },\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n\n requireWorkspaceAccess({request, workspaceId});\n const state = signGithubInstallState({workspaceId, userId: actor.userId});\n const installUrl = new URL(\n `https://github.com/apps/${config.GITHUB_APP_SLUG}/installations/new`,\n );\n installUrl.searchParams.set('state', state);\n\n return {install_url: installUrl.toString()};\n },\n });\n\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the GitHub App installation callback.',\n schema: {\n querystring: githubCallbackQuerySchema,\n response: {\n 200: githubCallbackResponseSchema,\n },\n },\n errorHandler: githubRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const connection = await handleGithubCallback({\n github,\n code: request.query.code,\n installationId: request.query.installation_id,\n state: request.query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n getExistingGithubConnection,\n connectGithubInstallation,\n });\n\n return toIntegrationConnectionDto(connection);\n },\n });\n\n return {\n prefix: '/integrations/github',\n routes: [createInstallRoute, callbackApiRoute],\n };\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","createGithubInstallBodySchema","createGithubInstallResponseSchema","githubCallbackQuerySchema","githubCallbackResponseSchema","defineRoute","config","handleGithubCallback","signGithubInstallState","toIntegrationConnectionDto","githubRouteErrorHandler","createGithubIntegrationRoutes","github","getExistingGithubConnection","connectGithubInstallation","requireActiveWorkspaceMembership","createInstallRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","state","userId","installUrl","URL","GITHUB_APP_SLUG","searchParams","set","install_url","toString","callbackApiRoute","querystring","errorHandler","connection","code","query","installationId","installation_id","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","unavailableWorkspaceMembershipCheck","prefix","routes","_input","Promise","reject","Error"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,kBAAkB,EAAEC,sBAAsB,QAAO,4BAA4B;AAEhG,SACEC,6BAA6B,EAC7BC,iCAAiC,EACjCC,yBAAyB,EACzBC,4BAA4B,QACvB,sCAAsC;AAC7C,SAAQC,WAAW,QAAwB,wBAAwB;AAEnE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAA6CC,oBAAoB,QAAO,mBAAmB;AAC3F,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,uBAAuB,QAAO,cAAc;AAiBpD,OAAO,SAASC,8BAA8B,EAC5CC,MAAM,EACNC,2BAA2B,EAC3BC,yBAAyB,EACzBC,gCAAgC,EACK;IACrC,MAAMC,qBAAqBX,YAAY;QACrCY,QAAQ;QACRC,MAAM;QACNC,MAAMrB;QACNsB,aAAa;QACbC,QAAQ;YACNC,MAAMrB;YACNsB,UAAU;gBACR,KAAKrB;YACP;QACF;QACAsB,SAAS,CAACC;YACR,MAAM,EAACC,cAAcC,WAAW,EAAC,GAAGF,QAAQH,IAAI;YAChD,MAAMM,QAAQ7B,mBAAmB0B;YAEjCzB,uBAAuB;gBAACyB;gBAASE;YAAW;YAC5C,MAAME,QAAQrB,uBAAuB;gBAACmB;gBAAaG,QAAQF,MAAME,MAAM;YAAA;YACvE,MAAMC,aAAa,IAAIC,IACrB,CAAC,wBAAwB,EAAE1B,OAAO2B,eAAe,CAAC,kBAAkB,CAAC;YAEvEF,WAAWG,YAAY,CAACC,GAAG,CAAC,SAASN;YAErC,OAAO;gBAACO,aAAaL,WAAWM,QAAQ;YAAE;QAC5C;IACF;IAEA,MAAMC,mBAAmBjC,YAAY;QACnCY,QAAQ;QACRC,MAAM;QACNC,MAAMrB;QACNsB,aAAa;QACbC,QAAQ;YACNkB,aAAapC;YACboB,UAAU;gBACR,KAAKnB;YACP;QACF;QACAoC,cAAc9B;QACdc,SAAS,OAAOC;YACd,MAAMG,QAAQ7B,mBAAmB0B;YACjC,MAAMgB,aAAa,MAAMlC,qBAAqB;gBAC5CK;gBACA8B,MAAMjB,QAAQkB,KAAK,CAACD,IAAI;gBACxBE,gBAAgBnB,QAAQkB,KAAK,CAACE,eAAe;gBAC7ChB,OAAOJ,QAAQkB,KAAK,CAACd,KAAK;gBAC1BiB,eAAelB,MAAME,MAAM;gBAC3BiB,oBAAoBnB,MAAMoB,WAAW;gBACrCC,4BACElC,oCAAoCmC;gBACtCrC;gBACAC;YACF;YAEA,OAAOL,2BAA2BgC;QACpC;IACF;IAEA,OAAO;QACLU,QAAQ;QACRC,QAAQ;YAACpC;YAAoBsB;SAAiB;IAChD;AACF;AAEA,SAASY,oCAAoCG,MAI5C;IACC,OAAOC,QAAQC,MAAM,CAAC,IAAIC,MAAM;AAClC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GetIntegrationConnectionByIdFn, PublishIntegrationEventReceivedFn, 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 GithubWebhookProcessor } from '#core/webhook-processor.js';
|
|
4
5
|
export interface CreateGithubWebhookRoutesOptions {
|
|
5
6
|
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
6
7
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
@@ -11,6 +12,7 @@ export interface CreateGithubWebhookRoutesOptions {
|
|
|
11
12
|
workspaceId: string;
|
|
12
13
|
installationId: number;
|
|
13
14
|
}) => Promise<unknown>) | undefined;
|
|
15
|
+
processor?: GithubWebhookProcessor | undefined;
|
|
14
16
|
}
|
|
15
17
|
export declare function createGithubWebhookRoutes(options: CreateGithubWebhookRoutesOptions): RouteGroup;
|
|
16
18
|
//# sourceMappingURL=webhooks.d.ts.map
|