@shipfox/api-integration-sentry 2.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 +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +192 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/api/client.d.ts +24 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +101 -0
- package/dist/api/client.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -0
- package/dist/core/errors.d.ts +57 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +67 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/install.d.ts +63 -0
- package/dist/core/install.d.ts.map +1 -0
- package/dist/core/install.js +209 -0
- package/dist/core/install.js.map +1 -0
- package/dist/core/signature.d.ts +7 -0
- package/dist/core/signature.d.ts.map +1 -0
- package/dist/core/signature.js +6 -0
- package/dist/core/signature.js.map +1 -0
- package/dist/core/webhook.d.ts +49 -0
- package/dist/core/webhook.d.ts.map +1 -0
- package/dist/core/webhook.js +175 -0
- package/dist/core/webhook.js.map +1 -0
- package/dist/db/db.d.ts +327 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +18 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/installations.d.ts +74 -0
- package/dist/db/installations.d.ts.map +1 -0
- package/dist/db/installations.js +120 -0
- package/dist/db/installations.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +5 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/installations.d.ts +165 -0
- package/dist/db/schema/installations.d.ts.map +1 -0
- package/dist/db/schema/installations.js +42 -0
- package/dist/db/schema/installations.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/presentation/dto/integrations.d.ts +15 -0
- package/dist/presentation/dto/integrations.d.ts.map +1 -0
- package/dist/presentation/dto/integrations.js +9 -0
- package/dist/presentation/dto/integrations.js.map +1 -0
- package/dist/presentation/routes/errors.d.ts +2 -0
- package/dist/presentation/routes/errors.d.ts.map +1 -0
- package/dist/presentation/routes/errors.js +51 -0
- package/dist/presentation/routes/errors.js.map +1 -0
- package/dist/presentation/routes/install.d.ts +16 -0
- package/dist/presentation/routes/install.d.ts.map +1 -0
- package/dist/presentation/routes/install.js +76 -0
- package/dist/presentation/routes/install.js.map +1 -0
- package/dist/presentation/routes/installation-handler.d.ts +9 -0
- package/dist/presentation/routes/installation-handler.d.ts.map +1 -0
- package/dist/presentation/routes/installation-handler.js +70 -0
- package/dist/presentation/routes/installation-handler.js.map +1 -0
- package/dist/presentation/routes/issue-handler.d.ts +9 -0
- package/dist/presentation/routes/issue-handler.d.ts.map +1 -0
- package/dist/presentation/routes/issue-handler.js +50 -0
- package/dist/presentation/routes/issue-handler.js.map +1 -0
- package/dist/presentation/routes/webhook-context.d.ts +20 -0
- package/dist/presentation/routes/webhook-context.d.ts.map +1 -0
- package/dist/presentation/routes/webhook-context.js +10 -0
- package/dist/presentation/routes/webhook-context.js.map +1 -0
- package/dist/presentation/routes/webhook-delivery.d.ts +28 -0
- package/dist/presentation/routes/webhook-delivery.d.ts.map +1 -0
- package/dist/presentation/routes/webhook-delivery.js +56 -0
- package/dist/presentation/routes/webhook-delivery.js.map +1 -0
- package/dist/presentation/routes/webhook-request.d.ts +10 -0
- package/dist/presentation/routes/webhook-request.d.ts.map +1 -0
- package/dist/presentation/routes/webhook-request.js +56 -0
- package/dist/presentation/routes/webhook-request.js.map +1 -0
- package/dist/presentation/routes/webhooks.d.ts +5 -0
- package/dist/presentation/routes/webhooks.d.ts.map +1 -0
- package/dist/presentation/routes/webhooks.js +71 -0
- package/dist/presentation/routes/webhooks.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +5 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +8 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/prune-unclaimed-installations.d.ts +4 -0
- package/dist/temporal/activities/prune-unclaimed-installations.d.ts.map +1 -0
- package/dist/temporal/activities/prune-unclaimed-installations.js +11 -0
- package/dist/temporal/activities/prune-unclaimed-installations.js.map +1 -0
- package/dist/temporal/constants.d.ts +2 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +3 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/workflows/index.d.ts +2 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +3 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/temporal/workflows/prune-unclaimed-installations-cron.d.ts +2 -0
- package/dist/temporal/workflows/prune-unclaimed-installations-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/prune-unclaimed-installations-cron.js +14 -0
- package/dist/temporal/workflows/prune-unclaimed-installations-cron.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +14 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +70 -0
- package/src/api/client.test.ts +147 -0
- package/src/api/client.ts +147 -0
- package/src/config.test.ts +16 -0
- package/src/config.ts +35 -0
- package/src/connection-external-url.test.ts +79 -0
- package/src/core/errors.ts +81 -0
- package/src/core/install.test.ts +343 -0
- package/src/core/install.ts +305 -0
- package/src/core/signature.test.ts +50 -0
- package/src/core/signature.ts +11 -0
- package/src/core/webhook.test.ts +118 -0
- package/src/core/webhook.ts +246 -0
- package/src/db/db.ts +18 -0
- package/src/db/installations.test.ts +224 -0
- package/src/db/installations.ts +207 -0
- package/src/db/migrations.ts +4 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/installations.ts +46 -0
- package/src/index.test.ts +18 -0
- package/src/index.ts +127 -0
- package/src/presentation/dto/integrations.ts +9 -0
- package/src/presentation/routes/errors.ts +48 -0
- package/src/presentation/routes/install.test.ts +325 -0
- package/src/presentation/routes/install.ts +102 -0
- package/src/presentation/routes/installation-handler.ts +76 -0
- package/src/presentation/routes/issue-handler.ts +52 -0
- package/src/presentation/routes/webhook-context.ts +25 -0
- package/src/presentation/routes/webhook-delivery.ts +61 -0
- package/src/presentation/routes/webhook-request.ts +66 -0
- package/src/presentation/routes/webhooks.test.ts +773 -0
- package/src/presentation/routes/webhooks.ts +59 -0
- package/src/temporal/activities/index.ts +7 -0
- package/src/temporal/activities/prune-unclaimed-installations.test.ts +52 -0
- package/src/temporal/activities/prune-unclaimed-installations.ts +11 -0
- package/src/temporal/constants.ts +1 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/prune-unclaimed-installations-cron.ts +15 -0
- package/test/env.ts +11 -0
- package/test/factories/index.ts +9 -0
- package/test/factories/sentry-installation-webhook.ts +35 -0
- package/test/factories/sentry-installation.ts +44 -0
- package/test/factories/sentry-issue-webhook.ts +34 -0
- package/test/globalSetup.ts +14 -0
- package/test/index.ts +9 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n RecordDeliveryOnlyFn,\n SentryIssuePayload,\n UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-core-dto';\nimport type {SentryIssueWebhookDto} from '@shipfox/api-integration-sentry-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {SentryApiClient} from '#api/client.js';\nimport {\n SentryConnectionNotFoundError,\n SentryInstallationDeletedError,\n SentryInstallationNotFoundError,\n SentryInstallationUnclaimedError,\n SentryIntegrationProviderError,\n} from '#core/errors.js';\nimport {verifyAndPersistUnclaimedInstallation} from '#core/install.js';\nimport {\n getSentryInstallationByInstallationUuid,\n markSentryInstallationDeleted,\n type SentryInstallation,\n} from '#db/installations.js';\n\nconst SENTRY_SOURCE = 'sentry';\nconst DEFAULT_ISSUE_TITLE = 'Sentry issue';\nconst DELETED_STATUS = 'deleted';\n\nexport interface HandleSentryIssueEventParams {\n tx: IntegrationTx;\n deliveryId: string;\n payload: SentryIssueWebhookDto;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\n// Publishes the mapped event for a verified issue delivery. Throws a typed\n// SentryIssueDroppedError subclass when the delivery references state we cannot\n// publish against (unknown/deleted installation, or an install not yet claimed\n// into a workspace); the webhook layer records-and-drops those. Dedup of an\n// already-seen delivery is handled inside publishIntegrationEventReceived.\nexport async function handleSentryIssueEvent(params: HandleSentryIssueEventParams): Promise<void> {\n const installationUuid = params.payload.installation.uuid;\n\n const installation = await getSentryInstallationByInstallationUuid(installationUuid, {\n tx: params.tx,\n });\n if (!installation) {\n throw new SentryInstallationNotFoundError(installationUuid);\n }\n if (installation.status === DELETED_STATUS) {\n throw new SentryInstallationDeletedError(installationUuid);\n }\n // A verified-but-unclaimed install has no workspace yet, so there is nothing to\n // publish an event against. This is the pre-claim drop window (counted/logged).\n if (!installation.connectionId) {\n throw new SentryInstallationUnclaimedError(installationUuid);\n }\n\n const connection = await params.getIntegrationConnectionById(installation.connectionId, {\n tx: params.tx,\n });\n if (!connection) {\n throw new SentryConnectionNotFoundError(installation.connectionId);\n }\n\n await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: SENTRY_SOURCE,\n source: connection.slug,\n event: `issue.${params.payload.action}`,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n connectionName: connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: normalizeIssuePayload(params.payload),\n },\n });\n}\n\nexport interface HandleSentryInstallationCreatedParams {\n deliveryId: string;\n installationUuid: string;\n // From the signed payload; the webhook is authoritative so the slug is trusted\n // without a getInstallation round-trip. The code is the single-use grant.\n orgSlug: string | undefined;\n code: string | undefined;\n sentry: SentryApiClient;\n verifyInstall: boolean;\n getSentryInstallation: (input: {\n installationUuid: string;\n }) => Promise<SentryInstallation | undefined>;\n // Persists the verified-unclaimed row and records the delivery in one short\n // transaction, returning the persisted row.\n persistUnclaimedAndRecordDelivery: (input: {\n installationUuid: string;\n orgSlug: string;\n codeHash: string;\n deliveryId: string;\n }) => Promise<SentryInstallation>;\n // Records the delivery for dedup without persisting an install (reconcile/no-op,\n // missing code, or exchange failure → record-and-drop).\n recordDelivery: (deliveryId: string) => Promise<void>;\n}\n\n/**\n * The authoritative `installation.created` path. Whichever of webhook or browser\n * arrives first exchanges the single-use code and persists a verified-unclaimed\n * row; the other reconciles. Idempotent on the installation uuid. The exchange\n * runs outside any DB transaction; a short transaction wraps only persist +\n * delivery record. On an exchange failure the delivery is record-and-dropped\n * (204) — the browser may still win, or the code expired. Never logs the raw code.\n */\nexport async function handleSentryInstallationCreated(\n params: HandleSentryInstallationCreatedParams,\n): Promise<void> {\n const existing = await params.getSentryInstallation({installationUuid: params.installationUuid});\n if (existing) {\n logger().debug(\n {deliveryId: params.deliveryId, installationUuid: params.installationUuid},\n 'sentry webhook: installation.created for an existing row, reconciling',\n );\n await params.recordDelivery(params.deliveryId);\n return;\n }\n\n if (!params.code) {\n logger().warn(\n {deliveryId: params.deliveryId, installationUuid: params.installationUuid},\n 'sentry webhook: installation.created without an authorization code, dropping',\n );\n await params.recordDelivery(params.deliveryId);\n return;\n }\n\n try {\n await verifyAndPersistUnclaimedInstallation({\n sentry: params.sentry,\n installationUuid: params.installationUuid,\n code: params.code,\n orgSlug: params.orgSlug,\n verifyInstall: params.verifyInstall,\n persistVerifiedUnclaimedInstallation: ({installationUuid, orgSlug, codeHash}) =>\n params.persistUnclaimedAndRecordDelivery({\n installationUuid,\n orgSlug,\n codeHash,\n deliveryId: params.deliveryId,\n }),\n });\n } catch (error) {\n if (error instanceof SentryIntegrationProviderError) {\n // The Sentry exchange (or org-slug lookup) failed, so we never durably\n // spent the code on a row we own. `reason` distinguishes a transient/expected\n // drop (access-denied: the code was already spent, e.g. the browser won) from\n // a likely misconfiguration (a bad client id/secret/slug fails every install)\n // so log-based alerting can fire on the sustained case. Never logs the raw code.\n logger().warn(\n {\n deliveryId: params.deliveryId,\n installationUuid: params.installationUuid,\n reason: error.reason,\n err: error,\n },\n 'sentry webhook: installation.created exchange failed, dropping',\n );\n } else {\n // The exchange succeeded (the single-use code is now spent) but the persist\n // transaction failed, so no row exists and Sentry will not usefully\n // re-deliver — the install is stranded until a reinstall mints a fresh uuid.\n // Log at error so alerting catches it; still record-and-drop because retrying\n // a spent code cannot recover the row. Never logs the raw code.\n logger().error(\n {deliveryId: params.deliveryId, installationUuid: params.installationUuid, err: error},\n 'sentry webhook: installation.created persisted nothing after a successful exchange, install stranded until reinstall',\n );\n }\n await params.recordDelivery(params.deliveryId);\n }\n}\n\nexport interface HandleSentryInstallationDeletedParams {\n tx: IntegrationTx;\n deliveryId: string;\n installationUuid: string;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n updateConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n}\n\n// Tombstones the install and disables its connection if one exists. An unknown\n// uuid (never installed, or reinstall mints a fresh uuid) only records the\n// delivery: there is no tombstone row to write. Runs entirely in the caller's\n// transaction — no exchange is needed.\nexport async function handleSentryInstallationDeleted(\n params: HandleSentryInstallationDeletedParams,\n): Promise<void> {\n const installation = await markSentryInstallationDeleted(\n {installationUuid: params.installationUuid},\n {tx: params.tx},\n );\n if (installation?.connectionId) {\n await params.updateConnectionLifecycleStatus(\n {id: installation.connectionId, lifecycleStatus: 'disabled'},\n {tx: params.tx},\n );\n }\n\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: SENTRY_SOURCE,\n deliveryId: params.deliveryId,\n });\n}\n\n// A raw Sentry `ignored` action is normalized to `archived` before validation,\n// so legacy ignore events still fire `issue.archived` workflows.\nexport function normalizeSentryIssueAction(parsedJson: unknown): unknown {\n if (typeof parsedJson !== 'object' || parsedJson === null) return parsedJson;\n const obj = parsedJson as {action?: unknown};\n if (obj.action === 'ignored') {\n return {...obj, action: 'archived'};\n }\n return parsedJson;\n}\n\nfunction normalizeIssuePayload(payload: SentryIssueWebhookDto): SentryIssuePayload {\n const issue = payload.data.issue;\n return {\n action: payload.action,\n issueId: issue.id,\n shortId: issue.shortId ?? null,\n title: issue.title ?? DEFAULT_ISSUE_TITLE,\n culprit: issue.culprit ?? null,\n level: issue.level ?? null,\n status: issue.status ?? null,\n platform: issue.platform ?? null,\n webUrl: issue.web_url ?? null,\n issueUrl: issue.url ?? null,\n projectUrl: issue.project_url ?? null,\n firstSeenAt: issue.firstSeen ?? null,\n lastSeenAt: issue.lastSeen ?? null,\n };\n}\n"],"names":["logger","SentryConnectionNotFoundError","SentryInstallationDeletedError","SentryInstallationNotFoundError","SentryInstallationUnclaimedError","SentryIntegrationProviderError","verifyAndPersistUnclaimedInstallation","getSentryInstallationByInstallationUuid","markSentryInstallationDeleted","SENTRY_SOURCE","DEFAULT_ISSUE_TITLE","DELETED_STATUS","handleSentryIssueEvent","params","installationUuid","payload","installation","uuid","tx","status","connectionId","connection","getIntegrationConnectionById","publishIntegrationEventReceived","event","provider","source","slug","action","workspaceId","id","connectionName","displayName","deliveryId","receivedAt","Date","toISOString","normalizeIssuePayload","handleSentryInstallationCreated","existing","getSentryInstallation","debug","recordDelivery","code","warn","sentry","orgSlug","verifyInstall","persistVerifiedUnclaimedInstallation","codeHash","persistUnclaimedAndRecordDelivery","error","reason","err","handleSentryInstallationDeleted","updateConnectionLifecycleStatus","lifecycleStatus","recordDeliveryOnly","normalizeSentryIssueAction","parsedJson","obj","issue","data","issueId","shortId","title","culprit","level","platform","webUrl","web_url","issueUrl","url","projectUrl","project_url","firstSeenAt","firstSeen","lastSeenAt","lastSeen"],"mappings":"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"}
|
package/dist/db/db.d.ts
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { type NodePgDatabase } from '@shipfox/node-drizzle';
|
|
2
|
+
export declare const schema: {
|
|
3
|
+
sentryInstallations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
4
|
+
name: "installations";
|
|
5
|
+
schema: undefined;
|
|
6
|
+
columns: {
|
|
7
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
8
|
+
name: "id";
|
|
9
|
+
tableName: "installations";
|
|
10
|
+
dataType: "string";
|
|
11
|
+
columnType: "PgUUID";
|
|
12
|
+
data: string;
|
|
13
|
+
driverParam: string;
|
|
14
|
+
notNull: true;
|
|
15
|
+
hasDefault: true;
|
|
16
|
+
isPrimaryKey: true;
|
|
17
|
+
isAutoincrement: false;
|
|
18
|
+
hasRuntimeDefault: false;
|
|
19
|
+
enumValues: undefined;
|
|
20
|
+
baseColumn: never;
|
|
21
|
+
identity: undefined;
|
|
22
|
+
generated: undefined;
|
|
23
|
+
}, {}, {}>;
|
|
24
|
+
connectionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
25
|
+
name: "connection_id";
|
|
26
|
+
tableName: "installations";
|
|
27
|
+
dataType: "string";
|
|
28
|
+
columnType: "PgUUID";
|
|
29
|
+
data: string;
|
|
30
|
+
driverParam: string;
|
|
31
|
+
notNull: false;
|
|
32
|
+
hasDefault: false;
|
|
33
|
+
isPrimaryKey: false;
|
|
34
|
+
isAutoincrement: false;
|
|
35
|
+
hasRuntimeDefault: false;
|
|
36
|
+
enumValues: undefined;
|
|
37
|
+
baseColumn: never;
|
|
38
|
+
identity: undefined;
|
|
39
|
+
generated: undefined;
|
|
40
|
+
}, {}, {}>;
|
|
41
|
+
installationUuid: import("drizzle-orm/pg-core").PgColumn<{
|
|
42
|
+
name: "installation_uuid";
|
|
43
|
+
tableName: "installations";
|
|
44
|
+
dataType: "string";
|
|
45
|
+
columnType: "PgText";
|
|
46
|
+
data: string;
|
|
47
|
+
driverParam: string;
|
|
48
|
+
notNull: true;
|
|
49
|
+
hasDefault: false;
|
|
50
|
+
isPrimaryKey: false;
|
|
51
|
+
isAutoincrement: false;
|
|
52
|
+
hasRuntimeDefault: false;
|
|
53
|
+
enumValues: [string, ...string[]];
|
|
54
|
+
baseColumn: never;
|
|
55
|
+
identity: undefined;
|
|
56
|
+
generated: undefined;
|
|
57
|
+
}, {}, {}>;
|
|
58
|
+
orgSlug: import("drizzle-orm/pg-core").PgColumn<{
|
|
59
|
+
name: "org_slug";
|
|
60
|
+
tableName: "installations";
|
|
61
|
+
dataType: "string";
|
|
62
|
+
columnType: "PgText";
|
|
63
|
+
data: string;
|
|
64
|
+
driverParam: string;
|
|
65
|
+
notNull: true;
|
|
66
|
+
hasDefault: false;
|
|
67
|
+
isPrimaryKey: false;
|
|
68
|
+
isAutoincrement: false;
|
|
69
|
+
hasRuntimeDefault: false;
|
|
70
|
+
enumValues: [string, ...string[]];
|
|
71
|
+
baseColumn: never;
|
|
72
|
+
identity: undefined;
|
|
73
|
+
generated: undefined;
|
|
74
|
+
}, {}, {}>;
|
|
75
|
+
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
76
|
+
name: "status";
|
|
77
|
+
tableName: "installations";
|
|
78
|
+
dataType: "string";
|
|
79
|
+
columnType: "PgText";
|
|
80
|
+
data: string;
|
|
81
|
+
driverParam: string;
|
|
82
|
+
notNull: true;
|
|
83
|
+
hasDefault: false;
|
|
84
|
+
isPrimaryKey: false;
|
|
85
|
+
isAutoincrement: false;
|
|
86
|
+
hasRuntimeDefault: false;
|
|
87
|
+
enumValues: [string, ...string[]];
|
|
88
|
+
baseColumn: never;
|
|
89
|
+
identity: undefined;
|
|
90
|
+
generated: undefined;
|
|
91
|
+
}, {}, {}>;
|
|
92
|
+
codeHash: import("drizzle-orm/pg-core").PgColumn<{
|
|
93
|
+
name: "code_hash";
|
|
94
|
+
tableName: "installations";
|
|
95
|
+
dataType: "string";
|
|
96
|
+
columnType: "PgText";
|
|
97
|
+
data: string;
|
|
98
|
+
driverParam: string;
|
|
99
|
+
notNull: false;
|
|
100
|
+
hasDefault: false;
|
|
101
|
+
isPrimaryKey: false;
|
|
102
|
+
isAutoincrement: false;
|
|
103
|
+
hasRuntimeDefault: false;
|
|
104
|
+
enumValues: [string, ...string[]];
|
|
105
|
+
baseColumn: never;
|
|
106
|
+
identity: undefined;
|
|
107
|
+
generated: undefined;
|
|
108
|
+
}, {}, {}>;
|
|
109
|
+
installerUserId: import("drizzle-orm/pg-core").PgColumn<{
|
|
110
|
+
name: "installer_user_id";
|
|
111
|
+
tableName: "installations";
|
|
112
|
+
dataType: "string";
|
|
113
|
+
columnType: "PgUUID";
|
|
114
|
+
data: string;
|
|
115
|
+
driverParam: string;
|
|
116
|
+
notNull: false;
|
|
117
|
+
hasDefault: false;
|
|
118
|
+
isPrimaryKey: false;
|
|
119
|
+
isAutoincrement: false;
|
|
120
|
+
hasRuntimeDefault: false;
|
|
121
|
+
enumValues: undefined;
|
|
122
|
+
baseColumn: never;
|
|
123
|
+
identity: undefined;
|
|
124
|
+
generated: undefined;
|
|
125
|
+
}, {}, {}>;
|
|
126
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
127
|
+
name: "created_at";
|
|
128
|
+
tableName: "installations";
|
|
129
|
+
dataType: "date";
|
|
130
|
+
columnType: "PgTimestamp";
|
|
131
|
+
data: Date;
|
|
132
|
+
driverParam: string;
|
|
133
|
+
notNull: true;
|
|
134
|
+
hasDefault: true;
|
|
135
|
+
isPrimaryKey: false;
|
|
136
|
+
isAutoincrement: false;
|
|
137
|
+
hasRuntimeDefault: false;
|
|
138
|
+
enumValues: undefined;
|
|
139
|
+
baseColumn: never;
|
|
140
|
+
identity: undefined;
|
|
141
|
+
generated: undefined;
|
|
142
|
+
}, {}, {}>;
|
|
143
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
144
|
+
name: "updated_at";
|
|
145
|
+
tableName: "installations";
|
|
146
|
+
dataType: "date";
|
|
147
|
+
columnType: "PgTimestamp";
|
|
148
|
+
data: Date;
|
|
149
|
+
driverParam: string;
|
|
150
|
+
notNull: true;
|
|
151
|
+
hasDefault: true;
|
|
152
|
+
isPrimaryKey: false;
|
|
153
|
+
isAutoincrement: false;
|
|
154
|
+
hasRuntimeDefault: false;
|
|
155
|
+
enumValues: undefined;
|
|
156
|
+
baseColumn: never;
|
|
157
|
+
identity: undefined;
|
|
158
|
+
generated: undefined;
|
|
159
|
+
}, {}, {}>;
|
|
160
|
+
};
|
|
161
|
+
dialect: "pg";
|
|
162
|
+
}>;
|
|
163
|
+
};
|
|
164
|
+
export declare function db(): NodePgDatabase<{
|
|
165
|
+
sentryInstallations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
166
|
+
name: "installations";
|
|
167
|
+
schema: undefined;
|
|
168
|
+
columns: {
|
|
169
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
170
|
+
name: "id";
|
|
171
|
+
tableName: "installations";
|
|
172
|
+
dataType: "string";
|
|
173
|
+
columnType: "PgUUID";
|
|
174
|
+
data: string;
|
|
175
|
+
driverParam: string;
|
|
176
|
+
notNull: true;
|
|
177
|
+
hasDefault: true;
|
|
178
|
+
isPrimaryKey: true;
|
|
179
|
+
isAutoincrement: false;
|
|
180
|
+
hasRuntimeDefault: false;
|
|
181
|
+
enumValues: undefined;
|
|
182
|
+
baseColumn: never;
|
|
183
|
+
identity: undefined;
|
|
184
|
+
generated: undefined;
|
|
185
|
+
}, {}, {}>;
|
|
186
|
+
connectionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
187
|
+
name: "connection_id";
|
|
188
|
+
tableName: "installations";
|
|
189
|
+
dataType: "string";
|
|
190
|
+
columnType: "PgUUID";
|
|
191
|
+
data: string;
|
|
192
|
+
driverParam: string;
|
|
193
|
+
notNull: false;
|
|
194
|
+
hasDefault: false;
|
|
195
|
+
isPrimaryKey: false;
|
|
196
|
+
isAutoincrement: false;
|
|
197
|
+
hasRuntimeDefault: false;
|
|
198
|
+
enumValues: undefined;
|
|
199
|
+
baseColumn: never;
|
|
200
|
+
identity: undefined;
|
|
201
|
+
generated: undefined;
|
|
202
|
+
}, {}, {}>;
|
|
203
|
+
installationUuid: import("drizzle-orm/pg-core").PgColumn<{
|
|
204
|
+
name: "installation_uuid";
|
|
205
|
+
tableName: "installations";
|
|
206
|
+
dataType: "string";
|
|
207
|
+
columnType: "PgText";
|
|
208
|
+
data: string;
|
|
209
|
+
driverParam: string;
|
|
210
|
+
notNull: true;
|
|
211
|
+
hasDefault: false;
|
|
212
|
+
isPrimaryKey: false;
|
|
213
|
+
isAutoincrement: false;
|
|
214
|
+
hasRuntimeDefault: false;
|
|
215
|
+
enumValues: [string, ...string[]];
|
|
216
|
+
baseColumn: never;
|
|
217
|
+
identity: undefined;
|
|
218
|
+
generated: undefined;
|
|
219
|
+
}, {}, {}>;
|
|
220
|
+
orgSlug: import("drizzle-orm/pg-core").PgColumn<{
|
|
221
|
+
name: "org_slug";
|
|
222
|
+
tableName: "installations";
|
|
223
|
+
dataType: "string";
|
|
224
|
+
columnType: "PgText";
|
|
225
|
+
data: string;
|
|
226
|
+
driverParam: string;
|
|
227
|
+
notNull: true;
|
|
228
|
+
hasDefault: false;
|
|
229
|
+
isPrimaryKey: false;
|
|
230
|
+
isAutoincrement: false;
|
|
231
|
+
hasRuntimeDefault: false;
|
|
232
|
+
enumValues: [string, ...string[]];
|
|
233
|
+
baseColumn: never;
|
|
234
|
+
identity: undefined;
|
|
235
|
+
generated: undefined;
|
|
236
|
+
}, {}, {}>;
|
|
237
|
+
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
238
|
+
name: "status";
|
|
239
|
+
tableName: "installations";
|
|
240
|
+
dataType: "string";
|
|
241
|
+
columnType: "PgText";
|
|
242
|
+
data: string;
|
|
243
|
+
driverParam: string;
|
|
244
|
+
notNull: true;
|
|
245
|
+
hasDefault: false;
|
|
246
|
+
isPrimaryKey: false;
|
|
247
|
+
isAutoincrement: false;
|
|
248
|
+
hasRuntimeDefault: false;
|
|
249
|
+
enumValues: [string, ...string[]];
|
|
250
|
+
baseColumn: never;
|
|
251
|
+
identity: undefined;
|
|
252
|
+
generated: undefined;
|
|
253
|
+
}, {}, {}>;
|
|
254
|
+
codeHash: import("drizzle-orm/pg-core").PgColumn<{
|
|
255
|
+
name: "code_hash";
|
|
256
|
+
tableName: "installations";
|
|
257
|
+
dataType: "string";
|
|
258
|
+
columnType: "PgText";
|
|
259
|
+
data: string;
|
|
260
|
+
driverParam: string;
|
|
261
|
+
notNull: false;
|
|
262
|
+
hasDefault: false;
|
|
263
|
+
isPrimaryKey: false;
|
|
264
|
+
isAutoincrement: false;
|
|
265
|
+
hasRuntimeDefault: false;
|
|
266
|
+
enumValues: [string, ...string[]];
|
|
267
|
+
baseColumn: never;
|
|
268
|
+
identity: undefined;
|
|
269
|
+
generated: undefined;
|
|
270
|
+
}, {}, {}>;
|
|
271
|
+
installerUserId: import("drizzle-orm/pg-core").PgColumn<{
|
|
272
|
+
name: "installer_user_id";
|
|
273
|
+
tableName: "installations";
|
|
274
|
+
dataType: "string";
|
|
275
|
+
columnType: "PgUUID";
|
|
276
|
+
data: string;
|
|
277
|
+
driverParam: string;
|
|
278
|
+
notNull: false;
|
|
279
|
+
hasDefault: false;
|
|
280
|
+
isPrimaryKey: false;
|
|
281
|
+
isAutoincrement: false;
|
|
282
|
+
hasRuntimeDefault: false;
|
|
283
|
+
enumValues: undefined;
|
|
284
|
+
baseColumn: never;
|
|
285
|
+
identity: undefined;
|
|
286
|
+
generated: undefined;
|
|
287
|
+
}, {}, {}>;
|
|
288
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
289
|
+
name: "created_at";
|
|
290
|
+
tableName: "installations";
|
|
291
|
+
dataType: "date";
|
|
292
|
+
columnType: "PgTimestamp";
|
|
293
|
+
data: Date;
|
|
294
|
+
driverParam: string;
|
|
295
|
+
notNull: true;
|
|
296
|
+
hasDefault: true;
|
|
297
|
+
isPrimaryKey: false;
|
|
298
|
+
isAutoincrement: false;
|
|
299
|
+
hasRuntimeDefault: false;
|
|
300
|
+
enumValues: undefined;
|
|
301
|
+
baseColumn: never;
|
|
302
|
+
identity: undefined;
|
|
303
|
+
generated: undefined;
|
|
304
|
+
}, {}, {}>;
|
|
305
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
306
|
+
name: "updated_at";
|
|
307
|
+
tableName: "installations";
|
|
308
|
+
dataType: "date";
|
|
309
|
+
columnType: "PgTimestamp";
|
|
310
|
+
data: Date;
|
|
311
|
+
driverParam: string;
|
|
312
|
+
notNull: true;
|
|
313
|
+
hasDefault: true;
|
|
314
|
+
isPrimaryKey: false;
|
|
315
|
+
isAutoincrement: false;
|
|
316
|
+
hasRuntimeDefault: false;
|
|
317
|
+
enumValues: undefined;
|
|
318
|
+
baseColumn: never;
|
|
319
|
+
identity: undefined;
|
|
320
|
+
generated: undefined;
|
|
321
|
+
}, {}, {}>;
|
|
322
|
+
};
|
|
323
|
+
dialect: "pg";
|
|
324
|
+
}>;
|
|
325
|
+
}>;
|
|
326
|
+
export declare function closeDb(): void;
|
|
327
|
+
//# sourceMappingURL=db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAInE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAElB,CAAC;AAIF,wBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjB;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B"}
|
package/dist/db/db.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { drizzle } from '@shipfox/node-drizzle';
|
|
2
|
+
import { pgClient } from '@shipfox/node-postgres';
|
|
3
|
+
import { sentryInstallations } from './schema/installations.js';
|
|
4
|
+
export const schema = {
|
|
5
|
+
sentryInstallations
|
|
6
|
+
};
|
|
7
|
+
let _db;
|
|
8
|
+
export function db() {
|
|
9
|
+
if (!_db) _db = drizzle(pgClient(), {
|
|
10
|
+
schema
|
|
11
|
+
});
|
|
12
|
+
return _db;
|
|
13
|
+
}
|
|
14
|
+
export function closeDb() {
|
|
15
|
+
_db = undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/db/db.ts"],"sourcesContent":["import {drizzle, type NodePgDatabase} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {sentryInstallations} from './schema/installations.js';\n\nexport const schema = {\n sentryInstallations,\n};\n\nlet _db: NodePgDatabase<typeof schema> | undefined;\n\nexport function db() {\n if (!_db) _db = drizzle(pgClient(), {schema});\n return _db;\n}\n\nexport function closeDb(): void {\n _db = undefined;\n}\n"],"names":["drizzle","pgClient","sentryInstallations","schema","_db","db","closeDb","undefined"],"mappings":"AAAA,SAAQA,OAAO,QAA4B,wBAAwB;AACnE,SAAQC,QAAQ,QAAO,yBAAyB;AAChD,SAAQC,mBAAmB,QAAO,4BAA4B;AAE9D,OAAO,MAAMC,SAAS;IACpBD;AACF,EAAE;AAEF,IAAIE;AAEJ,OAAO,SAASC;IACd,IAAI,CAACD,KAAKA,MAAMJ,QAAQC,YAAY;QAACE;IAAM;IAC3C,OAAOC;AACT;AAEA,OAAO,SAASE;IACdF,MAAMG;AACR"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type SentryInstallationStatus = 'installed' | 'deleted';
|
|
2
|
+
export interface SentryInstallation {
|
|
3
|
+
id: string;
|
|
4
|
+
connectionId: string | null;
|
|
5
|
+
installationUuid: string;
|
|
6
|
+
orgSlug: string;
|
|
7
|
+
status: string;
|
|
8
|
+
codeHash: string | null;
|
|
9
|
+
installerUserId: string | null;
|
|
10
|
+
createdAt: Date;
|
|
11
|
+
updatedAt: Date;
|
|
12
|
+
}
|
|
13
|
+
export interface UpsertSentryInstallationParams {
|
|
14
|
+
connectionId: string;
|
|
15
|
+
installationUuid: string;
|
|
16
|
+
orgSlug: string;
|
|
17
|
+
status: SentryInstallationStatus;
|
|
18
|
+
codeHash?: string | null | undefined;
|
|
19
|
+
installerUserId?: string | null | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface PersistVerifiedUnclaimedInstallationParams {
|
|
22
|
+
installationUuid: string;
|
|
23
|
+
orgSlug: string;
|
|
24
|
+
codeHash: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function upsertSentryInstallation(params: UpsertSentryInstallationParams, options?: {
|
|
27
|
+
tx?: unknown;
|
|
28
|
+
}): Promise<SentryInstallation>;
|
|
29
|
+
/**
|
|
30
|
+
* Inserts the verified-but-unclaimed install half (`connection_id = NULL`,
|
|
31
|
+
* `status = 'installed'`) after a successful code exchange. Idempotent on the
|
|
32
|
+
* installation uuid: a conflicting row refreshes only `code_hash`/`org_slug`/
|
|
33
|
+
* `updated_at`, so a webhook that lands after a claim never clobbers the
|
|
34
|
+
* `connection_id` it set nor downgrades a `deleted` tombstone back to installed.
|
|
35
|
+
*/
|
|
36
|
+
export declare function persistVerifiedUnclaimedInstallation(params: PersistVerifiedUnclaimedInstallationParams, options?: {
|
|
37
|
+
tx?: unknown;
|
|
38
|
+
}): Promise<SentryInstallation>;
|
|
39
|
+
/**
|
|
40
|
+
* Verified installs no user has claimed yet (`connection_id IS NULL`,
|
|
41
|
+
* `status='installed'`). Tombstoned rows are excluded so the TTL cron stays
|
|
42
|
+
* idempotent and the unclaimed metric stays accurate. Pass `olderThan` to scope
|
|
43
|
+
* to stale rows for the cron.
|
|
44
|
+
*/
|
|
45
|
+
export declare function listUnclaimedSentryInstallations(params?: {
|
|
46
|
+
olderThan?: Date;
|
|
47
|
+
}, options?: {
|
|
48
|
+
tx?: unknown;
|
|
49
|
+
}): Promise<SentryInstallation[]>;
|
|
50
|
+
export declare function getSentryInstallationByInstallationUuid(installationUuid: string, options?: {
|
|
51
|
+
tx?: unknown;
|
|
52
|
+
}): Promise<SentryInstallation | undefined>;
|
|
53
|
+
export declare function getSentryInstallationByConnectionId(connectionId: string, options?: {
|
|
54
|
+
tx?: unknown;
|
|
55
|
+
}): Promise<SentryInstallation | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* Tombstones verified-unclaimed installs older than `olderThan` (the TTL cleanup
|
|
58
|
+
* cron). Bounds a never-claimed install rather than leaving it pending forever.
|
|
59
|
+
* Returns how many rows were tombstoned. We hold no Sentry token for these rows
|
|
60
|
+
* (tokens are never persisted), so the Sentry-side uninstall is out of scope here.
|
|
61
|
+
*/
|
|
62
|
+
export declare function pruneUnclaimedSentryInstallations(params: {
|
|
63
|
+
olderThan: Date;
|
|
64
|
+
}, options?: {
|
|
65
|
+
tx?: unknown;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
tombstoned: number;
|
|
68
|
+
}>;
|
|
69
|
+
export declare function markSentryInstallationDeleted(params: {
|
|
70
|
+
installationUuid: string;
|
|
71
|
+
}, options?: {
|
|
72
|
+
tx?: unknown;
|
|
73
|
+
}): Promise<SentryInstallation | undefined>;
|
|
74
|
+
//# sourceMappingURL=installations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,wBAAwB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,0CAA0C;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,EACtC,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqC7B;AAED;;;;;;GAMG;AACH,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,0CAA0C,EAClD,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAwB7B;AAED;;;;;GAKG;AACH,wBAAsB,gCAAgC,CACpD,MAAM,GAAE;IAAC,SAAS,CAAC,EAAE,IAAI,CAAA;CAAM,EAC/B,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAc/B;AAED,wBAAsB,uCAAuC,CAC3D,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAUzC;AAED,wBAAsB,mCAAmC,CACvD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAUzC;AAED;;;;;GAKG;AACH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,EACzB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CAAC,CAa/B;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAC,EAClC,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CASzC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { and, eq, isNull, lt, sql } from 'drizzle-orm';
|
|
2
|
+
import { SentryInstallationAlreadyLinkedError } from '#core/errors.js';
|
|
3
|
+
import { db } from './db.js';
|
|
4
|
+
import { sentryInstallations, toSentryInstallation } from './schema/installations.js';
|
|
5
|
+
export async function upsertSentryInstallation(params, options = {}) {
|
|
6
|
+
const executor = options.tx ?? db();
|
|
7
|
+
const now = new Date();
|
|
8
|
+
const [row] = await executor.insert(sentryInstallations).values({
|
|
9
|
+
connectionId: params.connectionId,
|
|
10
|
+
installationUuid: params.installationUuid,
|
|
11
|
+
orgSlug: params.orgSlug,
|
|
12
|
+
status: params.status,
|
|
13
|
+
codeHash: params.codeHash ?? null,
|
|
14
|
+
installerUserId: params.installerUserId ?? null
|
|
15
|
+
}).onConflictDoUpdate({
|
|
16
|
+
target: sentryInstallations.installationUuid,
|
|
17
|
+
// TOCTOU guard: allow the update only when this installation is unclaimed
|
|
18
|
+
// (connection_id IS NULL, the first claim of a verified-unclaimed row) or
|
|
19
|
+
// already owned by this same connection (idempotent reconnect). A concurrent
|
|
20
|
+
// claim of the same installation to a different workspace commits its own
|
|
21
|
+
// connection_id first; this losing transaction then sees a different non-null
|
|
22
|
+
// connection_id, the predicate is false, Postgres updates nothing, and the
|
|
23
|
+
// empty RETURNING below rolls it back instead of silently repointing the
|
|
24
|
+
// installation (cross-tenant event misroute).
|
|
25
|
+
setWhere: sql`${sentryInstallations.connectionId} is null or ${sentryInstallations.connectionId} = ${params.connectionId}`,
|
|
26
|
+
set: {
|
|
27
|
+
connectionId: params.connectionId,
|
|
28
|
+
orgSlug: params.orgSlug,
|
|
29
|
+
status: params.status,
|
|
30
|
+
codeHash: params.codeHash ?? null,
|
|
31
|
+
installerUserId: params.installerUserId ?? null,
|
|
32
|
+
updatedAt: now
|
|
33
|
+
}
|
|
34
|
+
}).returning();
|
|
35
|
+
if (!row) throw new SentryInstallationAlreadyLinkedError(params.installationUuid);
|
|
36
|
+
return toSentryInstallation(row);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Inserts the verified-but-unclaimed install half (`connection_id = NULL`,
|
|
40
|
+
* `status = 'installed'`) after a successful code exchange. Idempotent on the
|
|
41
|
+
* installation uuid: a conflicting row refreshes only `code_hash`/`org_slug`/
|
|
42
|
+
* `updated_at`, so a webhook that lands after a claim never clobbers the
|
|
43
|
+
* `connection_id` it set nor downgrades a `deleted` tombstone back to installed.
|
|
44
|
+
*/ export async function persistVerifiedUnclaimedInstallation(params, options = {}) {
|
|
45
|
+
const executor = options.tx ?? db();
|
|
46
|
+
const now = new Date();
|
|
47
|
+
const [row] = await executor.insert(sentryInstallations).values({
|
|
48
|
+
connectionId: null,
|
|
49
|
+
installationUuid: params.installationUuid,
|
|
50
|
+
orgSlug: params.orgSlug,
|
|
51
|
+
status: 'installed',
|
|
52
|
+
codeHash: params.codeHash
|
|
53
|
+
}).onConflictDoUpdate({
|
|
54
|
+
target: sentryInstallations.installationUuid,
|
|
55
|
+
set: {
|
|
56
|
+
orgSlug: params.orgSlug,
|
|
57
|
+
codeHash: params.codeHash,
|
|
58
|
+
updatedAt: now
|
|
59
|
+
}
|
|
60
|
+
}).returning();
|
|
61
|
+
if (!row) throw new Error('Sentry installation persist returned no rows');
|
|
62
|
+
return toSentryInstallation(row);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Verified installs no user has claimed yet (`connection_id IS NULL`,
|
|
66
|
+
* `status='installed'`). Tombstoned rows are excluded so the TTL cron stays
|
|
67
|
+
* idempotent and the unclaimed metric stays accurate. Pass `olderThan` to scope
|
|
68
|
+
* to stale rows for the cron.
|
|
69
|
+
*/ export async function listUnclaimedSentryInstallations(params = {}, options = {}) {
|
|
70
|
+
const executor = options.tx ?? db();
|
|
71
|
+
const conditions = [
|
|
72
|
+
isNull(sentryInstallations.connectionId),
|
|
73
|
+
eq(sentryInstallations.status, 'installed')
|
|
74
|
+
];
|
|
75
|
+
if (params.olderThan) {
|
|
76
|
+
conditions.push(lt(sentryInstallations.createdAt, params.olderThan));
|
|
77
|
+
}
|
|
78
|
+
const rows = await executor.select().from(sentryInstallations).where(and(...conditions));
|
|
79
|
+
return rows.map(toSentryInstallation);
|
|
80
|
+
}
|
|
81
|
+
export async function getSentryInstallationByInstallationUuid(installationUuid, options = {}) {
|
|
82
|
+
const executor = options.tx ?? db();
|
|
83
|
+
const rows = await executor.select().from(sentryInstallations).where(eq(sentryInstallations.installationUuid, installationUuid)).limit(1);
|
|
84
|
+
const row = rows[0];
|
|
85
|
+
if (!row) return undefined;
|
|
86
|
+
return toSentryInstallation(row);
|
|
87
|
+
}
|
|
88
|
+
export async function getSentryInstallationByConnectionId(connectionId, options = {}) {
|
|
89
|
+
const executor = options.tx ?? db();
|
|
90
|
+
const rows = await executor.select().from(sentryInstallations).where(eq(sentryInstallations.connectionId, connectionId)).limit(1);
|
|
91
|
+
const row = rows[0];
|
|
92
|
+
if (!row) return undefined;
|
|
93
|
+
return toSentryInstallation(row);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Tombstones verified-unclaimed installs older than `olderThan` (the TTL cleanup
|
|
97
|
+
* cron). Bounds a never-claimed install rather than leaving it pending forever.
|
|
98
|
+
* Returns how many rows were tombstoned. We hold no Sentry token for these rows
|
|
99
|
+
* (tokens are never persisted), so the Sentry-side uninstall is out of scope here.
|
|
100
|
+
*/ export async function pruneUnclaimedSentryInstallations(params, options = {}) {
|
|
101
|
+
const executor = options.tx ?? db();
|
|
102
|
+
const result = await executor.update(sentryInstallations).set({
|
|
103
|
+
status: 'deleted',
|
|
104
|
+
updatedAt: new Date()
|
|
105
|
+
}).where(and(isNull(sentryInstallations.connectionId), eq(sentryInstallations.status, 'installed'), lt(sentryInstallations.createdAt, params.olderThan)));
|
|
106
|
+
return {
|
|
107
|
+
tombstoned: result.rowCount ?? 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export async function markSentryInstallationDeleted(params, options = {}) {
|
|
111
|
+
const executor = options.tx ?? db();
|
|
112
|
+
const [row] = await executor.update(sentryInstallations).set({
|
|
113
|
+
status: 'deleted',
|
|
114
|
+
updatedAt: new Date()
|
|
115
|
+
}).where(eq(sentryInstallations.installationUuid, params.installationUuid)).returning();
|
|
116
|
+
if (!row) return undefined;
|
|
117
|
+
return toSentryInstallation(row);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=installations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {and, eq, isNull, lt, sql} from 'drizzle-orm';\nimport {SentryInstallationAlreadyLinkedError} from '#core/errors.js';\nimport {db} from './db.js';\nimport {sentryInstallations, toSentryInstallation} from './schema/installations.js';\n\nexport type SentryInstallationStatus = 'installed' | 'deleted';\n\nexport interface SentryInstallation {\n id: string;\n connectionId: string | null;\n installationUuid: string;\n orgSlug: string;\n status: string;\n codeHash: string | null;\n installerUserId: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertSentryInstallationParams {\n connectionId: string;\n installationUuid: string;\n orgSlug: string;\n status: SentryInstallationStatus;\n codeHash?: string | null | undefined;\n installerUserId?: string | null | undefined;\n}\n\nexport interface PersistVerifiedUnclaimedInstallationParams {\n installationUuid: string;\n orgSlug: string;\n codeHash: string;\n}\n\ntype SentryDb = ReturnType<typeof db>;\ntype SentryTx = Parameters<Parameters<SentryDb['transaction']>[0]>[0];\n\nexport async function upsertSentryInstallation(\n params: UpsertSentryInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const now = new Date();\n const [row] = await executor\n .insert(sentryInstallations)\n .values({\n connectionId: params.connectionId,\n installationUuid: params.installationUuid,\n orgSlug: params.orgSlug,\n status: params.status,\n codeHash: params.codeHash ?? null,\n installerUserId: params.installerUserId ?? null,\n })\n .onConflictDoUpdate({\n target: sentryInstallations.installationUuid,\n // TOCTOU guard: allow the update only when this installation is unclaimed\n // (connection_id IS NULL, the first claim of a verified-unclaimed row) or\n // already owned by this same connection (idempotent reconnect). A concurrent\n // claim of the same installation to a different workspace commits its own\n // connection_id first; this losing transaction then sees a different non-null\n // connection_id, the predicate is false, Postgres updates nothing, and the\n // empty RETURNING below rolls it back instead of silently repointing the\n // installation (cross-tenant event misroute).\n setWhere: sql`${sentryInstallations.connectionId} is null or ${sentryInstallations.connectionId} = ${params.connectionId}`,\n set: {\n connectionId: params.connectionId,\n orgSlug: params.orgSlug,\n status: params.status,\n codeHash: params.codeHash ?? null,\n installerUserId: params.installerUserId ?? null,\n updatedAt: now,\n },\n })\n .returning();\n\n if (!row) throw new SentryInstallationAlreadyLinkedError(params.installationUuid);\n return toSentryInstallation(row);\n}\n\n/**\n * Inserts the verified-but-unclaimed install half (`connection_id = NULL`,\n * `status = 'installed'`) after a successful code exchange. Idempotent on the\n * installation uuid: a conflicting row refreshes only `code_hash`/`org_slug`/\n * `updated_at`, so a webhook that lands after a claim never clobbers the\n * `connection_id` it set nor downgrades a `deleted` tombstone back to installed.\n */\nexport async function persistVerifiedUnclaimedInstallation(\n params: PersistVerifiedUnclaimedInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const now = new Date();\n const [row] = await executor\n .insert(sentryInstallations)\n .values({\n connectionId: null,\n installationUuid: params.installationUuid,\n orgSlug: params.orgSlug,\n status: 'installed',\n codeHash: params.codeHash,\n })\n .onConflictDoUpdate({\n target: sentryInstallations.installationUuid,\n set: {\n orgSlug: params.orgSlug,\n codeHash: params.codeHash,\n updatedAt: now,\n },\n })\n .returning();\n\n if (!row) throw new Error('Sentry installation persist returned no rows');\n return toSentryInstallation(row);\n}\n\n/**\n * Verified installs no user has claimed yet (`connection_id IS NULL`,\n * `status='installed'`). Tombstoned rows are excluded so the TTL cron stays\n * idempotent and the unclaimed metric stays accurate. Pass `olderThan` to scope\n * to stale rows for the cron.\n */\nexport async function listUnclaimedSentryInstallations(\n params: {olderThan?: Date} = {},\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation[]> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const conditions = [\n isNull(sentryInstallations.connectionId),\n eq(sentryInstallations.status, 'installed'),\n ];\n if (params.olderThan) {\n conditions.push(lt(sentryInstallations.createdAt, params.olderThan));\n }\n const rows = await executor\n .select()\n .from(sentryInstallations)\n .where(and(...conditions));\n return rows.map(toSentryInstallation);\n}\n\nexport async function getSentryInstallationByInstallationUuid(\n installationUuid: string,\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation | undefined> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const rows = await executor\n .select()\n .from(sentryInstallations)\n .where(eq(sentryInstallations.installationUuid, installationUuid))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSentryInstallation(row);\n}\n\nexport async function getSentryInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation | undefined> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const rows = await executor\n .select()\n .from(sentryInstallations)\n .where(eq(sentryInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSentryInstallation(row);\n}\n\n/**\n * Tombstones verified-unclaimed installs older than `olderThan` (the TTL cleanup\n * cron). Bounds a never-claimed install rather than leaving it pending forever.\n * Returns how many rows were tombstoned. We hold no Sentry token for these rows\n * (tokens are never persisted), so the Sentry-side uninstall is out of scope here.\n */\nexport async function pruneUnclaimedSentryInstallations(\n params: {olderThan: Date},\n options: {tx?: unknown} = {},\n): Promise<{tombstoned: number}> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const result = await executor\n .update(sentryInstallations)\n .set({status: 'deleted', updatedAt: new Date()})\n .where(\n and(\n isNull(sentryInstallations.connectionId),\n eq(sentryInstallations.status, 'installed'),\n lt(sentryInstallations.createdAt, params.olderThan),\n ),\n );\n return {tombstoned: result.rowCount ?? 0};\n}\n\nexport async function markSentryInstallationDeleted(\n params: {installationUuid: string},\n options: {tx?: unknown} = {},\n): Promise<SentryInstallation | undefined> {\n const executor = (options.tx ?? db()) as SentryDb | SentryTx;\n const [row] = await executor\n .update(sentryInstallations)\n .set({status: 'deleted', updatedAt: new Date()})\n .where(eq(sentryInstallations.installationUuid, params.installationUuid))\n .returning();\n if (!row) return undefined;\n return toSentryInstallation(row);\n}\n"],"names":["and","eq","isNull","lt","sql","SentryInstallationAlreadyLinkedError","db","sentryInstallations","toSentryInstallation","upsertSentryInstallation","params","options","executor","tx","now","Date","row","insert","values","connectionId","installationUuid","orgSlug","status","codeHash","installerUserId","onConflictDoUpdate","target","setWhere","set","updatedAt","returning","persistVerifiedUnclaimedInstallation","Error","listUnclaimedSentryInstallations","conditions","olderThan","push","createdAt","rows","select","from","where","map","getSentryInstallationByInstallationUuid","limit","undefined","getSentryInstallationByConnectionId","pruneUnclaimedSentryInstallations","result","update","tombstoned","rowCount","markSentryInstallationDeleted"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,QAAoE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/db/migrations.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,OAAO,MAAMC,iBAAiBF,QAAQD,QAAQE,cAAc,YAAYE,GAAG,IAAI,iBAAiB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/db/schema/common.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,oDAA0D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/common.ts"],"sourcesContent":["import {pgTableCreator} from 'drizzle-orm/pg-core';\n\nexport const pgTable = pgTableCreator((name) => `integrations_sentry_${name}`);\n"],"names":["pgTableCreator","pgTable","name"],"mappings":"AAAA,SAAQA,cAAc,QAAO,sBAAsB;AAEnD,OAAO,MAAMC,UAAUD,eAAe,CAACE,OAAS,CAAC,oBAAoB,EAAEA,MAAM,EAAE"}
|