@learncard/network-brain-service 3.17.0 → 3.19.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/.env.example +4 -0
- package/AGENTS.md +177 -159
- package/CHANGELOG.md +148 -0
- package/README.md +26 -8
- package/compose.inbox.yml +7 -0
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/auth-grant/read.d.ts +1 -0
- package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +3 -2
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +5 -3
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.d.ts +2 -1
- package/dist/accesslayer/credential/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.test.d.ts +2 -0
- package/dist/accesslayer/credential/create.test.d.ts.map +1 -0
- package/dist/accesslayer/credential/relationships/read.d.ts +1 -0
- package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
- package/dist/accesslayer/inbox-batch/store.d.ts +33 -0
- package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
- package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
- package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
- package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
- package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
- package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
- package/dist/cache/atomic-counter.test.d.ts +2 -0
- package/dist/cache/atomic-counter.test.d.ts.map +1 -0
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/config/environment.d.ts +46 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/credential-refresh.d.ts.map +1 -1
- package/dist/didkit_wasm_bg.wasm +0 -0
- package/dist/helpers/activity.helpers.d.ts +4 -0
- package/dist/helpers/activity.helpers.d.ts.map +1 -1
- package/dist/helpers/boost-storage.helpers.test.d.ts +2 -0
- package/dist/helpers/boost-storage.helpers.test.d.ts.map +1 -0
- package/dist/helpers/boost.helpers.d.ts +3 -7
- package/dist/helpers/boost.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
- package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
- package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
- package/dist/helpers/credential.helpers.d.ts +4 -2
- package/dist/helpers/credential.helpers.d.ts.map +1 -1
- package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
- package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
- package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
- package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox.helpers.d.ts +9 -1
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/issuedCredentialStatus.helpers.d.ts +5 -0
- package/dist/helpers/issuedCredentialStatus.helpers.d.ts.map +1 -0
- package/dist/helpers/notifications.helpers.d.ts.map +1 -1
- package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
- package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
- package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
- package/dist/helpers/shim.d.ts +1 -1
- package/dist/helpers/shim.d.ts.map +1 -1
- package/dist/helpers/signingAuthority.helpers.d.ts +3 -2
- package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
- package/dist/helpers/signingAuthority.helpers.test.d.ts +2 -0
- package/dist/helpers/signingAuthority.helpers.test.d.ts.map +1 -0
- package/dist/helpers/status-list.helpers.d.ts.map +1 -1
- package/dist/helpers/status-list.helpers.test.d.ts +2 -0
- package/dist/helpers/status-list.helpers.test.d.ts.map +1 -0
- package/dist/inbox-queue-local.d.ts +2 -0
- package/dist/inbox-queue-local.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/models/AuthGrant.d.ts.map +1 -1
- package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
- package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
- package/dist/models/Credential.d.ts +1 -0
- package/dist/models/Credential.d.ts.map +1 -1
- package/dist/models/CredentialActivity.d.ts +1 -0
- package/dist/models/CredentialActivity.d.ts.map +1 -1
- package/dist/models/CredentialRefresh.d.ts +5 -2
- package/dist/models/CredentialRefresh.d.ts.map +1 -1
- package/dist/models/InboxCredential.d.ts +1 -0
- package/dist/models/InboxCredential.d.ts.map +1 -1
- package/dist/models/credential-refresh-constraints.d.ts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/routes/act-as.test.d.ts +2 -0
- package/dist/routes/act-as.test.d.ts.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/auth-grants.d.ts +10 -0
- package/dist/routes/auth-grants.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +506 -12
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +48 -2
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credential-refreshes.d.ts +4 -4
- package/dist/routes/credential-refreshes.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +8 -2
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/did-metadata.d.ts +8 -8
- package/dist/routes/federation.d.ts +2 -2
- package/dist/routes/inbox.d.ts +973 -31
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts +47 -127
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/presentations.d.ts +6 -6
- package/dist/routes/storage.d.ts +63 -63
- package/dist/routes/test.d.ts +6 -0
- package/dist/routes/test.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts +14 -14
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
- package/dist/services/registry/registry.factory.d.ts.map +1 -1
- package/dist/types/activity.d.ts +4 -0
- package/dist/types/activity.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +22 -8
- package/dist/types/consentflowcontract.d.ts.map +1 -1
- package/dist/types/credential-refresh.d.ts +10 -1
- package/dist/types/credential-refresh.d.ts.map +1 -1
- package/dist/types/credential.d.ts +7 -0
- package/dist/types/credential.d.ts.map +1 -1
- package/dist/types/did-metadata.d.ts +2 -2
- package/dist/types/inbox-batch.d.ts +38 -0
- package/dist/types/inbox-batch.d.ts.map +1 -0
- package/inbox-e2e-setup.ts +53 -1
- package/inbox-elasticmq.conf +10 -0
- package/lambda.ts +61 -16
- package/package.json +18 -16
- package/scripts/setup-inbox-queues.ts +60 -0
- package/serverless-local.yml +2 -0
- package/serverless.yml +75 -0
- package/src/accesslayer/app-store-listing/relationships/read.ts +4 -3
- package/src/accesslayer/auth-grant/read.ts +2 -1
- package/src/accesslayer/consentflowcontract/relationships/create.ts +15 -5
- package/src/accesslayer/consentflowcontract/relationships/read.ts +161 -63
- package/src/accesslayer/consentflowcontract/relationships/update.ts +43 -10
- package/src/accesslayer/credential/create.test.ts +141 -0
- package/src/accesslayer/credential/create.ts +27 -3
- package/src/accesslayer/credential-activity/create.ts +1 -0
- package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
- package/src/accesslayer/credential-refresh/index.ts +1 -0
- package/src/accesslayer/credential-refresh/read.ts +8 -2
- package/src/accesslayer/credential-refresh/update.ts +4 -0
- package/src/accesslayer/inbox-batch/store.ts +469 -0
- package/src/accesslayer/inbox-credential/create.ts +51 -19
- package/src/accesslayer/inbox-credential/refresh.ts +221 -0
- package/src/accesslayer/inbox-credential/update.ts +1 -1
- package/src/cache/atomic-counter.test-helpers.ts +27 -0
- package/src/cache/atomic-counter.test.ts +6 -0
- package/src/cache/index.ts +25 -13
- package/src/config/environment.ts +26 -0
- package/src/credential-refresh.ts +5 -1
- package/src/docker-entry.ts +8 -0
- package/src/helpers/activity.helpers.ts +4 -0
- package/src/helpers/app-counter.helpers.ts +1 -1
- package/src/helpers/boost-storage.helpers.test.ts +242 -0
- package/src/helpers/boost.helpers.ts +58 -437
- package/src/helpers/contact-method.helpers.test.ts +43 -0
- package/src/helpers/contact-method.helpers.ts +3 -4
- package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
- package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
- package/src/helpers/credential-refresh-email.helpers.ts +201 -0
- package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
- package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
- package/src/helpers/credential-refresh.helpers.ts +385 -107
- package/src/helpers/credential.helpers.ts +6 -2
- package/src/helpers/finalize-inbox.helpers.ts +39 -22
- package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
- package/src/helpers/inbox-batch-http.helpers.ts +150 -0
- package/src/helpers/inbox-batch-status.helpers.ts +11 -0
- package/src/helpers/inbox-batch.helpers.test.ts +668 -0
- package/src/helpers/inbox-batch.helpers.ts +374 -0
- package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
- package/src/helpers/inbox-queue.helpers.test.ts +253 -0
- package/src/helpers/inbox-queue.helpers.ts +372 -0
- package/src/helpers/inbox-refresh.helpers.ts +432 -0
- package/src/helpers/inbox.helpers.ts +301 -50
- package/src/helpers/issuedCredentialStatus.helpers.ts +11 -0
- package/src/helpers/notifications.helpers.ts +4 -2
- package/src/helpers/rateLimit.helpers.ts +2 -5
- package/src/helpers/refresh-send-intent.helpers.ts +273 -0
- package/src/helpers/shim.ts +32 -31
- package/src/helpers/signingAuthority.helpers.test.ts +198 -0
- package/src/helpers/signingAuthority.helpers.ts +41 -18
- package/src/helpers/status-list.helpers.test.ts +77 -0
- package/src/helpers/status-list.helpers.ts +31 -8
- package/src/inbox-queue-local.ts +28 -0
- package/src/models/AuthGrant.ts +1 -0
- package/src/models/ConsentFlowTerms.ts +4 -0
- package/src/models/ConsentFlowTransaction.ts +4 -0
- package/src/models/Credential.ts +1 -0
- package/src/models/CredentialActivity.ts +3 -5
- package/src/models/CredentialRefresh.ts +4 -2
- package/src/models/InboxCredential.ts +2 -0
- package/src/models/credential-refresh-constraints.test.ts +8 -3
- package/src/models/credential-refresh-constraints.ts +5 -0
- package/src/openapi.ts +8 -0
- package/src/routes/act-as.test.ts +239 -0
- package/src/routes/app-store.ts +69 -52
- package/src/routes/auth-grants.ts +13 -11
- package/src/routes/boosts.ts +943 -120
- package/src/routes/contracts.ts +55 -7
- package/src/routes/credential-refreshes.test.ts +8 -2
- package/src/routes/credential-refreshes.ts +4 -1
- package/src/routes/credentials.ts +2 -1
- package/src/routes/inbox.ts +95 -49
- package/src/routes/index.ts +164 -88
- package/src/routes/skills.ts +1 -1
- package/src/routes/workflows.ts +80 -31
- package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
- package/src/services/delivery/adapters/postmark.adapter.ts +12 -0
- package/src/services/registry/registry.factory.ts +2 -1
- package/src/types/activity.ts +2 -0
- package/src/types/consentflowcontract.ts +21 -3
- package/src/types/credential-refresh.ts +21 -2
- package/src/types/credential.ts +13 -1
- package/src/types/inbox-batch.ts +46 -0
- package/test/app-store-listing.spec.ts +66 -30
- package/test/atomic-counter.e2e.ts +12 -0
- package/test/auth-grant.spec.ts +21 -21
- package/test/bitstring-status-list.spec.ts +12 -9
- package/test/consentflow.spec.ts +453 -26
- package/test/credential-refresh-email.spec.ts +663 -0
- package/test/credential-refresh-publication.spec.ts +13 -5
- package/test/guardian-gated-routes.spec.ts +337 -180
- package/test/helpers/inbox-e2e-environment.ts +6 -0
- package/test/inbox-batch.spec.ts +1338 -0
- package/test/inbox-refresh.spec.ts +534 -0
- package/test/inbox-security.e2e.ts +48 -3
- package/test/openapi.spec.ts +41 -1
- package/test/refresh-send.spec.ts +1799 -0
- package/test/registry.spec.ts +56 -11
- package/test/revoke-suspend-per-instance.spec.ts +9 -1
- package/vitest.config.ts +13 -0
- package/vitest.inbox-e2e.config.ts +5 -1
- package/vitest.integration.config.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-refresh.d.ts","sourceRoot":"","sources":["../../src/types/credential-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"credential-refresh.d.ts","sourceRoot":"","sources":["../../src/types/credential-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,+BAA+B;;;;;EAK1C,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAGrF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkD3C,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAEvF,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;iBAajD,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AASnG,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BhD,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAGjG,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAElD,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAChD,OAAO,uCAAuC,CACjD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;EAOtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;iBAKtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import type { BitstringStatusListEntry, JWE, VC } from '@learncard/types';
|
|
3
|
+
export interface IssuedCredential {
|
|
4
|
+
readonly kind: 'issued-credential';
|
|
5
|
+
readonly credential: VC | JWE;
|
|
6
|
+
readonly statusEntries: BitstringStatusListEntry[];
|
|
7
|
+
}
|
|
2
8
|
export declare const CredentialValidator: z.ZodObject<{
|
|
3
9
|
id: z.ZodString;
|
|
4
10
|
credential: z.ZodString;
|
|
11
|
+
statusEntries: z.ZodOptional<z.ZodString>;
|
|
5
12
|
}, z.core.$strip>;
|
|
6
13
|
export type CredentialType = z.infer<typeof CredentialValidator>;
|
|
7
14
|
//# sourceMappingURL=credential.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/types/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/types/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAG1E,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,GAAG,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,wBAAwB,EAAE,CAAC;CACtD;AAED,eAAO,MAAM,mBAAmB;;;;iBAI9B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -129,7 +129,7 @@ export declare const DidMetadataValidator: z.ZodObject<{
|
|
|
129
129
|
}, z.core.$catchall<z.ZodAny>>>>>;
|
|
130
130
|
proof: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodObject<{
|
|
131
131
|
type: z.ZodString;
|
|
132
|
-
created: z.ZodString
|
|
132
|
+
created: z.ZodOptional<z.ZodString>;
|
|
133
133
|
challenge: z.ZodOptional<z.ZodString>;
|
|
134
134
|
domain: z.ZodOptional<z.ZodString>;
|
|
135
135
|
nonce: z.ZodOptional<z.ZodString>;
|
|
@@ -138,7 +138,7 @@ export declare const DidMetadataValidator: z.ZodObject<{
|
|
|
138
138
|
jws: z.ZodOptional<z.ZodString>;
|
|
139
139
|
}, z.core.$catchall<z.ZodAny>>, z.ZodArray<z.ZodObject<{
|
|
140
140
|
type: z.ZodString;
|
|
141
|
-
created: z.ZodString
|
|
141
|
+
created: z.ZodOptional<z.ZodString>;
|
|
142
142
|
challenge: z.ZodOptional<z.ZodString>;
|
|
143
143
|
domain: z.ZodOptional<z.ZodString>;
|
|
144
144
|
nonce: z.ZodOptional<z.ZodString>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { InboxBatchStatus, IssueInboxCredentialBatch } from '@learncard/types';
|
|
2
|
+
import type { Context } from '.././routes/index';
|
|
3
|
+
export type BatchItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
batchId: string;
|
|
6
|
+
index: number;
|
|
7
|
+
state: InboxBatchStatus['items'][number]['state'];
|
|
8
|
+
phase?: 'PREPARING' | 'ISSUING';
|
|
9
|
+
owner?: string;
|
|
10
|
+
leaseUntil?: number;
|
|
11
|
+
attempts: number;
|
|
12
|
+
result?: string;
|
|
13
|
+
replayKey: string;
|
|
14
|
+
duplicate: boolean;
|
|
15
|
+
correlation?: string;
|
|
16
|
+
};
|
|
17
|
+
export type InboxDispatchLease = {
|
|
18
|
+
id: string;
|
|
19
|
+
dispatchAt: number;
|
|
20
|
+
};
|
|
21
|
+
export type BatchJob = {
|
|
22
|
+
id: string;
|
|
23
|
+
issuer: string;
|
|
24
|
+
createdAt: number;
|
|
25
|
+
payload?: string;
|
|
26
|
+
completedAt?: number;
|
|
27
|
+
};
|
|
28
|
+
export type BatchJobPayload = {
|
|
29
|
+
batch: IssueInboxCredentialBatch;
|
|
30
|
+
context: Pick<Context, 'domain' | 'tenant'>;
|
|
31
|
+
scope?: string;
|
|
32
|
+
};
|
|
33
|
+
export type BatchReplayStore = {
|
|
34
|
+
get: (key: string) => Promise<string | null | undefined>;
|
|
35
|
+
setIfAbsent: (key: string, value: string, ttl: number) => Promise<'OK' | null | undefined>;
|
|
36
|
+
compareAndSet: (key: string, expected: string, value: string | null, ttl: number) => Promise<boolean | undefined>;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=inbox-batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox-batch.d.ts","sourceRoot":"","sources":["../../src/types/inbox-batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,MAAM,SAAS,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IAEnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,QAAQ,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC1B,KAAK,EAAE,yBAAyB,CAAC;IACjC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;IAE5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAC3F,aAAa,EAAE,CACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;CACrC,CAAC"}
|
package/inbox-e2e-setup.ts
CHANGED
|
@@ -1,18 +1,70 @@
|
|
|
1
1
|
import { Neo4jContainer } from '@testcontainers/neo4j';
|
|
2
2
|
import { GenericContainer } from 'testcontainers';
|
|
3
3
|
import type { TestProject } from 'vitest/node';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { SQSClient, CreateQueueCommand, GetQueueAttributesCommand } from '@aws-sdk/client-sqs';
|
|
4
6
|
|
|
5
7
|
/** Random host ports and fresh containers keep these tests away from development data. */
|
|
6
8
|
export default async function setup({ provide }: TestProject): Promise<() => Promise<void>> {
|
|
7
9
|
const neo4j = await new Neo4jContainer('neo4j:5').start();
|
|
8
10
|
try {
|
|
9
11
|
const redis = await new GenericContainer('redis:7-alpine').withExposedPorts(6379).start();
|
|
12
|
+
let sqs: Awaited<ReturnType<GenericContainer['start']>> | undefined;
|
|
13
|
+
try {
|
|
14
|
+
sqs = await new GenericContainer('softwaremill/elasticmq-native:1.6.15')
|
|
15
|
+
.withCopyFilesToContainer([
|
|
16
|
+
{
|
|
17
|
+
source: fileURLToPath(new URL('./inbox-elasticmq.conf', import.meta.url)),
|
|
18
|
+
target: '/opt/elasticmq.conf',
|
|
19
|
+
},
|
|
20
|
+
])
|
|
21
|
+
.withExposedPorts(9324)
|
|
22
|
+
.start();
|
|
23
|
+
const endpoint = `http://${sqs.getHost()}:${sqs.getMappedPort(9324)}`;
|
|
24
|
+
const client = new SQSClient({
|
|
25
|
+
endpoint,
|
|
26
|
+
region: 'us-east-1',
|
|
27
|
+
credentials: { accessKeyId: 'local', secretAccessKey: 'local' },
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
const dlq = await client.send(
|
|
31
|
+
new CreateQueueCommand({ QueueName: 'inbox-e2e-dlq' })
|
|
32
|
+
);
|
|
33
|
+
const dlqUrl = `${endpoint}${new URL(dlq.QueueUrl!).pathname}`;
|
|
34
|
+
const attributes = await client.send(
|
|
35
|
+
new GetQueueAttributesCommand({
|
|
36
|
+
QueueUrl: dlqUrl,
|
|
37
|
+
AttributeNames: ['QueueArn'],
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
const queue = await client.send(
|
|
41
|
+
new CreateQueueCommand({
|
|
42
|
+
QueueName: 'inbox-e2e',
|
|
43
|
+
Attributes: {
|
|
44
|
+
VisibilityTimeout: '1800',
|
|
45
|
+
RedrivePolicy: JSON.stringify({
|
|
46
|
+
deadLetterTargetArn: attributes.Attributes!.QueueArn,
|
|
47
|
+
maxReceiveCount: '5',
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
provide('inbox-queue-endpoint', endpoint);
|
|
53
|
+
provide('inbox-queue-url', `${endpoint}${new URL(queue.QueueUrl!).pathname}`);
|
|
54
|
+
provide('inbox-dead-letter-url', dlqUrl);
|
|
55
|
+
} finally {
|
|
56
|
+
client.destroy();
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
await Promise.all([redis.stop(), sqs?.stop()]);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
10
62
|
provide('neo4j-uri', neo4j.getBoltUri());
|
|
11
63
|
provide('neo4j-password', neo4j.getPassword());
|
|
12
64
|
provide('inbox-redis-host', redis.getHost());
|
|
13
65
|
provide('inbox-redis-port', redis.getMappedPort(6379));
|
|
14
66
|
return async () => {
|
|
15
|
-
await Promise.all([redis.stop(), neo4j.stop()]);
|
|
67
|
+
await Promise.all([redis.stop(), neo4j.stop(), sqs?.stop()]);
|
|
16
68
|
};
|
|
17
69
|
} catch (error) {
|
|
18
70
|
await neo4j.stop();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
include classpath("application.conf")
|
|
2
|
+
node-address { protocol = http, host = localhost, port = 9324, context-path = "" }
|
|
3
|
+
# Compose uses these queues; isolated tests create their own inbox-e2e queues dynamically.
|
|
4
|
+
queues {
|
|
5
|
+
inbox-dlq { defaultVisibilityTimeout = 180 seconds }
|
|
6
|
+
inbox {
|
|
7
|
+
defaultVisibilityTimeout = 1800 seconds
|
|
8
|
+
deadLettersQueue { name = "inbox-dlq", maxReceiveCount = 5 }
|
|
9
|
+
}
|
|
10
|
+
}
|
package/lambda.ts
CHANGED
|
@@ -25,6 +25,10 @@ import {
|
|
|
25
25
|
import { environment } from './src/config/environment';
|
|
26
26
|
import { toServerlessApplication } from './src/helpers/serverlessApplication';
|
|
27
27
|
import { runInboxMaintenance } from './src/helpers/inbox-maintenance.helpers';
|
|
28
|
+
import {
|
|
29
|
+
inboxBatchResponseMeta,
|
|
30
|
+
withInboxBatchBodyLimit,
|
|
31
|
+
} from './src/helpers/inbox-batch-http.helpers';
|
|
28
32
|
|
|
29
33
|
Sentry.AWSLambda.init({
|
|
30
34
|
dsn: environment.SENTRY_DSN,
|
|
@@ -61,8 +65,9 @@ export const credentialRefreshHandler: typeof credentialRefreshProxy = async (ev
|
|
|
61
65
|
|
|
62
66
|
export const _openApiHandler = createOpenApiAwsLambdaHandler({
|
|
63
67
|
router: appRouter,
|
|
64
|
-
responseMeta:
|
|
68
|
+
responseMeta: meta => {
|
|
65
69
|
return {
|
|
70
|
+
...inboxBatchResponseMeta(meta),
|
|
66
71
|
headers: {
|
|
67
72
|
'Access-Control-Allow-Origin': '*',
|
|
68
73
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
@@ -74,21 +79,24 @@ export const _openApiHandler = createOpenApiAwsLambdaHandler({
|
|
|
74
79
|
onError: handleTrpcError,
|
|
75
80
|
});
|
|
76
81
|
|
|
77
|
-
export const _trpcHandler =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
82
|
+
export const _trpcHandler = withInboxBatchBodyLimit(
|
|
83
|
+
awsLambdaRequestHandler({
|
|
84
|
+
allowMethodOverride: true,
|
|
85
|
+
router: appRouter,
|
|
86
|
+
createContext,
|
|
87
|
+
onError: handleTrpcError,
|
|
88
|
+
responseMeta: () => {
|
|
89
|
+
return {
|
|
90
|
+
headers: {
|
|
91
|
+
'Access-Control-Allow-Origin': '*',
|
|
92
|
+
'Access-Control-Allow-Methods': '*',
|
|
93
|
+
'Access-Control-Allow-Headers': 'authorization',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
'trpc'
|
|
99
|
+
);
|
|
92
100
|
|
|
93
101
|
export const openApiHandler = Sentry.AWSLambda.wrapHandler(
|
|
94
102
|
async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
|
|
@@ -124,6 +132,43 @@ export const trpcHandler = Sentry.AWSLambda.wrapHandler(
|
|
|
124
132
|
}
|
|
125
133
|
);
|
|
126
134
|
|
|
135
|
+
export const inboxQueueWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
136
|
+
const { processInboxQueueMessage } = await import('./src/helpers/inbox-queue.helpers');
|
|
137
|
+
const batchItemFailures = [];
|
|
138
|
+
for (const record of event.Records) {
|
|
139
|
+
try {
|
|
140
|
+
await processInboxQueueMessage(record.body);
|
|
141
|
+
} catch {
|
|
142
|
+
console.error('Inbox worker message failed', { messageId: record.messageId });
|
|
143
|
+
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const inboxQueueDispatcher = Sentry.AWSLambda.wrapHandler(
|
|
150
|
+
async (_event: unknown, context: Context): Promise<void> => {
|
|
151
|
+
const { dispatchInboxJobs } = await import('./src/helpers/inbox-queue.helpers');
|
|
152
|
+
// Share one clock across publication and recovery, retaining five seconds for shutdown.
|
|
153
|
+
const deadline = Date.now() + Math.max(0, context.getRemainingTimeInMillis() - 5_000);
|
|
154
|
+
await dispatchInboxJobs(deadline);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
export const inboxDeadLetterWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
159
|
+
const { processInboxDeadLetter } = await import('./src/helpers/inbox-queue.helpers');
|
|
160
|
+
const batchItemFailures = [];
|
|
161
|
+
for (const record of event.Records) {
|
|
162
|
+
try {
|
|
163
|
+
await processInboxDeadLetter(record.body);
|
|
164
|
+
} catch {
|
|
165
|
+
console.error('Inbox dead-letter processing failed', { messageId: record.messageId });
|
|
166
|
+
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
170
|
+
});
|
|
171
|
+
|
|
127
172
|
export const notificationsWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
128
173
|
const batchItemFailures = await Promise.all(
|
|
129
174
|
event.Records.map(async record => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/network-brain-service",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24.12 <25"
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"scripts": {
|
|
12
|
+
"inbox:setup-queues": "bun scripts/setup-inbox-queues.ts",
|
|
13
|
+
"inbox:worker": "bun --conditions=development src/inbox-queue-local.ts",
|
|
12
14
|
"test": "vitest run --config vitest.config.ts",
|
|
13
15
|
"test:watch": "vitest --config vitest.config.ts",
|
|
14
16
|
"test:coverage": "vitest run --coverage --config vitest.config.ts",
|
|
@@ -59,21 +61,21 @@
|
|
|
59
61
|
"@digitalcredentials/issuer-registry-client": "^3.2.0-beta.5",
|
|
60
62
|
"@fastify/cors": "^11.2.0",
|
|
61
63
|
"@google/genai": "^1.27.0",
|
|
62
|
-
"@learncard/core": "9.4.
|
|
63
|
-
"@learncard/crypto-plugin": "1.1.
|
|
64
|
-
"@learncard/did-web-plugin": "1.1.
|
|
65
|
-
"@learncard/didkey-plugin": "1.1.
|
|
66
|
-
"@learncard/didkit-plugin": "1.
|
|
67
|
-
"@learncard/didkit-plugin-node": "0.
|
|
68
|
-
"@learncard/dynamic-loader-plugin": "1.1.
|
|
69
|
-
"@learncard/encryption-plugin": "1.1.
|
|
70
|
-
"@learncard/expiration-plugin": "1.2.
|
|
71
|
-
"@learncard/email-templates": "1.0
|
|
72
|
-
"@learncard/helpers": "1.
|
|
73
|
-
"@learncard/learn-card-plugin": "1.2.
|
|
74
|
-
"@learncard/types": "5.
|
|
75
|
-
"@learncard/vc-plugin": "1.6.
|
|
76
|
-
"@learncard/vc-templates-plugin": "1.1.
|
|
64
|
+
"@learncard/core": "9.4.36",
|
|
65
|
+
"@learncard/crypto-plugin": "1.1.36",
|
|
66
|
+
"@learncard/did-web-plugin": "1.1.36",
|
|
67
|
+
"@learncard/didkey-plugin": "1.1.36",
|
|
68
|
+
"@learncard/didkit-plugin": "1.10.1",
|
|
69
|
+
"@learncard/didkit-plugin-node": "0.3.1",
|
|
70
|
+
"@learncard/dynamic-loader-plugin": "1.1.36",
|
|
71
|
+
"@learncard/encryption-plugin": "1.1.36",
|
|
72
|
+
"@learncard/expiration-plugin": "1.2.36",
|
|
73
|
+
"@learncard/email-templates": "1.1.0",
|
|
74
|
+
"@learncard/helpers": "1.6.0",
|
|
75
|
+
"@learncard/learn-card-plugin": "1.2.36",
|
|
76
|
+
"@learncard/types": "5.21.0",
|
|
77
|
+
"@learncard/vc-plugin": "1.6.2",
|
|
78
|
+
"@learncard/vc-templates-plugin": "1.1.36",
|
|
77
79
|
"@sentry/serverless": "7.61.0",
|
|
78
80
|
"@trpc/server": "11.8.0",
|
|
79
81
|
"@types/lodash": "^4.14.191",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SQSClient,
|
|
3
|
+
CreateQueueCommand,
|
|
4
|
+
GetQueueUrlCommand,
|
|
5
|
+
GetQueueAttributesCommand,
|
|
6
|
+
SetQueueAttributesCommand,
|
|
7
|
+
} from '@aws-sdk/client-sqs';
|
|
8
|
+
|
|
9
|
+
const endpoint = process.env.INBOX_QUEUE_ENDPOINT ?? 'http://localhost:9324';
|
|
10
|
+
const client = new SQSClient({
|
|
11
|
+
region: process.env.AWS_REGION ?? 'us-east-1',
|
|
12
|
+
endpoint,
|
|
13
|
+
credentials: { accessKeyId: 'local', secretAccessKey: 'local' },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const ensureQueue = async (name: string): Promise<string> => {
|
|
17
|
+
let url: string | undefined;
|
|
18
|
+
try {
|
|
19
|
+
url = (await client.send(new GetQueueUrlCommand({ QueueName: name }))).QueueUrl;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (
|
|
22
|
+
!(error instanceof Error) ||
|
|
23
|
+
!['QueueDoesNotExist', 'AWS.SimpleQueueService.NonExistentQueue'].includes(error.name)
|
|
24
|
+
)
|
|
25
|
+
throw error;
|
|
26
|
+
url = (await client.send(new CreateQueueCommand({ QueueName: name }))).QueueUrl;
|
|
27
|
+
}
|
|
28
|
+
if (!url) throw new Error(`Missing URL for ${name}`);
|
|
29
|
+
// ElasticMQ may advertise its container hostname; use the caller's reachable endpoint.
|
|
30
|
+
return `${endpoint.replace(/\/$/, '')}${new URL(url).pathname}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const dlqUrl = await ensureQueue('inbox-dlq');
|
|
35
|
+
const attributes = await client.send(
|
|
36
|
+
new GetQueueAttributesCommand({
|
|
37
|
+
QueueUrl: dlqUrl,
|
|
38
|
+
AttributeNames: ['QueueArn'],
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
if (!attributes.Attributes?.QueueArn)
|
|
42
|
+
throw new Error('Inbox dead-letter queue ARN is missing.');
|
|
43
|
+
const queueUrl = await ensureQueue('inbox');
|
|
44
|
+
await client.send(
|
|
45
|
+
new SetQueueAttributesCommand({
|
|
46
|
+
QueueUrl: queueUrl,
|
|
47
|
+
Attributes: {
|
|
48
|
+
VisibilityTimeout: '1800',
|
|
49
|
+
RedrivePolicy: JSON.stringify({
|
|
50
|
+
deadLetterTargetArn: attributes.Attributes.QueueArn,
|
|
51
|
+
maxReceiveCount: '5',
|
|
52
|
+
}),
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
console.log(`INBOX_QUEUE_URL=${queueUrl}`);
|
|
57
|
+
console.log(`INBOX_DEAD_LETTER_QUEUE_URL=${dlqUrl}`);
|
|
58
|
+
} finally {
|
|
59
|
+
client.destroy();
|
|
60
|
+
}
|
package/serverless-local.yml
CHANGED
|
@@ -47,6 +47,8 @@ provider:
|
|
|
47
47
|
stage: ${opt:stage, "dev"}
|
|
48
48
|
region: ${opt:region, "us-east-1"}
|
|
49
49
|
environment:
|
|
50
|
+
INBOX_QUEUE_URL: ${env:INBOX_QUEUE_URL, ''}
|
|
51
|
+
INBOX_QUEUE_ENDPOINT: ${env:INBOX_QUEUE_ENDPOINT, ''}
|
|
50
52
|
PORT: ${env:PORT, 3000}
|
|
51
53
|
LAMBDA_STAGE: ${opt:stage, "dev"}
|
|
52
54
|
SEED: ${env:SEED}
|
package/serverless.yml
CHANGED
|
@@ -15,6 +15,20 @@ plugins:
|
|
|
15
15
|
- serverless-prune-plugin
|
|
16
16
|
|
|
17
17
|
constructs:
|
|
18
|
+
inbox-queue:
|
|
19
|
+
type: queue
|
|
20
|
+
batchSize: 1
|
|
21
|
+
maxConcurrency: 10
|
|
22
|
+
maxRetries: 5
|
|
23
|
+
worker:
|
|
24
|
+
handler: lambda.inboxQueueWorker
|
|
25
|
+
timeout: 300
|
|
26
|
+
reservedConcurrency: 10
|
|
27
|
+
vpc:
|
|
28
|
+
securityGroupIds:
|
|
29
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
30
|
+
subnetIds:
|
|
31
|
+
- Ref: PrivateSubnetA
|
|
18
32
|
notifications-queue:
|
|
19
33
|
type: queue
|
|
20
34
|
worker:
|
|
@@ -128,6 +142,7 @@ provider:
|
|
|
128
142
|
PORT: ${env:PORT, 3000}
|
|
129
143
|
INBOX_DELETE_EXPIRED_RECORDS: ${env:INBOX_DELETE_EXPIRED_RECORDS, "false"}
|
|
130
144
|
LAMBDA_STAGE: ${opt:stage, "dev"}
|
|
145
|
+
INBOX_BATCH_ITEMS_PER_HOUR: ${env:INBOX_BATCH_ITEMS_PER_HOUR, '10000'}
|
|
131
146
|
SEED: ${env:SEED}
|
|
132
147
|
DOMAIN_NAME: ${env:DOMAIN_NAME}
|
|
133
148
|
CLIENT_APP_DOMAIN_NAME: ${env:CLIENT_APP_DOMAIN_NAME}
|
|
@@ -153,6 +168,7 @@ provider:
|
|
|
153
168
|
REDIS_PORT:
|
|
154
169
|
'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
|
|
155
170
|
NOTIFICATIONS_QUEUE_URL: ${construct:notifications-queue.queueUrl}
|
|
171
|
+
INBOX_QUEUE_URL: ${construct:inbox-queue.queueUrl}
|
|
156
172
|
SKILL_EMBEDDING_GOOGLE_API_KEY: ${env:SKILL_EMBEDDING_GOOGLE_API_KEY, ""}
|
|
157
173
|
SKILL_EMBEDDING_GOOGLE_MODEL: ${env:SKILL_EMBEDDING_GOOGLE_MODEL, "gemini-embedding-001"}
|
|
158
174
|
SKILL_EMBEDDING_BATCH_SIZE: ${env:SKILL_EMBEDDING_BATCH_SIZE, "25"}
|
|
@@ -190,6 +206,31 @@ provider:
|
|
|
190
206
|
- ETag
|
|
191
207
|
|
|
192
208
|
functions:
|
|
209
|
+
inboxDeadLetterWorker:
|
|
210
|
+
handler: lambda.inboxDeadLetterWorker
|
|
211
|
+
timeout: 30
|
|
212
|
+
reservedConcurrency: 2
|
|
213
|
+
vpc:
|
|
214
|
+
securityGroupIds:
|
|
215
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
216
|
+
subnetIds:
|
|
217
|
+
- Ref: PrivateSubnetA
|
|
218
|
+
events:
|
|
219
|
+
- sqs:
|
|
220
|
+
arn: ${construct:inbox-queue.dlqArn}
|
|
221
|
+
batchSize: 1
|
|
222
|
+
functionResponseType: ReportBatchItemFailures
|
|
223
|
+
inboxQueueDispatcher:
|
|
224
|
+
handler: lambda.inboxQueueDispatcher
|
|
225
|
+
timeout: 60
|
|
226
|
+
reservedConcurrency: 1
|
|
227
|
+
vpc:
|
|
228
|
+
securityGroupIds:
|
|
229
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
230
|
+
subnetIds:
|
|
231
|
+
- Ref: PrivateSubnetA
|
|
232
|
+
events:
|
|
233
|
+
- schedule: rate(1 minute)
|
|
193
234
|
inboxMaintenance:
|
|
194
235
|
handler: lambda.inboxMaintenanceHandler
|
|
195
236
|
timeout: 300
|
|
@@ -288,6 +329,40 @@ functions:
|
|
|
288
329
|
|
|
289
330
|
resources:
|
|
290
331
|
Resources:
|
|
332
|
+
InboxQueueAgeAlarm:
|
|
333
|
+
Type: AWS::CloudWatch::Alarm
|
|
334
|
+
Properties:
|
|
335
|
+
AlarmDescription: Inbox issuance has waited over five minutes
|
|
336
|
+
Namespace: AWS/SQS
|
|
337
|
+
MetricName: ApproximateAgeOfOldestMessage
|
|
338
|
+
Dimensions:
|
|
339
|
+
- Name: QueueName
|
|
340
|
+
Value:
|
|
341
|
+
Fn::Select:
|
|
342
|
+
- 4
|
|
343
|
+
- Fn::Split: ['/', '${construct:inbox-queue.queueUrl}']
|
|
344
|
+
Statistic: Maximum
|
|
345
|
+
Period: 60
|
|
346
|
+
EvaluationPeriods: 2
|
|
347
|
+
Threshold: 300
|
|
348
|
+
ComparisonOperator: GreaterThanThreshold
|
|
349
|
+
TreatMissingData: notBreaching
|
|
350
|
+
InboxDispatcherErrorsAlarm:
|
|
351
|
+
Type: AWS::CloudWatch::Alarm
|
|
352
|
+
Properties:
|
|
353
|
+
AlarmDescription: Durable inbox dispatch or recovery failed in three of five minutes
|
|
354
|
+
Namespace: AWS/Lambda
|
|
355
|
+
MetricName: Errors
|
|
356
|
+
Dimensions:
|
|
357
|
+
- Name: FunctionName
|
|
358
|
+
Value: { Ref: InboxQueueDispatcherLambdaFunction }
|
|
359
|
+
Statistic: Sum
|
|
360
|
+
Period: 60
|
|
361
|
+
EvaluationPeriods: 5
|
|
362
|
+
DatapointsToAlarm: 3
|
|
363
|
+
Threshold: 0
|
|
364
|
+
ComparisonOperator: GreaterThanThreshold
|
|
365
|
+
TreatMissingData: notBreaching
|
|
291
366
|
VPC:
|
|
292
367
|
Type: AWS::EC2::VPC
|
|
293
368
|
Properties:
|
|
@@ -192,11 +192,12 @@ export const getCredentialsSentByListingToProfile = async (
|
|
|
192
192
|
const isReceived = record.get('isReceived');
|
|
193
193
|
const sentStatus = record.get('sentStatus');
|
|
194
194
|
const receivedStatus = record.get('receivedStatus');
|
|
195
|
-
const rawStatus = sentStatus ?? receivedStatus;
|
|
196
195
|
let status: 'pending' | 'claimed' | 'revoked' | 'suspended' = 'pending';
|
|
197
196
|
|
|
198
|
-
if (
|
|
199
|
-
status =
|
|
197
|
+
if (sentStatus === 'revoked' || receivedStatus === 'revoked') {
|
|
198
|
+
status = 'revoked';
|
|
199
|
+
} else if (sentStatus === 'suspended' || receivedStatus === 'suspended') {
|
|
200
|
+
status = 'suspended';
|
|
200
201
|
} else if (isReceived) {
|
|
201
202
|
status = 'claimed';
|
|
202
203
|
}
|
|
@@ -98,7 +98,7 @@ export const isAuthGrantAssociatedWithProfile = async (
|
|
|
98
98
|
export const isAuthGrantChallengeValidForDID = async (
|
|
99
99
|
challenge: string,
|
|
100
100
|
did: string
|
|
101
|
-
): Promise<{ isChallengeValid: boolean; scope: string }> => {
|
|
101
|
+
): Promise<{ isChallengeValid: boolean; scope: string; actAs?: string }> => {
|
|
102
102
|
// TODO: Ideally, this transformation isn't required and we could use DID directly.
|
|
103
103
|
const profileId = getProfileIdFromDid(did);
|
|
104
104
|
if (!profileId) return { isChallengeValid: false, scope: AUTH_GRANT_NO_ACCESS_SCOPE };
|
|
@@ -126,5 +126,6 @@ export const isAuthGrantChallengeValidForDID = async (
|
|
|
126
126
|
return {
|
|
127
127
|
isChallengeValid: result.records.length > 0,
|
|
128
128
|
scope: authGrant?.scope || AUTH_GRANT_NO_ACCESS_SCOPE,
|
|
129
|
+
actAs: authGrant?.actAs,
|
|
129
130
|
};
|
|
130
131
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConsentFlowTerms as ConsentFlowTermsType,
|
|
3
3
|
ConsentFlowTransaction,
|
|
4
|
+
ConsentFlowGuardianApproval,
|
|
4
5
|
LCNNotificationTypeEnumValidator,
|
|
5
6
|
LCNProfile,
|
|
6
7
|
UnsignedVC,
|
|
@@ -76,11 +77,13 @@ export const consentToContract = async (
|
|
|
76
77
|
terms: _terms,
|
|
77
78
|
expiresAt,
|
|
78
79
|
oneTime,
|
|
80
|
+
guardianApproval,
|
|
79
81
|
}: {
|
|
80
82
|
terms: ConsentFlowTermsType;
|
|
81
83
|
expiresAt?: string;
|
|
82
84
|
liveSyncing?: boolean;
|
|
83
85
|
oneTime?: boolean;
|
|
86
|
+
guardianApproval?: ConsentFlowGuardianApproval;
|
|
84
87
|
},
|
|
85
88
|
domain: string
|
|
86
89
|
) => {
|
|
@@ -122,7 +125,7 @@ export const consentToContract = async (
|
|
|
122
125
|
if (existing.length > 0) {
|
|
123
126
|
return reconsentTerms(
|
|
124
127
|
{ terms: inflateObject(existing[0]!.terms), consenter, contract, contractOwner },
|
|
125
|
-
{ terms, expiresAt, oneTime },
|
|
128
|
+
{ terms, expiresAt, oneTime, guardianApproval },
|
|
126
129
|
domain
|
|
127
130
|
);
|
|
128
131
|
}
|
|
@@ -137,7 +140,11 @@ export const consentToContract = async (
|
|
|
137
140
|
|
|
138
141
|
const termsId = uuid();
|
|
139
142
|
|
|
140
|
-
const result = await new QueryBuilder(
|
|
143
|
+
const result = await new QueryBuilder(
|
|
144
|
+
new BindParam({
|
|
145
|
+
params: flattenObject({ terms, ...(guardianApproval ? { guardianApproval } : {}) }),
|
|
146
|
+
})
|
|
147
|
+
)
|
|
141
148
|
.match({
|
|
142
149
|
multiple: [
|
|
143
150
|
{
|
|
@@ -157,8 +164,8 @@ export const consentToContract = async (
|
|
|
157
164
|
properties: {
|
|
158
165
|
id: termsId,
|
|
159
166
|
status: oneTime ? 'stale' : 'live',
|
|
160
|
-
createdAt:
|
|
161
|
-
updatedAt:
|
|
167
|
+
createdAt: transaction.date,
|
|
168
|
+
updatedAt: transaction.date,
|
|
162
169
|
...(expiresAt ? { expiresAt } : {}),
|
|
163
170
|
...(oneTime ? { oneTime } : {}),
|
|
164
171
|
},
|
|
@@ -259,7 +266,10 @@ export const consentToContract = async (
|
|
|
259
266
|
boostCredential,
|
|
260
267
|
contractOwnerSigningAuthority,
|
|
261
268
|
domain,
|
|
262
|
-
|
|
269
|
+
true,
|
|
270
|
+
undefined,
|
|
271
|
+
true,
|
|
272
|
+
[getDidWeb(domain, contractOwner.profileId)]
|
|
263
273
|
);
|
|
264
274
|
|
|
265
275
|
const boostTransaction = {
|