@brightchain/brightchain-api-lib 0.15.0 → 0.17.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/package.json +4 -4
- package/src/browser.d.ts +13 -0
- package/src/browser.d.ts.map +1 -0
- package/src/browser.js +16 -0
- package/src/browser.js.map +1 -0
- package/src/index.d.ts +5 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +7 -1
- package/src/index.js.map +1 -1
- package/src/lib/adapters/brightChainDbDocumentStoreAdapter.d.ts +24 -0
- package/src/lib/adapters/brightChainDbDocumentStoreAdapter.d.ts.map +1 -0
- package/src/lib/adapters/brightChainDbDocumentStoreAdapter.js +53 -0
- package/src/lib/adapters/brightChainDbDocumentStoreAdapter.js.map +1 -0
- package/src/lib/application-base.d.ts +8 -37
- package/src/lib/application-base.d.ts.map +1 -1
- package/src/lib/application-base.js +8 -80
- package/src/lib/application-base.js.map +1 -1
- package/src/lib/application.d.ts +56 -7
- package/src/lib/application.d.ts.map +1 -1
- package/src/lib/application.js +169 -134
- package/src/lib/application.js.map +1 -1
- package/src/lib/availability/gossipService.d.ts +20 -1
- package/src/lib/availability/gossipService.d.ts.map +1 -1
- package/src/lib/availability/gossipService.js +59 -0
- package/src/lib/availability/gossipService.js.map +1 -1
- package/src/lib/availability/index.d.ts +1 -0
- package/src/lib/availability/index.d.ts.map +1 -1
- package/src/lib/availability/index.js +1 -0
- package/src/lib/availability/index.js.map +1 -1
- package/src/lib/availability/poolDiscoveryService.d.ts +117 -0
- package/src/lib/availability/poolDiscoveryService.d.ts.map +1 -0
- package/src/lib/availability/poolDiscoveryService.js +218 -0
- package/src/lib/availability/poolDiscoveryService.js.map +1 -0
- package/src/lib/blockFetch/httpBlockFetchTransport.d.ts.map +1 -1
- package/src/lib/blockFetch/httpBlockFetchTransport.js.map +1 -1
- package/src/lib/constants.js +2 -2
- package/src/lib/constants.js.map +1 -1
- package/src/lib/controllers/api/blocks.d.ts.map +1 -1
- package/src/lib/controllers/api/blocks.js +290 -290
- package/src/lib/controllers/api/blocks.js.map +1 -1
- package/src/lib/controllers/api/brightpass.d.ts.map +1 -1
- package/src/lib/controllers/api/brightpass.js +270 -274
- package/src/lib/controllers/api/brightpass.js.map +1 -1
- package/src/lib/controllers/api/energy.d.ts.map +1 -1
- package/src/lib/controllers/api/energy.js +93 -93
- package/src/lib/controllers/api/energy.js.map +1 -1
- package/src/lib/controllers/api/health.d.ts +4 -4
- package/src/lib/controllers/api/health.d.ts.map +1 -1
- package/src/lib/controllers/api/health.js.map +1 -1
- package/src/lib/controllers/api/i18n.d.ts.map +1 -1
- package/src/lib/controllers/api/i18n.js +8 -8
- package/src/lib/controllers/api/i18n.js.map +1 -1
- package/src/lib/controllers/api/index.d.ts +1 -0
- package/src/lib/controllers/api/index.d.ts.map +1 -1
- package/src/lib/controllers/api/index.js +1 -0
- package/src/lib/controllers/api/index.js.map +1 -1
- package/src/lib/controllers/api/introspection.d.ts +60 -0
- package/src/lib/controllers/api/introspection.d.ts.map +1 -0
- package/src/lib/controllers/api/introspection.js +375 -0
- package/src/lib/controllers/api/introspection.js.map +1 -0
- package/src/lib/controllers/api/nodes.d.ts +7 -6
- package/src/lib/controllers/api/nodes.d.ts.map +1 -1
- package/src/lib/controllers/api/nodes.js.map +1 -1
- package/src/lib/controllers/api/quorum.d.ts.map +1 -1
- package/src/lib/controllers/api/quorum.js +483 -483
- package/src/lib/controllers/api/quorum.js.map +1 -1
- package/src/lib/controllers/api/sessions.d.ts.map +1 -1
- package/src/lib/controllers/api/sessions.js +8 -8
- package/src/lib/controllers/api/sessions.js.map +1 -1
- package/src/lib/controllers/api/sync.d.ts +6 -5
- package/src/lib/controllers/api/sync.d.ts.map +1 -1
- package/src/lib/controllers/api/sync.js.map +1 -1
- package/src/lib/controllers/api/user.d.ts.map +1 -1
- package/src/lib/controllers/api/user.js +260 -180
- package/src/lib/controllers/api/user.js.map +1 -1
- package/src/lib/controllers/base.d.ts +1 -2
- package/src/lib/controllers/base.d.ts.map +1 -1
- package/src/lib/controllers/base.js +0 -1
- package/src/lib/controllers/base.js.map +1 -1
- package/src/lib/databaseInit.d.ts +28 -0
- package/src/lib/databaseInit.d.ts.map +1 -0
- package/src/lib/databaseInit.js +107 -0
- package/src/lib/databaseInit.js.map +1 -0
- package/src/lib/enumerations/brightChainApiStrings.d.ts +4 -0
- package/src/lib/enumerations/brightChainApiStrings.d.ts.map +1 -1
- package/src/lib/enumerations/brightChainApiStrings.js +4 -0
- package/src/lib/enumerations/brightChainApiStrings.js.map +1 -1
- package/src/lib/enumerations/index.d.ts +1 -0
- package/src/lib/enumerations/index.d.ts.map +1 -1
- package/src/lib/enumerations/index.js +1 -0
- package/src/lib/enumerations/index.js.map +1 -1
- package/src/lib/enumerations/websocketMessageType.d.ts +5 -1
- package/src/lib/enumerations/websocketMessageType.d.ts.map +1 -1
- package/src/lib/enumerations/websocketMessageType.js +4 -0
- package/src/lib/enumerations/websocketMessageType.js.map +1 -1
- package/src/lib/environment.d.ts +5 -0
- package/src/lib/environment.d.ts.map +1 -1
- package/src/lib/environment.js +7 -0
- package/src/lib/environment.js.map +1 -1
- package/src/lib/errors/index.d.ts +1 -0
- package/src/lib/errors/index.d.ts.map +1 -1
- package/src/lib/errors/index.js +1 -0
- package/src/lib/errors/index.js.map +1 -1
- package/src/lib/errors/memberIndexSchemaValidationError.d.ts +6 -0
- package/src/lib/errors/memberIndexSchemaValidationError.d.ts.map +1 -0
- package/src/lib/errors/memberIndexSchemaValidationError.js +15 -0
- package/src/lib/errors/memberIndexSchemaValidationError.js.map +1 -0
- package/src/lib/factories/blockStoreFactory.d.ts +2 -13
- package/src/lib/factories/blockStoreFactory.d.ts.map +1 -1
- package/src/lib/factories/blockStoreFactory.js +5 -10
- package/src/lib/factories/blockStoreFactory.js.map +1 -1
- package/src/lib/interfaces/application.d.ts +7 -1
- package/src/lib/interfaces/application.d.ts.map +1 -1
- package/src/lib/interfaces/blocksHandlers.d.ts +3 -3
- package/src/lib/interfaces/blocksHandlers.d.ts.map +1 -1
- package/src/lib/interfaces/brightchain-init-result.d.ts +17 -0
- package/src/lib/interfaces/brightchain-init-result.d.ts.map +1 -0
- package/src/lib/interfaces/brightchain-init-result.js +9 -0
- package/src/lib/interfaces/brightchain-init-result.js.map +1 -0
- package/src/lib/interfaces/cblHandlers.d.ts +3 -2
- package/src/lib/interfaces/cblHandlers.d.ts.map +1 -1
- package/src/lib/interfaces/environment.d.ts +4 -0
- package/src/lib/interfaces/environment.d.ts.map +1 -1
- package/src/lib/interfaces/index.d.ts +5 -0
- package/src/lib/interfaces/index.d.ts.map +1 -1
- package/src/lib/interfaces/introspectionApiResponses.d.ts +66 -0
- package/src/lib/interfaces/introspectionApiResponses.d.ts.map +1 -0
- package/src/lib/interfaces/introspectionApiResponses.js +3 -0
- package/src/lib/interfaces/introspectionApiResponses.js.map +1 -0
- package/src/lib/interfaces/members-handlers.d.ts +11 -0
- package/src/lib/interfaces/members-handlers.d.ts.map +1 -0
- package/src/lib/interfaces/members-handlers.js +3 -0
- package/src/lib/interfaces/members-handlers.js.map +1 -0
- package/src/lib/interfaces/responses/api-backup-codes-response.d.ts +2 -5
- package/src/lib/interfaces/responses/api-backup-codes-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-challenge-response.d.ts +2 -5
- package/src/lib/interfaces/responses/api-challenge-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-code-count-response.d.ts +2 -5
- package/src/lib/interfaces/responses/api-code-count-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-detailed-health-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-detailed-health-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-detailed-health-response.js +3 -0
- package/src/lib/interfaces/responses/api-detailed-health-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-discover-block-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-discover-block-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-discover-block-response.js +3 -0
- package/src/lib/interfaces/responses/api-discover-block-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-error.d.ts +5 -0
- package/src/lib/interfaces/responses/api-error.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-error.js +3 -0
- package/src/lib/interfaces/responses/api-error.js.map +1 -0
- package/src/lib/interfaces/responses/api-express-validation-error.d.ts +7 -0
- package/src/lib/interfaces/responses/api-express-validation-error.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-express-validation-error.js +3 -0
- package/src/lib/interfaces/responses/api-express-validation-error.js.map +1 -0
- package/src/lib/interfaces/responses/api-get-block-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-get-block-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-get-block-response.js +3 -0
- package/src/lib/interfaces/responses/api-get-block-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-get-node-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-get-node-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-get-node-response.js +3 -0
- package/src/lib/interfaces/responses/api-get-node-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-health-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-health-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-health-response.js +3 -0
- package/src/lib/interfaces/responses/api-health-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-list-nodes-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-list-nodes-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-list-nodes-response.js +3 -0
- package/src/lib/interfaces/responses/api-list-nodes-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-login-response.d.ts +2 -14
- package/src/lib/interfaces/responses/api-login-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-members-response.d.ts +8 -0
- package/src/lib/interfaces/responses/api-members-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-members-response.js +3 -0
- package/src/lib/interfaces/responses/api-members-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-mnemonic-response.d.ts +2 -5
- package/src/lib/interfaces/responses/api-mnemonic-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-reconcile-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-reconcile-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-reconcile-response.js +3 -0
- package/src/lib/interfaces/responses/api-reconcile-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-register-node-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-register-node-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-register-node-response.js +3 -0
- package/src/lib/interfaces/responses/api-register-node-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-registration-response.d.ts +2 -10
- package/src/lib/interfaces/responses/api-registration-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-replicate-block-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-replicate-block-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-replicate-block-response.js +3 -0
- package/src/lib/interfaces/responses/api-replicate-block-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-request-user-response.d.ts +2 -6
- package/src/lib/interfaces/responses/api-request-user-response.d.ts.map +1 -1
- package/src/lib/interfaces/responses/api-status-code-response.d.ts +7 -0
- package/src/lib/interfaces/responses/api-status-code-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-status-code-response.js +3 -0
- package/src/lib/interfaces/responses/api-status-code-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-store-block-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-store-block-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-store-block-response.js +3 -0
- package/src/lib/interfaces/responses/api-store-block-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-store-cbl-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-store-cbl-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-store-cbl-response.js +3 -0
- package/src/lib/interfaces/responses/api-store-cbl-response.js.map +1 -0
- package/src/lib/interfaces/responses/api-sync-request-response.d.ts +5 -0
- package/src/lib/interfaces/responses/api-sync-request-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/api-sync-request-response.js +3 -0
- package/src/lib/interfaces/responses/api-sync-request-response.js.map +1 -0
- package/src/lib/interfaces/responses/block-data-response.d.ts +11 -0
- package/src/lib/interfaces/responses/block-data-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/block-data-response.js +3 -0
- package/src/lib/interfaces/responses/block-data-response.js.map +1 -0
- package/src/lib/interfaces/responses/block-location-response.d.ts +11 -0
- package/src/lib/interfaces/responses/block-location-response.d.ts.map +1 -0
- package/src/lib/interfaces/responses/block-location-response.js +3 -0
- package/src/lib/interfaces/responses/block-location-response.js.map +1 -0
- package/src/lib/interfaces/responses/index.d.ts +18 -0
- package/src/lib/interfaces/responses/index.d.ts.map +1 -1
- package/src/lib/interfaces/status-code-response.d.ts +1 -6
- package/src/lib/interfaces/status-code-response.d.ts.map +1 -1
- package/src/lib/interfaces/storage/client-session.d.ts +8 -0
- package/src/lib/interfaces/storage/client-session.d.ts.map +1 -0
- package/src/lib/interfaces/storage/client-session.js +3 -0
- package/src/lib/interfaces/storage/client-session.js.map +1 -0
- package/src/lib/interfaces/storage/collection.d.ts +9 -0
- package/src/lib/interfaces/storage/collection.d.ts.map +1 -0
- package/src/lib/interfaces/storage/collection.js +3 -0
- package/src/lib/interfaces/storage/collection.js.map +1 -0
- package/src/lib/interfaces/storage/database-lifecycle-hooks.d.ts +8 -0
- package/src/lib/interfaces/storage/database-lifecycle-hooks.d.ts.map +1 -0
- package/src/lib/interfaces/storage/database-lifecycle-hooks.js +3 -0
- package/src/lib/interfaces/storage/database-lifecycle-hooks.js.map +1 -0
- package/src/lib/interfaces/storage/database.d.ts +9 -0
- package/src/lib/interfaces/storage/database.d.ts.map +1 -0
- package/src/lib/interfaces/storage/database.js +3 -0
- package/src/lib/interfaces/storage/database.js.map +1 -0
- package/src/lib/interfaces/storage/document-types.d.ts +9 -0
- package/src/lib/interfaces/storage/document-types.d.ts.map +1 -0
- package/src/lib/interfaces/storage/document-types.js +10 -0
- package/src/lib/interfaces/storage/document-types.js.map +1 -0
- package/src/lib/interfaces/storage/index.d.ts +7 -0
- package/src/lib/interfaces/storage/index.d.ts.map +1 -0
- package/src/lib/interfaces/storage/index.js +7 -0
- package/src/lib/interfaces/storage/index.js.map +1 -0
- package/src/lib/interfaces/storage/memberIndexSchema.d.ts +11 -0
- package/src/lib/interfaces/storage/memberIndexSchema.d.ts.map +1 -0
- package/src/lib/interfaces/storage/memberIndexSchema.js +26 -0
- package/src/lib/interfaces/storage/memberIndexSchema.js.map +1 -0
- package/src/lib/interfaces/userApiResponse.d.ts +17 -0
- package/src/lib/interfaces/userApiResponse.d.ts.map +1 -0
- package/src/lib/interfaces/userApiResponse.js +3 -0
- package/src/lib/interfaces/userApiResponse.js.map +1 -0
- package/src/lib/interfaces/websocketMessages.d.ts +64 -1
- package/src/lib/interfaces/websocketMessages.d.ts.map +1 -1
- package/src/lib/middleware/index.d.ts +2 -0
- package/src/lib/middleware/index.d.ts.map +1 -0
- package/src/lib/middleware/index.js +6 -0
- package/src/lib/middleware/index.js.map +1 -0
- package/src/lib/middleware/validateBody.d.ts +13 -0
- package/src/lib/middleware/validateBody.d.ts.map +1 -0
- package/src/lib/middleware/validateBody.js +35 -0
- package/src/lib/middleware/validateBody.js.map +1 -0
- package/src/lib/middlewares/authentication.d.ts +2 -0
- package/src/lib/middlewares/authentication.d.ts.map +1 -1
- package/src/lib/middlewares/authentication.js.map +1 -1
- package/src/lib/routers/api.d.ts +27 -0
- package/src/lib/routers/api.d.ts.map +1 -1
- package/src/lib/routers/api.js +44 -0
- package/src/lib/routers/api.js.map +1 -1
- package/src/lib/routers/app.d.ts +22 -14
- package/src/lib/routers/app.d.ts.map +1 -1
- package/src/lib/routers/app.js +28 -116
- package/src/lib/routers/app.js.map +1 -1
- package/src/lib/services/auth.d.ts +3 -1
- package/src/lib/services/auth.d.ts.map +1 -1
- package/src/lib/services/auth.js +51 -4
- package/src/lib/services/auth.js.map +1 -1
- package/src/lib/services/brightchain-member-init.service.d.ts +66 -0
- package/src/lib/services/brightchain-member-init.service.d.ts.map +1 -0
- package/src/lib/services/brightchain-member-init.service.js +152 -0
- package/src/lib/services/brightchain-member-init.service.js.map +1 -0
- package/src/lib/services/brightpass.property.helpers.d.ts +23 -0
- package/src/lib/services/brightpass.property.helpers.d.ts.map +1 -0
- package/src/lib/services/brightpass.property.helpers.js +113 -0
- package/src/lib/services/brightpass.property.helpers.js.map +1 -0
- package/src/lib/services/clientWebSocketServer.d.ts +147 -0
- package/src/lib/services/clientWebSocketServer.d.ts.map +1 -0
- package/src/lib/services/clientWebSocketServer.js +437 -0
- package/src/lib/services/clientWebSocketServer.js.map +1 -0
- package/src/lib/services/index.d.ts +2 -0
- package/src/lib/services/index.d.ts.map +1 -1
- package/src/lib/services/index.js +2 -0
- package/src/lib/services/index.js.map +1 -1
- package/src/lib/stores/diskBlockAsyncStore.d.ts +4 -4
- package/src/lib/stores/diskBlockAsyncStore.d.ts.map +1 -1
- package/src/lib/stores/diskBlockAsyncStore.js.map +1 -1
- package/src/lib/stores/diskBlockStore.d.ts +82 -17
- package/src/lib/stores/diskBlockStore.d.ts.map +1 -1
- package/src/lib/stores/diskBlockStore.js +787 -19
- package/src/lib/stores/diskBlockStore.js.map +1 -1
- package/src/lib/stores/diskBlockSyncStore.d.ts +24 -5
- package/src/lib/stores/diskBlockSyncStore.d.ts.map +1 -1
- package/src/lib/stores/diskBlockSyncStore.js +30 -13
- package/src/lib/stores/diskBlockSyncStore.js.map +1 -1
- package/src/lib/upstream-stubs.d.ts +36 -0
- package/src/lib/upstream-stubs.d.ts.map +1 -0
- package/src/lib/upstream-stubs.js +43 -0
- package/src/lib/upstream-stubs.js.map +1 -0
- package/src/lib/validation/userValidation.d.ts +27 -0
- package/src/lib/validation/userValidation.d.ts.map +1 -0
- package/src/lib/validation/userValidation.js +70 -0
- package/src/lib/validation/userValidation.js.map +1 -0
|
@@ -118,489 +118,6 @@ function serializeMember(member) {
|
|
|
118
118
|
class QuorumController extends base_1.BaseController {
|
|
119
119
|
constructor(application) {
|
|
120
120
|
super(application);
|
|
121
|
-
// === Member Management Handlers ===
|
|
122
|
-
/**
|
|
123
|
-
* POST /api/quorum/members
|
|
124
|
-
* Add a new member to the quorum with generated cryptographic keys.
|
|
125
|
-
*
|
|
126
|
-
* Creates a new member with ECIES key pair and adds them to the quorum.
|
|
127
|
-
* The mnemonic phrase is returned for key recovery - it should be stored
|
|
128
|
-
* securely by the user as it cannot be retrieved later.
|
|
129
|
-
*
|
|
130
|
-
* @param req - Request containing member name and optional metadata
|
|
131
|
-
* @returns Member details and mnemonic on success
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```json
|
|
135
|
-
* // Request
|
|
136
|
-
* POST /api/quorum/members
|
|
137
|
-
* {
|
|
138
|
-
* "name": "Alice",
|
|
139
|
-
* "email": "alice@example.com",
|
|
140
|
-
* "role": "admin"
|
|
141
|
-
* }
|
|
142
|
-
*
|
|
143
|
-
* // Response
|
|
144
|
-
* {
|
|
145
|
-
* "message": "Member added successfully",
|
|
146
|
-
* "member": {
|
|
147
|
-
* "id": "abc123...",
|
|
148
|
-
* "publicKey": "04...",
|
|
149
|
-
* "metadata": { "name": "Alice", "email": "alice@example.com", "role": "admin" },
|
|
150
|
-
* "isActive": true,
|
|
151
|
-
* "createdAt": "2025-01-16T10:00:00Z",
|
|
152
|
-
* "updatedAt": "2025-01-16T10:00:00Z"
|
|
153
|
-
* },
|
|
154
|
-
* "mnemonic": "word1 word2 word3..."
|
|
155
|
-
* }
|
|
156
|
-
* ```
|
|
157
|
-
*/
|
|
158
|
-
this.handleAddMember = async (req) => {
|
|
159
|
-
try {
|
|
160
|
-
const { name, email, role } = req.body;
|
|
161
|
-
// Validate required fields
|
|
162
|
-
if (!name) {
|
|
163
|
-
return (0, errorResponse_1.validationError)('Missing required field: name');
|
|
164
|
-
}
|
|
165
|
-
// Create a new member with generated keys
|
|
166
|
-
const eciesService = brightchain_lib_1.ServiceProvider.getInstance().eciesService;
|
|
167
|
-
const emailString = email
|
|
168
|
-
? new ecies_lib_1.EmailString(email)
|
|
169
|
-
: new ecies_lib_1.EmailString(`${name.toLowerCase().replace(/\s+/g, '.')}@placeholder.local`);
|
|
170
|
-
const memberWithMnemonic = ecies_lib_1.Member.newMember(eciesService, ecies_lib_1.MemberType.User, name, emailString);
|
|
171
|
-
const metadata = {
|
|
172
|
-
name,
|
|
173
|
-
email,
|
|
174
|
-
role,
|
|
175
|
-
};
|
|
176
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
177
|
-
const quorumMember = await quorumService.addMember(memberWithMnemonic.member, metadata);
|
|
178
|
-
return {
|
|
179
|
-
statusCode: 201,
|
|
180
|
-
response: {
|
|
181
|
-
message: 'Member added successfully',
|
|
182
|
-
member: serializeMember(quorumMember),
|
|
183
|
-
mnemonic: memberWithMnemonic.mnemonic.value ?? '',
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
catch (_error) {
|
|
188
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
189
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
190
|
-
}
|
|
191
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
/**
|
|
195
|
-
* GET /api/quorum/members
|
|
196
|
-
* List all active quorum members.
|
|
197
|
-
*
|
|
198
|
-
* Returns all members that are currently active in the quorum.
|
|
199
|
-
* Inactive (removed) members are not included in the response.
|
|
200
|
-
*
|
|
201
|
-
* @param _req - Request (no parameters required)
|
|
202
|
-
* @returns Array of member objects on success
|
|
203
|
-
*
|
|
204
|
-
* @example
|
|
205
|
-
* ```json
|
|
206
|
-
* // Request
|
|
207
|
-
* GET /api/quorum/members
|
|
208
|
-
*
|
|
209
|
-
* // Response
|
|
210
|
-
* {
|
|
211
|
-
* "message": "Members retrieved successfully",
|
|
212
|
-
* "members": [
|
|
213
|
-
* {
|
|
214
|
-
* "id": "abc123...",
|
|
215
|
-
* "publicKey": "04...",
|
|
216
|
-
* "metadata": { "name": "Alice", "role": "admin" },
|
|
217
|
-
* "isActive": true,
|
|
218
|
-
* "createdAt": "2025-01-16T10:00:00Z",
|
|
219
|
-
* "updatedAt": "2025-01-16T10:00:00Z"
|
|
220
|
-
* }
|
|
221
|
-
* ]
|
|
222
|
-
* }
|
|
223
|
-
* ```
|
|
224
|
-
*/
|
|
225
|
-
this.handleListMembers = async (_req) => {
|
|
226
|
-
try {
|
|
227
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
228
|
-
const members = await quorumService.listMembers();
|
|
229
|
-
return {
|
|
230
|
-
statusCode: 200,
|
|
231
|
-
response: {
|
|
232
|
-
message: 'Members retrieved successfully',
|
|
233
|
-
members: members.map(serializeMember),
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
catch (_error) {
|
|
238
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
239
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
240
|
-
}
|
|
241
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
/**
|
|
245
|
-
* DELETE /api/quorum/members/:memberId
|
|
246
|
-
* Remove a member from the quorum (deactivate).
|
|
247
|
-
*
|
|
248
|
-
* Marks the member as inactive. The member's access to existing documents
|
|
249
|
-
* they are part of is preserved, but they cannot be added to new documents.
|
|
250
|
-
*
|
|
251
|
-
* @param req - Request containing the member ID parameter
|
|
252
|
-
* @returns Success confirmation on success, or 404 if not found
|
|
253
|
-
*
|
|
254
|
-
* @example
|
|
255
|
-
* ```json
|
|
256
|
-
* // Request
|
|
257
|
-
* DELETE /api/quorum/members/abc123...
|
|
258
|
-
*
|
|
259
|
-
* // Response
|
|
260
|
-
* {
|
|
261
|
-
* "message": "Member removed successfully",
|
|
262
|
-
* "success": true,
|
|
263
|
-
* "memberId": "abc123..."
|
|
264
|
-
* }
|
|
265
|
-
* ```
|
|
266
|
-
*/
|
|
267
|
-
this.handleRemoveMember = async (req) => {
|
|
268
|
-
try {
|
|
269
|
-
const { memberId } = req.params;
|
|
270
|
-
if (!memberId) {
|
|
271
|
-
return (0, errorResponse_1.validationError)('Missing required parameter: memberId');
|
|
272
|
-
}
|
|
273
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
274
|
-
await quorumService.removeMember(memberId);
|
|
275
|
-
return {
|
|
276
|
-
statusCode: 200,
|
|
277
|
-
response: {
|
|
278
|
-
message: 'Member removed successfully',
|
|
279
|
-
success: true,
|
|
280
|
-
memberId,
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
catch (_error) {
|
|
285
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
286
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
287
|
-
}
|
|
288
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
// === Document Sealing/Unsealing Handlers ===
|
|
292
|
-
/**
|
|
293
|
-
* POST /api/quorum/documents/seal
|
|
294
|
-
* Seal a document using Shamir's Secret Sharing.
|
|
295
|
-
*
|
|
296
|
-
* Encrypts the document with a randomly generated symmetric key, then splits
|
|
297
|
-
* the key into shares using Shamir's Secret Sharing. Each share is encrypted
|
|
298
|
-
* with the corresponding member's public key.
|
|
299
|
-
*
|
|
300
|
-
* @param req - Request containing document, member IDs, and optional threshold
|
|
301
|
-
* @returns Document ID and sealing metadata on success
|
|
302
|
-
*
|
|
303
|
-
* @example
|
|
304
|
-
* ```json
|
|
305
|
-
* // Request
|
|
306
|
-
* POST /api/quorum/documents/seal
|
|
307
|
-
* {
|
|
308
|
-
* "document": { "secret": "data", "value": 42 },
|
|
309
|
-
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
310
|
-
* "sharesRequired": 2
|
|
311
|
-
* }
|
|
312
|
-
*
|
|
313
|
-
* // Response
|
|
314
|
-
* {
|
|
315
|
-
* "message": "Document sealed successfully",
|
|
316
|
-
* "documentId": "doc123...",
|
|
317
|
-
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
318
|
-
* "sharesRequired": 2,
|
|
319
|
-
* "createdAt": "2025-01-16T10:00:00Z"
|
|
320
|
-
* }
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
this.handleSealDocument = async (req) => {
|
|
324
|
-
try {
|
|
325
|
-
const { document, memberIds, sharesRequired } = req.body;
|
|
326
|
-
// Validate required fields
|
|
327
|
-
if (document === undefined) {
|
|
328
|
-
return (0, errorResponse_1.validationError)('Missing required field: document');
|
|
329
|
-
}
|
|
330
|
-
if (!memberIds || !Array.isArray(memberIds) || memberIds.length < 2) {
|
|
331
|
-
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_MEMBER_COUNT, 'At least 2 member IDs are required for sealing');
|
|
332
|
-
}
|
|
333
|
-
// Validate threshold if provided
|
|
334
|
-
if (sharesRequired !== undefined) {
|
|
335
|
-
if (sharesRequired < 2) {
|
|
336
|
-
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_THRESHOLD, 'sharesRequired must be at least 2');
|
|
337
|
-
}
|
|
338
|
-
if (sharesRequired > memberIds.length) {
|
|
339
|
-
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_THRESHOLD, 'sharesRequired cannot exceed the number of members');
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// Get the agent (authenticated member) from session
|
|
343
|
-
const sessionsController = this.application.getController('sessions');
|
|
344
|
-
let agent;
|
|
345
|
-
try {
|
|
346
|
-
const sessionMember = sessionsController.getMemberFromSession(req.headers.authorization);
|
|
347
|
-
// Cast to GuidV4 member type - the session controller returns a generic Member
|
|
348
|
-
agent = sessionMember;
|
|
349
|
-
}
|
|
350
|
-
catch {
|
|
351
|
-
return (0, errorResponse_1.unauthorizedError)();
|
|
352
|
-
}
|
|
353
|
-
if (!agent) {
|
|
354
|
-
return (0, errorResponse_1.unauthorizedError)();
|
|
355
|
-
}
|
|
356
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
357
|
-
const result = await quorumService.sealDocument(agent, document, memberIds, sharesRequired);
|
|
358
|
-
return {
|
|
359
|
-
statusCode: 201,
|
|
360
|
-
response: {
|
|
361
|
-
message: 'Document sealed successfully',
|
|
362
|
-
documentId: result.documentId,
|
|
363
|
-
memberIds: result.memberIds,
|
|
364
|
-
sharesRequired: result.sharesRequired,
|
|
365
|
-
createdAt: result.createdAt.toISOString(),
|
|
366
|
-
},
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
catch (_error) {
|
|
370
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
371
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
372
|
-
}
|
|
373
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
/**
|
|
377
|
-
* POST /api/quorum/documents/:documentId/unseal
|
|
378
|
-
* Unseal a document using member credentials (mnemonics).
|
|
379
|
-
*
|
|
380
|
-
* Recovers member private keys from provided mnemonics, then uses them
|
|
381
|
-
* to decrypt shares and reconstruct the original document.
|
|
382
|
-
*
|
|
383
|
-
* Note: This endpoint requires the QuorumService to have the document
|
|
384
|
-
* loaded in memory. For disk-persisted documents, the full reconstruction
|
|
385
|
-
* is not yet implemented.
|
|
386
|
-
*
|
|
387
|
-
* @param req - Request containing document ID and member credentials
|
|
388
|
-
* @returns Original document on success
|
|
389
|
-
*
|
|
390
|
-
* @example
|
|
391
|
-
* ```json
|
|
392
|
-
* // Request
|
|
393
|
-
* POST /api/quorum/documents/doc123.../unseal
|
|
394
|
-
* {
|
|
395
|
-
* "memberCredentials": [
|
|
396
|
-
* { "memberId": "member1...", "mnemonic": "word1 word2 word3..." },
|
|
397
|
-
* { "memberId": "member2...", "mnemonic": "word4 word5 word6..." }
|
|
398
|
-
* ]
|
|
399
|
-
* }
|
|
400
|
-
*
|
|
401
|
-
* // Response
|
|
402
|
-
* {
|
|
403
|
-
* "message": "Document unsealed successfully",
|
|
404
|
-
* "document": { "secret": "data", "value": 42 }
|
|
405
|
-
* }
|
|
406
|
-
* ```
|
|
407
|
-
*/
|
|
408
|
-
this.handleUnsealDocument = async (req) => {
|
|
409
|
-
try {
|
|
410
|
-
const { documentId } = req.params;
|
|
411
|
-
const { memberCredentials } = req
|
|
412
|
-
.body;
|
|
413
|
-
if (!documentId) {
|
|
414
|
-
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
415
|
-
}
|
|
416
|
-
if (!memberCredentials ||
|
|
417
|
-
!Array.isArray(memberCredentials) ||
|
|
418
|
-
memberCredentials.length === 0) {
|
|
419
|
-
return (0, errorResponse_1.validationError)('Missing required field: memberCredentials (array of {memberId, mnemonic})');
|
|
420
|
-
}
|
|
421
|
-
// Validate each credential has required fields
|
|
422
|
-
for (const cred of memberCredentials) {
|
|
423
|
-
if (!cred.memberId || !cred.mnemonic) {
|
|
424
|
-
return (0, errorResponse_1.validationError)('Each memberCredential must have memberId and mnemonic');
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
428
|
-
// Check if document exists and if we have enough shares
|
|
429
|
-
const docInfo = await quorumService.getDocument(documentId);
|
|
430
|
-
if (!docInfo) {
|
|
431
|
-
return (0, errorResponse_1.notFoundError)('Document', documentId);
|
|
432
|
-
}
|
|
433
|
-
// Check if provided members can unlock the document
|
|
434
|
-
const memberIds = memberCredentials.map((c) => c.memberId);
|
|
435
|
-
const canUnlockResult = await quorumService.canUnlock(documentId, memberIds);
|
|
436
|
-
if (!canUnlockResult.canUnlock) {
|
|
437
|
-
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INSUFFICIENT_SHARES, `Insufficient shares: provided ${canUnlockResult.sharesProvided}, required ${canUnlockResult.sharesRequired}`);
|
|
438
|
-
}
|
|
439
|
-
// Recover members with private keys from mnemonics
|
|
440
|
-
const eciesService = brightchain_lib_1.ServiceProvider.getInstance().eciesService;
|
|
441
|
-
const membersWithPrivateKey = [];
|
|
442
|
-
for (const cred of memberCredentials) {
|
|
443
|
-
// Get the stored member info to get their metadata
|
|
444
|
-
const storedMember = await quorumService.getMember(cred.memberId);
|
|
445
|
-
if (!storedMember) {
|
|
446
|
-
return (0, errorResponse_1.notFoundError)('Member', cred.memberId);
|
|
447
|
-
}
|
|
448
|
-
// Recover wallet from mnemonic - need to wrap string in SecureString
|
|
449
|
-
const secureString = new ecies_lib_1.SecureString(cred.mnemonic);
|
|
450
|
-
const { wallet } = eciesService.walletAndSeedFromMnemonic(secureString);
|
|
451
|
-
const recoveredPublicKey = new Uint8Array(wallet.getPublicKey());
|
|
452
|
-
// Verify the recovered public key matches the stored one
|
|
453
|
-
if ((0, ecies_lib_1.uint8ArrayToHex)(recoveredPublicKey) !==
|
|
454
|
-
(0, ecies_lib_1.uint8ArrayToHex)(storedMember.publicKey)) {
|
|
455
|
-
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.SHARE_DECRYPTION_FAILED, `Mnemonic does not match member ${cred.memberId}`);
|
|
456
|
-
}
|
|
457
|
-
// Create a member with the recovered private key
|
|
458
|
-
const email = storedMember.metadata.email
|
|
459
|
-
? new ecies_lib_1.EmailString(storedMember.metadata.email)
|
|
460
|
-
: new ecies_lib_1.EmailString(`${storedMember.metadata.name?.toLowerCase().replace(/\s+/g, '.') ?? 'unknown'}@placeholder.local`);
|
|
461
|
-
// Use Member.newMember and then replace the wallet
|
|
462
|
-
// This is a workaround since Member constructor may not accept wallet directly
|
|
463
|
-
const { member: tempMember } = ecies_lib_1.Member.newMember(eciesService, ecies_lib_1.MemberType.User, storedMember.metadata.name ?? 'Unknown', email);
|
|
464
|
-
// Create a member-like object with the recovered wallet's private key
|
|
465
|
-
// The unsealDocument method needs members with privateKey access
|
|
466
|
-
const recoveredMember = Object.create(tempMember);
|
|
467
|
-
Object.defineProperty(recoveredMember, 'privateKey', {
|
|
468
|
-
get: () => new Uint8Array(wallet.getPrivateKey()),
|
|
469
|
-
configurable: true,
|
|
470
|
-
});
|
|
471
|
-
Object.defineProperty(recoveredMember, 'publicKey', {
|
|
472
|
-
get: () => recoveredPublicKey,
|
|
473
|
-
configurable: true,
|
|
474
|
-
});
|
|
475
|
-
membersWithPrivateKey.push(recoveredMember);
|
|
476
|
-
}
|
|
477
|
-
// Unseal the document
|
|
478
|
-
const unsealedDocument = await quorumService.unsealDocument(documentId, membersWithPrivateKey);
|
|
479
|
-
return {
|
|
480
|
-
statusCode: 200,
|
|
481
|
-
response: {
|
|
482
|
-
message: 'Document unsealed successfully',
|
|
483
|
-
document: unsealedDocument,
|
|
484
|
-
},
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
catch (_error) {
|
|
488
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
489
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
490
|
-
}
|
|
491
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
/**
|
|
495
|
-
* GET /api/quorum/documents/:documentId
|
|
496
|
-
* Get metadata for a sealed document.
|
|
497
|
-
*
|
|
498
|
-
* Returns information about the document including which members have access
|
|
499
|
-
* and how many shares are required to unseal it.
|
|
500
|
-
*
|
|
501
|
-
* @param req - Request containing the document ID parameter
|
|
502
|
-
* @returns Document metadata on success, or 404 if not found
|
|
503
|
-
*
|
|
504
|
-
* @example
|
|
505
|
-
* ```json
|
|
506
|
-
* // Request
|
|
507
|
-
* GET /api/quorum/documents/doc123...
|
|
508
|
-
*
|
|
509
|
-
* // Response
|
|
510
|
-
* {
|
|
511
|
-
* "message": "Document retrieved successfully",
|
|
512
|
-
* "document": {
|
|
513
|
-
* "id": "doc123...",
|
|
514
|
-
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
515
|
-
* "sharesRequired": 2,
|
|
516
|
-
* "createdAt": "2025-01-16T10:00:00Z",
|
|
517
|
-
* "creatorId": "creator..."
|
|
518
|
-
* }
|
|
519
|
-
* }
|
|
520
|
-
* ```
|
|
521
|
-
*/
|
|
522
|
-
this.handleGetDocument = async (req) => {
|
|
523
|
-
try {
|
|
524
|
-
const { documentId } = req.params;
|
|
525
|
-
if (!documentId) {
|
|
526
|
-
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
527
|
-
}
|
|
528
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
529
|
-
const document = await quorumService.getDocument(documentId);
|
|
530
|
-
if (!document) {
|
|
531
|
-
return (0, errorResponse_1.notFoundError)('Document', documentId);
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
statusCode: 200,
|
|
535
|
-
response: {
|
|
536
|
-
message: 'Document retrieved successfully',
|
|
537
|
-
document,
|
|
538
|
-
},
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
catch (_error) {
|
|
542
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
543
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
544
|
-
}
|
|
545
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
/**
|
|
549
|
-
* GET /api/quorum/documents/:documentId/can-unlock
|
|
550
|
-
* Check if a set of members can unlock a document.
|
|
551
|
-
*
|
|
552
|
-
* Determines whether the provided members have enough shares to meet
|
|
553
|
-
* the threshold required to unseal the document.
|
|
554
|
-
*
|
|
555
|
-
* @param req - Request containing document ID and member IDs query parameter
|
|
556
|
-
* @returns Unlock status and share count information
|
|
557
|
-
*
|
|
558
|
-
* @example
|
|
559
|
-
* ```json
|
|
560
|
-
* // Request
|
|
561
|
-
* GET /api/quorum/documents/doc123.../can-unlock?memberIds=member1,member2
|
|
562
|
-
*
|
|
563
|
-
* // Response
|
|
564
|
-
* {
|
|
565
|
-
* "message": "Can-unlock check completed",
|
|
566
|
-
* "canUnlock": true,
|
|
567
|
-
* "sharesProvided": 2,
|
|
568
|
-
* "sharesRequired": 2,
|
|
569
|
-
* "missingMembers": ["member3..."]
|
|
570
|
-
* }
|
|
571
|
-
* ```
|
|
572
|
-
*/
|
|
573
|
-
this.handleCanUnlock = async (req) => {
|
|
574
|
-
try {
|
|
575
|
-
const { documentId } = req.params;
|
|
576
|
-
const { memberIds: memberIdsStr } = req
|
|
577
|
-
.query;
|
|
578
|
-
if (!documentId) {
|
|
579
|
-
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
580
|
-
}
|
|
581
|
-
if (!memberIdsStr) {
|
|
582
|
-
return (0, errorResponse_1.validationError)('Missing required query parameter: memberIds (comma-separated)');
|
|
583
|
-
}
|
|
584
|
-
const memberIds = memberIdsStr
|
|
585
|
-
.split(',')
|
|
586
|
-
.map((id) => id.trim());
|
|
587
|
-
const quorumService = this.quorumServiceWrapper.getService();
|
|
588
|
-
const result = await quorumService.canUnlock(documentId, memberIds);
|
|
589
|
-
return {
|
|
590
|
-
statusCode: 200,
|
|
591
|
-
response: {
|
|
592
|
-
message: 'Can-unlock check completed',
|
|
593
|
-
...result,
|
|
594
|
-
},
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
catch (_error) {
|
|
598
|
-
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
599
|
-
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
600
|
-
}
|
|
601
|
-
return (0, errorResponse_1.handleError)(_error);
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
121
|
this.quorumServiceWrapper = new quorum_1.QuorumServiceWrapper(application);
|
|
605
122
|
}
|
|
606
123
|
initRouteDefinitions() {
|
|
@@ -653,6 +170,489 @@ class QuorumController extends base_1.BaseController {
|
|
|
653
170
|
canUnlock: this.handleCanUnlock.bind(this),
|
|
654
171
|
};
|
|
655
172
|
}
|
|
173
|
+
// === Member Management Handlers ===
|
|
174
|
+
/**
|
|
175
|
+
* POST /api/quorum/members
|
|
176
|
+
* Add a new member to the quorum with generated cryptographic keys.
|
|
177
|
+
*
|
|
178
|
+
* Creates a new member with ECIES key pair and adds them to the quorum.
|
|
179
|
+
* The mnemonic phrase is returned for key recovery - it should be stored
|
|
180
|
+
* securely by the user as it cannot be retrieved later.
|
|
181
|
+
*
|
|
182
|
+
* @param req - Request containing member name and optional metadata
|
|
183
|
+
* @returns Member details and mnemonic on success
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```json
|
|
187
|
+
* // Request
|
|
188
|
+
* POST /api/quorum/members
|
|
189
|
+
* {
|
|
190
|
+
* "name": "Alice",
|
|
191
|
+
* "email": "alice@example.com",
|
|
192
|
+
* "role": "admin"
|
|
193
|
+
* }
|
|
194
|
+
*
|
|
195
|
+
* // Response
|
|
196
|
+
* {
|
|
197
|
+
* "message": "Member added successfully",
|
|
198
|
+
* "member": {
|
|
199
|
+
* "id": "abc123...",
|
|
200
|
+
* "publicKey": "04...",
|
|
201
|
+
* "metadata": { "name": "Alice", "email": "alice@example.com", "role": "admin" },
|
|
202
|
+
* "isActive": true,
|
|
203
|
+
* "createdAt": "2025-01-16T10:00:00Z",
|
|
204
|
+
* "updatedAt": "2025-01-16T10:00:00Z"
|
|
205
|
+
* },
|
|
206
|
+
* "mnemonic": "word1 word2 word3..."
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
async handleAddMember(req) {
|
|
211
|
+
try {
|
|
212
|
+
const { name, email, role } = req.body;
|
|
213
|
+
// Validate required fields
|
|
214
|
+
if (!name) {
|
|
215
|
+
return (0, errorResponse_1.validationError)('Missing required field: name');
|
|
216
|
+
}
|
|
217
|
+
// Create a new member with generated keys
|
|
218
|
+
const eciesService = brightchain_lib_1.ServiceProvider.getInstance().eciesService;
|
|
219
|
+
const emailString = email
|
|
220
|
+
? new ecies_lib_1.EmailString(email)
|
|
221
|
+
: new ecies_lib_1.EmailString(`${name.toLowerCase().replace(/\s+/g, '.')}@placeholder.local`);
|
|
222
|
+
const memberWithMnemonic = ecies_lib_1.Member.newMember(eciesService, ecies_lib_1.MemberType.User, name, emailString);
|
|
223
|
+
const metadata = {
|
|
224
|
+
name,
|
|
225
|
+
email,
|
|
226
|
+
role,
|
|
227
|
+
};
|
|
228
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
229
|
+
const quorumMember = await quorumService.addMember(memberWithMnemonic.member, metadata);
|
|
230
|
+
return {
|
|
231
|
+
statusCode: 201,
|
|
232
|
+
response: {
|
|
233
|
+
message: 'Member added successfully',
|
|
234
|
+
member: serializeMember(quorumMember),
|
|
235
|
+
mnemonic: memberWithMnemonic.mnemonic.value ?? '',
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (_error) {
|
|
240
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
241
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
242
|
+
}
|
|
243
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* GET /api/quorum/members
|
|
248
|
+
* List all active quorum members.
|
|
249
|
+
*
|
|
250
|
+
* Returns all members that are currently active in the quorum.
|
|
251
|
+
* Inactive (removed) members are not included in the response.
|
|
252
|
+
*
|
|
253
|
+
* @param _req - Request (no parameters required)
|
|
254
|
+
* @returns Array of member objects on success
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```json
|
|
258
|
+
* // Request
|
|
259
|
+
* GET /api/quorum/members
|
|
260
|
+
*
|
|
261
|
+
* // Response
|
|
262
|
+
* {
|
|
263
|
+
* "message": "Members retrieved successfully",
|
|
264
|
+
* "members": [
|
|
265
|
+
* {
|
|
266
|
+
* "id": "abc123...",
|
|
267
|
+
* "publicKey": "04...",
|
|
268
|
+
* "metadata": { "name": "Alice", "role": "admin" },
|
|
269
|
+
* "isActive": true,
|
|
270
|
+
* "createdAt": "2025-01-16T10:00:00Z",
|
|
271
|
+
* "updatedAt": "2025-01-16T10:00:00Z"
|
|
272
|
+
* }
|
|
273
|
+
* ]
|
|
274
|
+
* }
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
async handleListMembers(_req) {
|
|
278
|
+
try {
|
|
279
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
280
|
+
const members = await quorumService.listMembers();
|
|
281
|
+
return {
|
|
282
|
+
statusCode: 200,
|
|
283
|
+
response: {
|
|
284
|
+
message: 'Members retrieved successfully',
|
|
285
|
+
members: members.map(serializeMember),
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (_error) {
|
|
290
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
291
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
292
|
+
}
|
|
293
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* DELETE /api/quorum/members/:memberId
|
|
298
|
+
* Remove a member from the quorum (deactivate).
|
|
299
|
+
*
|
|
300
|
+
* Marks the member as inactive. The member's access to existing documents
|
|
301
|
+
* they are part of is preserved, but they cannot be added to new documents.
|
|
302
|
+
*
|
|
303
|
+
* @param req - Request containing the member ID parameter
|
|
304
|
+
* @returns Success confirmation on success, or 404 if not found
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```json
|
|
308
|
+
* // Request
|
|
309
|
+
* DELETE /api/quorum/members/abc123...
|
|
310
|
+
*
|
|
311
|
+
* // Response
|
|
312
|
+
* {
|
|
313
|
+
* "message": "Member removed successfully",
|
|
314
|
+
* "success": true,
|
|
315
|
+
* "memberId": "abc123..."
|
|
316
|
+
* }
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
async handleRemoveMember(req) {
|
|
320
|
+
try {
|
|
321
|
+
const { memberId } = req.params;
|
|
322
|
+
if (!memberId) {
|
|
323
|
+
return (0, errorResponse_1.validationError)('Missing required parameter: memberId');
|
|
324
|
+
}
|
|
325
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
326
|
+
await quorumService.removeMember(memberId);
|
|
327
|
+
return {
|
|
328
|
+
statusCode: 200,
|
|
329
|
+
response: {
|
|
330
|
+
message: 'Member removed successfully',
|
|
331
|
+
success: true,
|
|
332
|
+
memberId,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (_error) {
|
|
337
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
338
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
339
|
+
}
|
|
340
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// === Document Sealing/Unsealing Handlers ===
|
|
344
|
+
/**
|
|
345
|
+
* POST /api/quorum/documents/seal
|
|
346
|
+
* Seal a document using Shamir's Secret Sharing.
|
|
347
|
+
*
|
|
348
|
+
* Encrypts the document with a randomly generated symmetric key, then splits
|
|
349
|
+
* the key into shares using Shamir's Secret Sharing. Each share is encrypted
|
|
350
|
+
* with the corresponding member's public key.
|
|
351
|
+
*
|
|
352
|
+
* @param req - Request containing document, member IDs, and optional threshold
|
|
353
|
+
* @returns Document ID and sealing metadata on success
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```json
|
|
357
|
+
* // Request
|
|
358
|
+
* POST /api/quorum/documents/seal
|
|
359
|
+
* {
|
|
360
|
+
* "document": { "secret": "data", "value": 42 },
|
|
361
|
+
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
362
|
+
* "sharesRequired": 2
|
|
363
|
+
* }
|
|
364
|
+
*
|
|
365
|
+
* // Response
|
|
366
|
+
* {
|
|
367
|
+
* "message": "Document sealed successfully",
|
|
368
|
+
* "documentId": "doc123...",
|
|
369
|
+
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
370
|
+
* "sharesRequired": 2,
|
|
371
|
+
* "createdAt": "2025-01-16T10:00:00Z"
|
|
372
|
+
* }
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
async handleSealDocument(req) {
|
|
376
|
+
try {
|
|
377
|
+
const { document, memberIds, sharesRequired } = req.body;
|
|
378
|
+
// Validate required fields
|
|
379
|
+
if (document === undefined) {
|
|
380
|
+
return (0, errorResponse_1.validationError)('Missing required field: document');
|
|
381
|
+
}
|
|
382
|
+
if (!memberIds || !Array.isArray(memberIds) || memberIds.length < 2) {
|
|
383
|
+
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_MEMBER_COUNT, 'At least 2 member IDs are required for sealing');
|
|
384
|
+
}
|
|
385
|
+
// Validate threshold if provided
|
|
386
|
+
if (sharesRequired !== undefined) {
|
|
387
|
+
if (sharesRequired < 2) {
|
|
388
|
+
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_THRESHOLD, 'sharesRequired must be at least 2');
|
|
389
|
+
}
|
|
390
|
+
if (sharesRequired > memberIds.length) {
|
|
391
|
+
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INVALID_THRESHOLD, 'sharesRequired cannot exceed the number of members');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Get the agent (authenticated member) from session
|
|
395
|
+
const sessionsController = this.application.getController('sessions');
|
|
396
|
+
let agent;
|
|
397
|
+
try {
|
|
398
|
+
const sessionMember = sessionsController.getMemberFromSession(req.headers.authorization);
|
|
399
|
+
// Cast to GuidV4 member type - the session controller returns a generic Member
|
|
400
|
+
agent = sessionMember;
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return (0, errorResponse_1.unauthorizedError)();
|
|
404
|
+
}
|
|
405
|
+
if (!agent) {
|
|
406
|
+
return (0, errorResponse_1.unauthorizedError)();
|
|
407
|
+
}
|
|
408
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
409
|
+
const result = await quorumService.sealDocument(agent, document, memberIds, sharesRequired);
|
|
410
|
+
return {
|
|
411
|
+
statusCode: 201,
|
|
412
|
+
response: {
|
|
413
|
+
message: 'Document sealed successfully',
|
|
414
|
+
documentId: result.documentId,
|
|
415
|
+
memberIds: result.memberIds,
|
|
416
|
+
sharesRequired: result.sharesRequired,
|
|
417
|
+
createdAt: result.createdAt.toISOString(),
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
catch (_error) {
|
|
422
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
423
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
424
|
+
}
|
|
425
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* POST /api/quorum/documents/:documentId/unseal
|
|
430
|
+
* Unseal a document using member credentials (mnemonics).
|
|
431
|
+
*
|
|
432
|
+
* Recovers member private keys from provided mnemonics, then uses them
|
|
433
|
+
* to decrypt shares and reconstruct the original document.
|
|
434
|
+
*
|
|
435
|
+
* Note: This endpoint requires the QuorumService to have the document
|
|
436
|
+
* loaded in memory. For disk-persisted documents, the full reconstruction
|
|
437
|
+
* is not yet implemented.
|
|
438
|
+
*
|
|
439
|
+
* @param req - Request containing document ID and member credentials
|
|
440
|
+
* @returns Original document on success
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```json
|
|
444
|
+
* // Request
|
|
445
|
+
* POST /api/quorum/documents/doc123.../unseal
|
|
446
|
+
* {
|
|
447
|
+
* "memberCredentials": [
|
|
448
|
+
* { "memberId": "member1...", "mnemonic": "word1 word2 word3..." },
|
|
449
|
+
* { "memberId": "member2...", "mnemonic": "word4 word5 word6..." }
|
|
450
|
+
* ]
|
|
451
|
+
* }
|
|
452
|
+
*
|
|
453
|
+
* // Response
|
|
454
|
+
* {
|
|
455
|
+
* "message": "Document unsealed successfully",
|
|
456
|
+
* "document": { "secret": "data", "value": 42 }
|
|
457
|
+
* }
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
async handleUnsealDocument(req) {
|
|
461
|
+
try {
|
|
462
|
+
const { documentId } = req.params;
|
|
463
|
+
const { memberCredentials } = req
|
|
464
|
+
.body;
|
|
465
|
+
if (!documentId) {
|
|
466
|
+
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
467
|
+
}
|
|
468
|
+
if (!memberCredentials ||
|
|
469
|
+
!Array.isArray(memberCredentials) ||
|
|
470
|
+
memberCredentials.length === 0) {
|
|
471
|
+
return (0, errorResponse_1.validationError)('Missing required field: memberCredentials (array of {memberId, mnemonic})');
|
|
472
|
+
}
|
|
473
|
+
// Validate each credential has required fields
|
|
474
|
+
for (const cred of memberCredentials) {
|
|
475
|
+
if (!cred.memberId || !cred.mnemonic) {
|
|
476
|
+
return (0, errorResponse_1.validationError)('Each memberCredential must have memberId and mnemonic');
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
480
|
+
// Check if document exists and if we have enough shares
|
|
481
|
+
const docInfo = await quorumService.getDocument(documentId);
|
|
482
|
+
if (!docInfo) {
|
|
483
|
+
return (0, errorResponse_1.notFoundError)('Document', documentId);
|
|
484
|
+
}
|
|
485
|
+
// Check if provided members can unlock the document
|
|
486
|
+
const memberIds = memberCredentials.map((c) => c.memberId);
|
|
487
|
+
const canUnlockResult = await quorumService.canUnlock(documentId, memberIds);
|
|
488
|
+
if (!canUnlockResult.canUnlock) {
|
|
489
|
+
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.INSUFFICIENT_SHARES, `Insufficient shares: provided ${canUnlockResult.sharesProvided}, required ${canUnlockResult.sharesRequired}`);
|
|
490
|
+
}
|
|
491
|
+
// Recover members with private keys from mnemonics
|
|
492
|
+
const eciesService = brightchain_lib_1.ServiceProvider.getInstance().eciesService;
|
|
493
|
+
const membersWithPrivateKey = [];
|
|
494
|
+
for (const cred of memberCredentials) {
|
|
495
|
+
// Get the stored member info to get their metadata
|
|
496
|
+
const storedMember = await quorumService.getMember(cred.memberId);
|
|
497
|
+
if (!storedMember) {
|
|
498
|
+
return (0, errorResponse_1.notFoundError)('Member', cred.memberId);
|
|
499
|
+
}
|
|
500
|
+
// Recover wallet from mnemonic - need to wrap string in SecureString
|
|
501
|
+
const secureString = new ecies_lib_1.SecureString(cred.mnemonic);
|
|
502
|
+
const { wallet } = eciesService.walletAndSeedFromMnemonic(secureString);
|
|
503
|
+
const recoveredPublicKey = new Uint8Array(wallet.getPublicKey());
|
|
504
|
+
// Verify the recovered public key matches the stored one
|
|
505
|
+
if ((0, ecies_lib_1.uint8ArrayToHex)(recoveredPublicKey) !==
|
|
506
|
+
(0, ecies_lib_1.uint8ArrayToHex)(storedMember.publicKey)) {
|
|
507
|
+
return (0, errorResponse_1.createApiErrorResult)(400, errorResponse_1.ErrorCode.SHARE_DECRYPTION_FAILED, `Mnemonic does not match member ${cred.memberId}`);
|
|
508
|
+
}
|
|
509
|
+
// Create a member with the recovered private key
|
|
510
|
+
const email = storedMember.metadata.email
|
|
511
|
+
? new ecies_lib_1.EmailString(storedMember.metadata.email)
|
|
512
|
+
: new ecies_lib_1.EmailString(`${storedMember.metadata.name?.toLowerCase().replace(/\s+/g, '.') ?? 'unknown'}@placeholder.local`);
|
|
513
|
+
// Use Member.newMember and then replace the wallet
|
|
514
|
+
// This is a workaround since Member constructor may not accept wallet directly
|
|
515
|
+
const { member: tempMember } = ecies_lib_1.Member.newMember(eciesService, ecies_lib_1.MemberType.User, storedMember.metadata.name ?? 'Unknown', email);
|
|
516
|
+
// Create a member-like object with the recovered wallet's private key
|
|
517
|
+
// The unsealDocument method needs members with privateKey access
|
|
518
|
+
const recoveredMember = Object.create(tempMember);
|
|
519
|
+
Object.defineProperty(recoveredMember, 'privateKey', {
|
|
520
|
+
get: () => new Uint8Array(wallet.getPrivateKey()),
|
|
521
|
+
configurable: true,
|
|
522
|
+
});
|
|
523
|
+
Object.defineProperty(recoveredMember, 'publicKey', {
|
|
524
|
+
get: () => recoveredPublicKey,
|
|
525
|
+
configurable: true,
|
|
526
|
+
});
|
|
527
|
+
membersWithPrivateKey.push(recoveredMember);
|
|
528
|
+
}
|
|
529
|
+
// Unseal the document
|
|
530
|
+
const unsealedDocument = await quorumService.unsealDocument(documentId, membersWithPrivateKey);
|
|
531
|
+
return {
|
|
532
|
+
statusCode: 200,
|
|
533
|
+
response: {
|
|
534
|
+
message: 'Document unsealed successfully',
|
|
535
|
+
document: unsealedDocument,
|
|
536
|
+
},
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
catch (_error) {
|
|
540
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
541
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
542
|
+
}
|
|
543
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* GET /api/quorum/documents/:documentId
|
|
548
|
+
* Get metadata for a sealed document.
|
|
549
|
+
*
|
|
550
|
+
* Returns information about the document including which members have access
|
|
551
|
+
* and how many shares are required to unseal it.
|
|
552
|
+
*
|
|
553
|
+
* @param req - Request containing the document ID parameter
|
|
554
|
+
* @returns Document metadata on success, or 404 if not found
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* ```json
|
|
558
|
+
* // Request
|
|
559
|
+
* GET /api/quorum/documents/doc123...
|
|
560
|
+
*
|
|
561
|
+
* // Response
|
|
562
|
+
* {
|
|
563
|
+
* "message": "Document retrieved successfully",
|
|
564
|
+
* "document": {
|
|
565
|
+
* "id": "doc123...",
|
|
566
|
+
* "memberIds": ["member1...", "member2...", "member3..."],
|
|
567
|
+
* "sharesRequired": 2,
|
|
568
|
+
* "createdAt": "2025-01-16T10:00:00Z",
|
|
569
|
+
* "creatorId": "creator..."
|
|
570
|
+
* }
|
|
571
|
+
* }
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
async handleGetDocument(req) {
|
|
575
|
+
try {
|
|
576
|
+
const { documentId } = req.params;
|
|
577
|
+
if (!documentId) {
|
|
578
|
+
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
579
|
+
}
|
|
580
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
581
|
+
const document = await quorumService.getDocument(documentId);
|
|
582
|
+
if (!document) {
|
|
583
|
+
return (0, errorResponse_1.notFoundError)('Document', documentId);
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
statusCode: 200,
|
|
587
|
+
response: {
|
|
588
|
+
message: 'Document retrieved successfully',
|
|
589
|
+
document,
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
catch (_error) {
|
|
594
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
595
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
596
|
+
}
|
|
597
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* GET /api/quorum/documents/:documentId/can-unlock
|
|
602
|
+
* Check if a set of members can unlock a document.
|
|
603
|
+
*
|
|
604
|
+
* Determines whether the provided members have enough shares to meet
|
|
605
|
+
* the threshold required to unseal the document.
|
|
606
|
+
*
|
|
607
|
+
* @param req - Request containing document ID and member IDs query parameter
|
|
608
|
+
* @returns Unlock status and share count information
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```json
|
|
612
|
+
* // Request
|
|
613
|
+
* GET /api/quorum/documents/doc123.../can-unlock?memberIds=member1,member2
|
|
614
|
+
*
|
|
615
|
+
* // Response
|
|
616
|
+
* {
|
|
617
|
+
* "message": "Can-unlock check completed",
|
|
618
|
+
* "canUnlock": true,
|
|
619
|
+
* "sharesProvided": 2,
|
|
620
|
+
* "sharesRequired": 2,
|
|
621
|
+
* "missingMembers": ["member3..."]
|
|
622
|
+
* }
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
async handleCanUnlock(req) {
|
|
626
|
+
try {
|
|
627
|
+
const { documentId } = req.params;
|
|
628
|
+
const { memberIds: memberIdsStr } = req
|
|
629
|
+
.query;
|
|
630
|
+
if (!documentId) {
|
|
631
|
+
return (0, errorResponse_1.validationError)('Missing required parameter: documentId');
|
|
632
|
+
}
|
|
633
|
+
if (!memberIdsStr) {
|
|
634
|
+
return (0, errorResponse_1.validationError)('Missing required query parameter: memberIds (comma-separated)');
|
|
635
|
+
}
|
|
636
|
+
const memberIds = memberIdsStr
|
|
637
|
+
.split(',')
|
|
638
|
+
.map((id) => id.trim());
|
|
639
|
+
const quorumService = this.quorumServiceWrapper.getService();
|
|
640
|
+
const result = await quorumService.canUnlock(documentId, memberIds);
|
|
641
|
+
return {
|
|
642
|
+
statusCode: 200,
|
|
643
|
+
response: {
|
|
644
|
+
message: 'Can-unlock check completed',
|
|
645
|
+
...result,
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
catch (_error) {
|
|
650
|
+
if (_error instanceof brightchain_lib_1.QuorumError) {
|
|
651
|
+
return (0, errorResponse_1.mapQuorumError)(_error);
|
|
652
|
+
}
|
|
653
|
+
return (0, errorResponse_1.handleError)(_error);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
656
|
}
|
|
657
657
|
exports.QuorumController = QuorumController;
|
|
658
658
|
//# sourceMappingURL=quorum.js.map
|