mindoodb 0.0.85 → 0.0.88
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/README-server.md +6 -2
- package/README.md +6 -4
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +2 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +151 -12
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +340 -24
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/browser/index.mjs +14 -0
- package/dist/cloudflare/DurableObjectSql.d.ts +56 -0
- package/dist/cloudflare/DurableObjectSql.d.ts.map +1 -0
- package/dist/cloudflare/DurableObjectSql.js +180 -0
- package/dist/cloudflare/DurableObjectSql.js.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts +86 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.js +614 -0
- package/dist/cloudflare/MindooDatabaseObject.js.map +1 -0
- package/dist/cloudflare/MindooServerObject.d.ts +63 -0
- package/dist/cloudflare/MindooServerObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooServerObject.js +521 -0
- package/dist/cloudflare/MindooServerObject.js.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts +7 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js +14 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js.map +1 -0
- package/dist/cloudflare/host.d.ts +49 -0
- package/dist/cloudflare/host.d.ts.map +1 -0
- package/dist/cloudflare/host.js +104 -0
- package/dist/cloudflare/host.js.map +1 -0
- package/dist/cloudflare/http.d.ts +91 -0
- package/dist/cloudflare/http.d.ts.map +1 -0
- package/dist/cloudflare/http.js +266 -0
- package/dist/cloudflare/http.js.map +1 -0
- package/dist/cloudflare/index.d.ts +12 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +20 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/storeRpc.d.ts +22 -0
- package/dist/cloudflare/storeRpc.d.ts.map +1 -0
- package/dist/cloudflare/storeRpc.js +85 -0
- package/dist/cloudflare/storeRpc.js.map +1 -0
- package/dist/cloudflare/types.d.ts +101 -0
- package/dist/cloudflare/types.d.ts.map +1 -0
- package/dist/cloudflare/types.js +10 -0
- package/dist/cloudflare/types.js.map +1 -0
- package/dist/cloudflare/worker.d.ts +25 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +170 -0
- package/dist/cloudflare/worker.js.map +1 -0
- package/dist/core/BaseMindooDB.d.ts +95 -10
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +441 -98
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +43 -0
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +157 -6
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +103 -0
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +371 -8
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +2 -1
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/ChangeDocDraft.d.ts +133 -0
- package/dist/core/ChangeDocDraft.d.ts.map +1 -0
- package/dist/core/ChangeDocDraft.js +492 -0
- package/dist/core/ChangeDocDraft.js.map +1 -0
- package/dist/core/MindooDBServerAdmin.d.ts.map +1 -1
- package/dist/core/MindooDBServerAdmin.js +2 -1
- package/dist/core/MindooDBServerAdmin.js.map +1 -1
- package/dist/core/accesscontrol/directoryProjection.d.ts.map +1 -1
- package/dist/core/accesscontrol/directoryProjection.js +19 -0
- package/dist/core/accesscontrol/directoryProjection.js.map +1 -1
- package/dist/core/accesscontrol/evaluate.d.ts +14 -0
- package/dist/core/accesscontrol/evaluate.d.ts.map +1 -1
- package/dist/core/accesscontrol/evaluate.js +42 -1
- package/dist/core/accesscontrol/evaluate.js.map +1 -1
- package/dist/core/accesscontrol/types.d.ts +88 -4
- package/dist/core/accesscontrol/types.d.ts.map +1 -1
- package/dist/core/accesscontrol/types.js +114 -1
- package/dist/core/accesscontrol/types.js.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts +14 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.js +27 -7
- package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
- package/dist/core/appendonlystores/network/IrohNetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/IrohNetworkTransport.js +12 -0
- package/dist/core/appendonlystores/network/IrohNetworkTransport.js.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.js +3 -23
- package/dist/core/appendonlystores/network/IrohStreamIO.js.map +1 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts +6 -0
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js +20 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.d.ts.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js +3 -23
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts +7 -0
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js +39 -30
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts +37 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js +99 -40
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js.map +1 -1
- package/dist/core/appendonlystores/syncStores.d.ts.map +1 -1
- package/dist/core/appendonlystores/syncStores.js +25 -5
- package/dist/core/appendonlystores/syncStores.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +7 -1
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/types.js +1 -0
- package/dist/core/appendonlystores/types.js.map +1 -1
- package/dist/core/builtinDbInvariants.d.ts +49 -0
- package/dist/core/builtinDbInvariants.d.ts.map +1 -1
- package/dist/core/builtinDbInvariants.js +86 -1
- package/dist/core/builtinDbInvariants.js.map +1 -1
- package/dist/core/crypto/AuthChallenge.d.ts +68 -0
- package/dist/core/crypto/AuthChallenge.d.ts.map +1 -0
- package/dist/core/crypto/AuthChallenge.js +118 -0
- package/dist/core/crypto/AuthChallenge.js.map +1 -0
- package/dist/core/crypto/DomainStatement.d.ts +5 -3
- package/dist/core/crypto/DomainStatement.d.ts.map +1 -1
- package/dist/core/crypto/DomainStatement.js +5 -3
- package/dist/core/crypto/DomainStatement.js.map +1 -1
- package/dist/core/crypto/encryptedFields.d.ts +11 -0
- package/dist/core/crypto/encryptedFields.d.ts.map +1 -1
- package/dist/core/crypto/encryptedFields.js +18 -0
- package/dist/core/crypto/encryptedFields.js.map +1 -1
- package/dist/core/expressions/evaluateExpression.d.ts +8 -0
- package/dist/core/expressions/evaluateExpression.d.ts.map +1 -1
- package/dist/core/expressions/evaluateExpression.js +29 -4
- package/dist/core/expressions/evaluateExpression.js.map +1 -1
- package/dist/core/expressions/metadata.js +4 -4
- package/dist/core/expressions/metadata.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +17 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/query/executeQuery.d.ts.map +1 -1
- package/dist/core/query/executeQuery.js +5 -1
- package/dist/core/query/executeQuery.js.map +1 -1
- package/dist/core/query/executeQueryInclude.d.ts.map +1 -1
- package/dist/core/query/executeQueryInclude.js +2 -0
- package/dist/core/query/executeQueryInclude.js.map +1 -1
- package/dist/core/query/queryView.d.ts.map +1 -1
- package/dist/core/query/queryView.js +5 -1
- package/dist/core/query/queryView.js.map +1 -1
- package/dist/core/types.d.ts +204 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts.map +1 -1
- package/dist/core/uri/MindooURI.js +3 -24
- package/dist/core/uri/MindooURI.js.map +1 -1
- package/dist/core/userkeys/index.d.ts +1 -0
- package/dist/core/userkeys/index.d.ts.map +1 -1
- package/dist/core/userkeys/index.js +5 -1
- package/dist/core/userkeys/index.js.map +1 -1
- package/dist/core/userkeys/sealedValues.d.ts +54 -0
- package/dist/core/userkeys/sealedValues.d.ts.map +1 -0
- package/dist/core/userkeys/sealedValues.js +67 -0
- package/dist/core/userkeys/sealedValues.js.map +1 -0
- package/dist/core/utils/base64.d.ts +17 -0
- package/dist/core/utils/base64.d.ts.map +1 -0
- package/dist/core/utils/base64.js +54 -0
- package/dist/core/utils/base64.js.map +1 -0
- package/dist/core/utils/dynamicImportInterop.d.ts +10 -0
- package/dist/core/utils/dynamicImportInterop.d.ts.map +1 -0
- package/dist/core/utils/dynamicImportInterop.js +22 -0
- package/dist/core/utils/dynamicImportInterop.js.map +1 -0
- package/dist/core/utils/idGeneration.d.ts.map +1 -1
- package/dist/core/utils/idGeneration.js +14 -3
- package/dist/core/utils/idGeneration.js.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +7 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
- package/dist/node/server/FsServerDataStore.d.ts +23 -0
- package/dist/node/server/FsServerDataStore.d.ts.map +1 -0
- package/dist/node/server/FsServerDataStore.js +59 -0
- package/dist/node/server/FsServerDataStore.js.map +1 -0
- package/dist/node/server/IrohServerEndpoint.d.ts +1 -1
- package/dist/node/server/IrohServerEndpoint.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.d.ts +5 -5
- package/dist/node/server/IrohServerRpc.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.js +1 -1
- package/dist/node/server/IrohServerRpc.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +29 -150
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +160 -1573
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/PurgedDocRegistry.d.ts +4 -27
- package/dist/node/server/PurgedDocRegistry.d.ts.map +1 -1
- package/dist/node/server/PurgedDocRegistry.js +6 -83
- package/dist/node/server/PurgedDocRegistry.js.map +1 -1
- package/dist/node/server/StoreFactory.d.ts +1 -1
- package/dist/node/server/StoreFactory.d.ts.map +1 -1
- package/dist/node/server/TenantManager.d.ts +16 -284
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +35 -1168
- package/dist/node/server/TenantManager.js.map +1 -1
- package/dist/node/server/add-to-network.js +1 -1
- package/dist/node/server/add-to-network.js.map +1 -1
- package/dist/node/server/config.d.ts +11 -15
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +30 -245
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/expressAdapter.d.ts +23 -0
- package/dist/node/server/expressAdapter.d.ts.map +1 -0
- package/dist/node/server/expressAdapter.js +108 -0
- package/dist/node/server/expressAdapter.js.map +1 -0
- package/dist/node/server/nativeIrohStreamIO.d.ts +1 -1
- package/dist/node/server/nativeIrohStreamIO.d.ts.map +1 -1
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts +16 -0
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts.map +1 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js +39 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js.map +1 -0
- package/dist/node/server/peer/IrohPeerLink.d.ts +1 -1
- package/dist/node/server/peer/IrohPeerLink.d.ts.map +1 -1
- package/dist/node/server/peer/routes.d.ts +10 -44
- package/dist/node/server/peer/routes.d.ts.map +1 -1
- package/dist/node/server/peer/routes.js +33 -275
- package/dist/node/server/peer/routes.js.map +1 -1
- package/dist/node/server/resolveServerPassword.js +1 -1
- package/dist/node/server/resolveServerPassword.js.map +1 -1
- package/dist/node/server/server.js +1 -1
- package/dist/node/server/server.js.map +1 -1
- package/dist/node/server/serverinit.js +1 -1
- package/dist/node/server/serverinit.js.map +1 -1
- package/dist/server/CapabilityMatcher.d.ts +60 -0
- package/dist/server/CapabilityMatcher.d.ts.map +1 -0
- package/dist/server/CapabilityMatcher.js +138 -0
- package/dist/server/CapabilityMatcher.js.map +1 -0
- package/dist/server/DeviceDiscoveryService.d.ts +36 -0
- package/dist/server/DeviceDiscoveryService.d.ts.map +1 -0
- package/dist/server/DeviceDiscoveryService.js +164 -0
- package/dist/server/DeviceDiscoveryService.js.map +1 -0
- package/dist/server/PurgedDocRegistry.d.ts +35 -0
- package/dist/server/PurgedDocRegistry.d.ts.map +1 -0
- package/dist/server/PurgedDocRegistry.js +90 -0
- package/dist/server/PurgedDocRegistry.js.map +1 -0
- package/dist/server/ServerDataStore.d.ts +67 -0
- package/dist/server/ServerDataStore.d.ts.map +1 -0
- package/dist/server/ServerDataStore.js +88 -0
- package/dist/server/ServerDataStore.js.map +1 -0
- package/dist/server/SyncEventBus.d.ts +39 -0
- package/dist/server/SyncEventBus.d.ts.map +1 -0
- package/dist/server/SyncEventBus.js +41 -0
- package/dist/server/SyncEventBus.js.map +1 -0
- package/dist/server/SystemAdminAuth.d.ts +82 -0
- package/dist/server/SystemAdminAuth.d.ts.map +1 -0
- package/dist/server/SystemAdminAuth.js +227 -0
- package/dist/server/SystemAdminAuth.js.map +1 -0
- package/dist/server/TenantManager.d.ts +366 -0
- package/dist/server/TenantManager.d.ts.map +1 -0
- package/dist/server/TenantManager.js +1416 -0
- package/dist/server/TenantManager.js.map +1 -0
- package/dist/server/http/MindooDBServerCore.d.ts +313 -0
- package/dist/server/http/MindooDBServerCore.d.ts.map +1 -0
- package/dist/server/http/MindooDBServerCore.js +1410 -0
- package/dist/server/http/MindooDBServerCore.js.map +1 -0
- package/dist/server/http/types.d.ts +89 -0
- package/dist/server/http/types.d.ts.map +1 -0
- package/dist/server/http/types.js +29 -0
- package/dist/server/http/types.js.map +1 -0
- package/dist/server/index.d.ts +32 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +76 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/peer/ClusterAuditLog.d.ts +71 -0
- package/dist/server/peer/ClusterAuditLog.d.ts.map +1 -0
- package/dist/server/peer/ClusterAuditLog.js +81 -0
- package/dist/server/peer/ClusterAuditLog.js.map +1 -0
- package/dist/server/peer/ClusterJobRunner.d.ts +43 -0
- package/dist/server/peer/ClusterJobRunner.d.ts.map +1 -0
- package/dist/server/peer/ClusterJobRunner.js +119 -0
- package/dist/server/peer/ClusterJobRunner.js.map +1 -0
- package/dist/server/peer/ClusterManager.d.ts +149 -0
- package/dist/server/peer/ClusterManager.d.ts.map +1 -0
- package/dist/server/peer/ClusterManager.js +292 -0
- package/dist/server/peer/ClusterManager.js.map +1 -0
- package/dist/server/peer/PeerAuthService.d.ts +69 -0
- package/dist/server/peer/PeerAuthService.d.ts.map +1 -0
- package/dist/server/peer/PeerAuthService.js +207 -0
- package/dist/server/peer/PeerAuthService.js.map +1 -0
- package/dist/server/peer/PeerReplicator.d.ts +303 -0
- package/dist/server/peer/PeerReplicator.d.ts.map +1 -0
- package/dist/server/peer/PeerReplicator.js +1072 -0
- package/dist/server/peer/PeerReplicator.js.map +1 -0
- package/dist/server/peer/clusterHttp.d.ts +113 -0
- package/dist/server/peer/clusterHttp.d.ts.map +1 -0
- package/dist/server/peer/clusterHttp.js +290 -0
- package/dist/server/peer/clusterHttp.js.map +1 -0
- package/dist/server/peer/types.d.ts +244 -0
- package/dist/server/peer/types.d.ts.map +1 -0
- package/dist/server/peer/types.js +189 -0
- package/dist/server/peer/types.js.map +1 -0
- package/dist/server/rateLimits.d.ts +119 -0
- package/dist/server/rateLimits.d.ts.map +1 -0
- package/dist/server/rateLimits.js +159 -0
- package/dist/server/rateLimits.js.map +1 -0
- package/dist/server/serverConfig.d.ts +37 -0
- package/dist/server/serverConfig.d.ts.map +1 -0
- package/dist/server/serverConfig.js +251 -0
- package/dist/server/serverConfig.js.map +1 -0
- package/dist/server/timestampProxy.d.ts +129 -0
- package/dist/server/timestampProxy.d.ts.map +1 -0
- package/dist/server/timestampProxy.js +361 -0
- package/dist/server/timestampProxy.js.map +1 -0
- package/dist/server/types.d.ts +267 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +56 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/validation.d.ts +43 -0
- package/dist/server/validation.d.ts.map +1 -0
- package/dist/server/validation.js +104 -0
- package/dist/server/validation.js.map +1 -0
- package/package.json +9 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Job registry for the mutating cluster actions.
|
|
4
|
+
*
|
|
5
|
+
* Every `POST /system/cluster/actions/*` returns a `jobId` immediately instead
|
|
6
|
+
* of holding the connection open. A full mirror of a large tenant can run for
|
|
7
|
+
* minutes; a console that waits for the response would either time out at some
|
|
8
|
+
* proxy or force the operator to stare at a spinner with no idea what is
|
|
9
|
+
* happening. With a job the UI gets an id in milliseconds and then polls
|
|
10
|
+
* `GET /system/cluster/jobs/:id` for typed progress.
|
|
11
|
+
*
|
|
12
|
+
* Jobs live in memory. They describe an action in flight, not a durable
|
|
13
|
+
* promise: after a restart nothing is running, so there is nothing to report,
|
|
14
|
+
* and the audit log (not this registry) is what preserves the record that the
|
|
15
|
+
* action happened.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.ClusterJobRunner = void 0;
|
|
19
|
+
const idGeneration_1 = require("../../core/utils/idGeneration");
|
|
20
|
+
const types_1 = require("./types");
|
|
21
|
+
/** How many finished jobs to keep for polling before evicting the oldest. */
|
|
22
|
+
const DEFAULT_HISTORY_LIMIT = 200;
|
|
23
|
+
class ClusterJobRunner {
|
|
24
|
+
constructor(historyLimit = DEFAULT_HISTORY_LIMIT) {
|
|
25
|
+
this.historyLimit = historyLimit;
|
|
26
|
+
this.jobs = new Map();
|
|
27
|
+
this.order = [];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Register a job and start it. Returns as soon as the job is queued, so the
|
|
31
|
+
* caller can answer `202 Accepted` with the id.
|
|
32
|
+
*/
|
|
33
|
+
start(kind, scope, requestedBy, work) {
|
|
34
|
+
const job = {
|
|
35
|
+
id: (0, idGeneration_1.generateRandomUuid)(),
|
|
36
|
+
kind,
|
|
37
|
+
scope,
|
|
38
|
+
state: "queued",
|
|
39
|
+
createdAt: new Date().toISOString(),
|
|
40
|
+
startedAt: null,
|
|
41
|
+
finishedAt: null,
|
|
42
|
+
progress: { total: 0, completed: 0 },
|
|
43
|
+
result: null,
|
|
44
|
+
error: null,
|
|
45
|
+
errorClass: null,
|
|
46
|
+
requestedBy,
|
|
47
|
+
};
|
|
48
|
+
this.jobs.set(job.id, job);
|
|
49
|
+
this.order.push(job.id);
|
|
50
|
+
this.evictOverLimit();
|
|
51
|
+
void this.run(job, work);
|
|
52
|
+
return job;
|
|
53
|
+
}
|
|
54
|
+
async run(job, work) {
|
|
55
|
+
job.state = "running";
|
|
56
|
+
job.startedAt = new Date().toISOString();
|
|
57
|
+
try {
|
|
58
|
+
job.result = await work({
|
|
59
|
+
progress: (completed, total) => {
|
|
60
|
+
job.progress = { completed, total };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
job.state = "succeeded";
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
job.state = "failed";
|
|
67
|
+
job.error = error instanceof Error ? error.message : String(error);
|
|
68
|
+
job.errorClass = (0, types_1.classifyPeerError)(error);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
job.finishedAt = new Date().toISOString();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
get(id) {
|
|
75
|
+
return this.jobs.get(id) ?? null;
|
|
76
|
+
}
|
|
77
|
+
/** Newest first, which is the order a console wants to render. */
|
|
78
|
+
list(limit = 50) {
|
|
79
|
+
const ids = this.order.slice(-limit).reverse();
|
|
80
|
+
return ids
|
|
81
|
+
.map((id) => this.jobs.get(id))
|
|
82
|
+
.filter((job) => job !== undefined);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Wait for a job to settle. Only for tests — the HTTP surface always polls,
|
|
86
|
+
* because that is the contract the console is built against.
|
|
87
|
+
*/
|
|
88
|
+
async waitFor(id, timeoutMs = 30000) {
|
|
89
|
+
const deadline = Date.now() + timeoutMs;
|
|
90
|
+
for (;;) {
|
|
91
|
+
const job = this.jobs.get(id);
|
|
92
|
+
if (!job)
|
|
93
|
+
return null;
|
|
94
|
+
if (job.state === "succeeded" || job.state === "failed")
|
|
95
|
+
return job;
|
|
96
|
+
if (Date.now() > deadline)
|
|
97
|
+
return job;
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
evictOverLimit() {
|
|
102
|
+
let index = 0;
|
|
103
|
+
while (this.order.length > this.historyLimit && index < this.order.length) {
|
|
104
|
+
const id = this.order[index];
|
|
105
|
+
const job = this.jobs.get(id);
|
|
106
|
+
// Never drop a job that is still going: its id is in a caller's hands and
|
|
107
|
+
// they are polling for it. Skip past it and evict the next finished one,
|
|
108
|
+
// which keeps `order` oldest-to-newest.
|
|
109
|
+
if (job && (job.state === "queued" || job.state === "running")) {
|
|
110
|
+
index += 1;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
this.order.splice(index, 1);
|
|
114
|
+
this.jobs.delete(id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ClusterJobRunner = ClusterJobRunner;
|
|
119
|
+
//# sourceMappingURL=ClusterJobRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClusterJobRunner.js","sourceRoot":"","sources":["../../../src/server/peer/ClusterJobRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owns everything cluster-shaped on one node: the peer session authenticator,
|
|
3
|
+
* one {@link PeerReplicator} per trusted server that has a `url`, the job
|
|
4
|
+
* registry behind the mutating admin actions, and the audit log.
|
|
5
|
+
*
|
|
6
|
+
* `MindooDBServer` holds exactly one of these and routes both `/system/peer/*`
|
|
7
|
+
* (server-to-server protocol) and `/system/cluster/*` (admin surface) into it.
|
|
8
|
+
* Keeping the two prefixes on one object is fine — they never share an auth
|
|
9
|
+
* class, and the read model both need is the same in-memory replication state.
|
|
10
|
+
*/
|
|
11
|
+
import { StoreKind, type ContentAddressedStore } from "../../core/types";
|
|
12
|
+
import { Logger } from "../../core/logging";
|
|
13
|
+
import type { CryptoAdapter } from "../../core/crypto/CryptoAdapter";
|
|
14
|
+
import type { IrohStreamIO } from "../../core/appendonlystores/network/IrohStreamIO";
|
|
15
|
+
import type { TrustedServer } from "../types";
|
|
16
|
+
import type { SyncEventBus } from "../SyncEventBus";
|
|
17
|
+
import { PeerAuthService } from "./PeerAuthService";
|
|
18
|
+
import { PeerReplicator, type PeerIrohLink, type PeerTransferLimits } from "./PeerReplicator";
|
|
19
|
+
import { ClusterJobRunner } from "./ClusterJobRunner";
|
|
20
|
+
import { ClusterAuditLog, type ClusterAuditStorage } from "./ClusterAuditLog";
|
|
21
|
+
import { type ClusterJob, type ClusterJobKind, type ClusterJobScope, type ClusterStatusResponse, type ClusterTenantStatusResponse, type ClusterTopologyResponse, type PeerRole } from "./types";
|
|
22
|
+
/** Result of dispatching an admin action; `accepted` discriminates the union. */
|
|
23
|
+
export type ClusterActionOutcome = {
|
|
24
|
+
accepted: true;
|
|
25
|
+
job: ClusterJob;
|
|
26
|
+
} | {
|
|
27
|
+
accepted: false;
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
export interface ClusterManagerDeps {
|
|
31
|
+
cryptoAdapter: CryptoAdapter;
|
|
32
|
+
/** Where the audit log is kept (on Node `<dataDir>/cluster/audit.jsonl`). */
|
|
33
|
+
auditStorage: ClusterAuditStorage;
|
|
34
|
+
eventBus: SyncEventBus;
|
|
35
|
+
/** Trusted servers, re-read on every call so peer CRUD takes effect at once. */
|
|
36
|
+
listTrustedServers(): TrustedServer[];
|
|
37
|
+
/** This node's own name from `server.identity.json`, or null without identity. */
|
|
38
|
+
localServerName(): string | null;
|
|
39
|
+
/** This node's Ed25519 public key (PEM), how peers look it up. */
|
|
40
|
+
localSigningPublicKey(): string | null;
|
|
41
|
+
getSigningKey(): Promise<CryptoKey | undefined>;
|
|
42
|
+
getEncryptionKey(): Promise<CryptoKey | undefined>;
|
|
43
|
+
listTenants(): string[];
|
|
44
|
+
listDatabases(tenantId: string): string[] | Promise<string[]>;
|
|
45
|
+
getLocalStore(tenantId: string, dbId: string, storeKind: StoreKind): Promise<ContentAddressedStore>;
|
|
46
|
+
/** This node's mesh role; `peer` unless config says hub or spoke. */
|
|
47
|
+
localRole?: PeerRole;
|
|
48
|
+
/**
|
|
49
|
+
* Bound Iroh endpoint for outbound peer dials. `null` until Iroh is online
|
|
50
|
+
* (or when `iroh.enabled` is off). Replicators treat that as a reconnect.
|
|
51
|
+
*/
|
|
52
|
+
getIrohStreamIO?: () => IrohStreamIO | null;
|
|
53
|
+
/** Dial a peer over Iroh; without it `iroh:` peer urls cannot be replicated. */
|
|
54
|
+
openIrohLink?: (io: IrohStreamIO, ticket: string) => PeerIrohLink;
|
|
55
|
+
/** See {@link PeerReplicatorHost.liveEventStream}. */
|
|
56
|
+
liveEventStream?: boolean;
|
|
57
|
+
/** See {@link PeerReplicatorHost.transferLimits}. */
|
|
58
|
+
transferLimits?: PeerTransferLimits;
|
|
59
|
+
/**
|
|
60
|
+
* HMAC secret of peer tokens. Defaults to a random one per process; a host
|
|
61
|
+
* that validates peer tokens in several places passes a shared secret.
|
|
62
|
+
*/
|
|
63
|
+
peerJwtSecret?: Uint8Array;
|
|
64
|
+
/** See {@link PeerReplicatorHost.getPurgedDocIds}. */
|
|
65
|
+
getPurgedDocIds?(tenantId: string, dbId: string): ReadonlySet<string>;
|
|
66
|
+
/** See {@link PeerReplicatorHost.onDirectoryPulled}. */
|
|
67
|
+
onDirectoryPulled?(tenantId: string): Promise<void>;
|
|
68
|
+
logger?: Logger;
|
|
69
|
+
}
|
|
70
|
+
export declare class ClusterManager {
|
|
71
|
+
private readonly deps;
|
|
72
|
+
readonly auth: PeerAuthService;
|
|
73
|
+
readonly jobs: ClusterJobRunner;
|
|
74
|
+
readonly audit: ClusterAuditLog;
|
|
75
|
+
private readonly replicators;
|
|
76
|
+
private readonly logger;
|
|
77
|
+
private started;
|
|
78
|
+
/**
|
|
79
|
+
* Tenants each peer is allowed to hear about, from the last Bloom handshake
|
|
80
|
+
* that peer performed against us.
|
|
81
|
+
*
|
|
82
|
+
* Inbound state, distinct from a replicator's own outbound intersection: this
|
|
83
|
+
* is what *they* asked about, and it exists so the peer event stream can be
|
|
84
|
+
* filtered. Without it the feed would announce every tenant we host, which
|
|
85
|
+
* would hand a peer the tenant list the Bloom exchange is designed to keep
|
|
86
|
+
* from it.
|
|
87
|
+
*/
|
|
88
|
+
private readonly peerIntersections;
|
|
89
|
+
constructor(deps: ClusterManagerDeps);
|
|
90
|
+
private get localRole();
|
|
91
|
+
/**
|
|
92
|
+
* Bring the replicator set in line with `trusted-servers.json`.
|
|
93
|
+
*
|
|
94
|
+
* Called at startup and after any peer CRUD. Existing replicators are updated
|
|
95
|
+
* in place rather than recreated so their scan cursors and echo-suppression
|
|
96
|
+
* state survive an edit to, say, the attachment mode.
|
|
97
|
+
*/
|
|
98
|
+
syncReplicators(): void;
|
|
99
|
+
start(): void;
|
|
100
|
+
stop(): Promise<void>;
|
|
101
|
+
get autoSyncEnabled(): boolean;
|
|
102
|
+
getReplicator(name: string): PeerReplicator | null;
|
|
103
|
+
/**
|
|
104
|
+
* One replication pass with every peer, resolved when all are done. For
|
|
105
|
+
* hosts that drive replication from a schedule rather than from timers.
|
|
106
|
+
*/
|
|
107
|
+
syncAll(options?: {
|
|
108
|
+
deadline?: number;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
deferred: boolean;
|
|
111
|
+
}>;
|
|
112
|
+
/** Push a local change to every peer that takes it; see {@link PeerReplicator.replicateLocalChange}. */
|
|
113
|
+
replicateLocalChange(event: Parameters<PeerReplicator["replicateLocalChange"]>[0]): Promise<void>;
|
|
114
|
+
/** Record what a peer's Bloom handshake matched; see {@link peerIntersections}. */
|
|
115
|
+
recordPeerIntersection(peerName: string, tenantIds: string[]): void;
|
|
116
|
+
/**
|
|
117
|
+
* May this peer be told that something changed in this tenant?
|
|
118
|
+
*
|
|
119
|
+
* Closed by default: a peer that has not run the Bloom handshake in this
|
|
120
|
+
* process gets nothing, rather than everything.
|
|
121
|
+
*/
|
|
122
|
+
peerMaySeeTenant(peerName: string, tenantId: string): boolean;
|
|
123
|
+
topology(): ClusterTopologyResponse;
|
|
124
|
+
status(): ClusterStatusResponse;
|
|
125
|
+
tenantStatus(tenantId: string): ClusterTenantStatusResponse;
|
|
126
|
+
/**
|
|
127
|
+
* Dispatch an action as a job across the peers the scope selects.
|
|
128
|
+
*
|
|
129
|
+
* Records the request in the audit log first, with the job id, so an action
|
|
130
|
+
* that is issued is on record even if the node dies mid-run.
|
|
131
|
+
*/
|
|
132
|
+
runAction(kind: ClusterJobKind, scope: ClusterJobScope, requestedBy: string): ClusterActionOutcome;
|
|
133
|
+
private targetsFor;
|
|
134
|
+
/** Record a peer CRUD change; the caller has already persisted it. */
|
|
135
|
+
auditPeerChange(action: "peer-create" | "peer-update" | "peer-delete", peerName: string, actor: string): void;
|
|
136
|
+
/**
|
|
137
|
+
* Mirrors of one tenant, for `GET /.well-known/mindoodb-cluster`.
|
|
138
|
+
*
|
|
139
|
+
* Only peers that actually hold the requested tenant are listed — a client
|
|
140
|
+
* asking about tenant A must not learn that this server also replicates
|
|
141
|
+
* tenants B and C to some other node.
|
|
142
|
+
*/
|
|
143
|
+
mirrorsForTenant(tenantId: string): Array<{
|
|
144
|
+
name: string;
|
|
145
|
+
url: string;
|
|
146
|
+
role: PeerRole;
|
|
147
|
+
}>;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=ClusterManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClusterManager.d.ts","sourceRoot":"","sources":["../../../src/server/peer/ClusterManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Owns everything cluster-shaped on one node: the peer session authenticator,
|
|
4
|
+
* one {@link PeerReplicator} per trusted server that has a `url`, the job
|
|
5
|
+
* registry behind the mutating admin actions, and the audit log.
|
|
6
|
+
*
|
|
7
|
+
* `MindooDBServer` holds exactly one of these and routes both `/system/peer/*`
|
|
8
|
+
* (server-to-server protocol) and `/system/cluster/*` (admin surface) into it.
|
|
9
|
+
* Keeping the two prefixes on one object is fine — they never share an auth
|
|
10
|
+
* class, and the read model both need is the same in-memory replication state.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.ClusterManager = void 0;
|
|
14
|
+
const logging_1 = require("../../core/logging");
|
|
15
|
+
const PeerAuthService_1 = require("./PeerAuthService");
|
|
16
|
+
const PeerReplicator_1 = require("./PeerReplicator");
|
|
17
|
+
const ClusterJobRunner_1 = require("./ClusterJobRunner");
|
|
18
|
+
const ClusterAuditLog_1 = require("./ClusterAuditLog");
|
|
19
|
+
class ClusterManager {
|
|
20
|
+
constructor(deps) {
|
|
21
|
+
this.deps = deps;
|
|
22
|
+
this.jobs = new ClusterJobRunner_1.ClusterJobRunner();
|
|
23
|
+
this.replicators = new Map();
|
|
24
|
+
this.started = false;
|
|
25
|
+
/**
|
|
26
|
+
* Tenants each peer is allowed to hear about, from the last Bloom handshake
|
|
27
|
+
* that peer performed against us.
|
|
28
|
+
*
|
|
29
|
+
* Inbound state, distinct from a replicator's own outbound intersection: this
|
|
30
|
+
* is what *they* asked about, and it exists so the peer event stream can be
|
|
31
|
+
* filtered. Without it the feed would announce every tenant we host, which
|
|
32
|
+
* would hand a peer the tenant list the Bloom exchange is designed to keep
|
|
33
|
+
* from it.
|
|
34
|
+
*/
|
|
35
|
+
this.peerIntersections = new Map();
|
|
36
|
+
this.logger =
|
|
37
|
+
deps.logger?.createChild("Cluster") ??
|
|
38
|
+
new logging_1.MindooLogger((0, logging_1.getDefaultLogLevel)(), "Cluster", true);
|
|
39
|
+
this.auth = new PeerAuthService_1.PeerAuthService(deps.cryptoAdapter, () => deps.listTrustedServers(), deps.peerJwtSecret ? { jwtSecret: deps.peerJwtSecret } : undefined);
|
|
40
|
+
this.audit = new ClusterAuditLog_1.ClusterAuditLog(deps.auditStorage);
|
|
41
|
+
}
|
|
42
|
+
get localRole() {
|
|
43
|
+
return this.deps.localRole ?? "peer";
|
|
44
|
+
}
|
|
45
|
+
// ------------------------------------------------------------- lifecycle
|
|
46
|
+
/**
|
|
47
|
+
* Bring the replicator set in line with `trusted-servers.json`.
|
|
48
|
+
*
|
|
49
|
+
* Called at startup and after any peer CRUD. Existing replicators are updated
|
|
50
|
+
* in place rather than recreated so their scan cursors and echo-suppression
|
|
51
|
+
* state survive an edit to, say, the attachment mode.
|
|
52
|
+
*/
|
|
53
|
+
syncReplicators() {
|
|
54
|
+
const configured = new Map(this.deps
|
|
55
|
+
.listTrustedServers()
|
|
56
|
+
.filter((peer) => typeof peer.url === "string" && peer.url.length > 0)
|
|
57
|
+
.map((peer) => [peer.name, peer]));
|
|
58
|
+
for (const [name, replicator] of this.replicators) {
|
|
59
|
+
if (!configured.has(name)) {
|
|
60
|
+
void replicator.stop();
|
|
61
|
+
this.replicators.delete(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const [name, peer] of configured) {
|
|
65
|
+
const existing = this.replicators.get(name);
|
|
66
|
+
if (existing) {
|
|
67
|
+
existing.updatePeer(peer);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const replicator = new PeerReplicator_1.PeerReplicator(peer, {
|
|
71
|
+
cryptoAdapter: this.deps.cryptoAdapter,
|
|
72
|
+
localServerName: this.deps.localServerName() ?? "",
|
|
73
|
+
localSigningPublicKey: this.deps.localSigningPublicKey() ?? "",
|
|
74
|
+
localRole: this.localRole,
|
|
75
|
+
getSigningKey: () => this.deps.getSigningKey(),
|
|
76
|
+
getEncryptionKey: () => this.deps.getEncryptionKey(),
|
|
77
|
+
listTenants: () => this.deps.listTenants(),
|
|
78
|
+
listDatabases: (tenantId) => this.deps.listDatabases(tenantId),
|
|
79
|
+
getLocalStore: (tenantId, dbId, storeKind) => this.deps.getLocalStore(tenantId, dbId, storeKind),
|
|
80
|
+
eventBus: this.deps.eventBus,
|
|
81
|
+
getIrohStreamIO: this.deps.getIrohStreamIO,
|
|
82
|
+
openIrohLink: this.deps.openIrohLink,
|
|
83
|
+
liveEventStream: this.deps.liveEventStream,
|
|
84
|
+
transferLimits: this.deps.transferLimits,
|
|
85
|
+
getPurgedDocIds: this.deps.getPurgedDocIds?.bind(this.deps),
|
|
86
|
+
onDirectoryPulled: this.deps.onDirectoryPulled?.bind(this.deps),
|
|
87
|
+
logger: this.logger,
|
|
88
|
+
});
|
|
89
|
+
this.replicators.set(name, replicator);
|
|
90
|
+
if (this.started)
|
|
91
|
+
replicator.start();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
start() {
|
|
95
|
+
if (this.started)
|
|
96
|
+
return;
|
|
97
|
+
if (!this.deps.localServerName()) {
|
|
98
|
+
this.logger.info("No server identity; peer replication stays off");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.started = true;
|
|
102
|
+
this.syncReplicators();
|
|
103
|
+
for (const replicator of this.replicators.values()) {
|
|
104
|
+
replicator.start();
|
|
105
|
+
}
|
|
106
|
+
this.logger.info(`Peer replication started for ${this.replicators.size} peer(s)`);
|
|
107
|
+
}
|
|
108
|
+
async stop() {
|
|
109
|
+
this.started = false;
|
|
110
|
+
await Promise.all([...this.replicators.values()].map((peer) => peer.stop()));
|
|
111
|
+
this.replicators.clear();
|
|
112
|
+
}
|
|
113
|
+
get autoSyncEnabled() {
|
|
114
|
+
return this.started;
|
|
115
|
+
}
|
|
116
|
+
getReplicator(name) {
|
|
117
|
+
return this.replicators.get(name) ?? null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* One replication pass with every peer, resolved when all are done. For
|
|
121
|
+
* hosts that drive replication from a schedule rather than from timers.
|
|
122
|
+
*/
|
|
123
|
+
async syncAll(options = {}) {
|
|
124
|
+
const runs = await Promise.all([...this.replicators.values()].map((replicator) => replicator.syncNow({ deadline: options.deadline }).catch(() => null)));
|
|
125
|
+
return { deferred: runs.some((run) => run?.deferred === true) };
|
|
126
|
+
}
|
|
127
|
+
/** Push a local change to every peer that takes it; see {@link PeerReplicator.replicateLocalChange}. */
|
|
128
|
+
async replicateLocalChange(event) {
|
|
129
|
+
await Promise.all([...this.replicators.values()].map((replicator) => replicator.replicateLocalChange(event)));
|
|
130
|
+
}
|
|
131
|
+
/** Record what a peer's Bloom handshake matched; see {@link peerIntersections}. */
|
|
132
|
+
recordPeerIntersection(peerName, tenantIds) {
|
|
133
|
+
this.peerIntersections.set(peerName, new Set(tenantIds));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* May this peer be told that something changed in this tenant?
|
|
137
|
+
*
|
|
138
|
+
* Closed by default: a peer that has not run the Bloom handshake in this
|
|
139
|
+
* process gets nothing, rather than everything.
|
|
140
|
+
*/
|
|
141
|
+
peerMaySeeTenant(peerName, tenantId) {
|
|
142
|
+
return this.peerIntersections.get(peerName)?.has(tenantId) ?? false;
|
|
143
|
+
}
|
|
144
|
+
// ------------------------------------------------------------ read model
|
|
145
|
+
topology() {
|
|
146
|
+
return {
|
|
147
|
+
server: { name: this.deps.localServerName(), role: this.localRole },
|
|
148
|
+
peers: [...this.replicators.values()].map((peer) => peer.topologyEntry()),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
status() {
|
|
152
|
+
const peers = [...this.replicators.values()].map((peer) => peer.snapshot());
|
|
153
|
+
// Node health is the worst peer's health: one stalled link is enough to
|
|
154
|
+
// mean this node is not fully converged with the mesh.
|
|
155
|
+
const { health, reasons } = (0, PeerReplicator_1.rollUpHealth)([], peers.flatMap((peer) => peer.reasons));
|
|
156
|
+
return {
|
|
157
|
+
server: this.deps.localServerName(),
|
|
158
|
+
health: peers.length === 0 ? "ok" : health,
|
|
159
|
+
reasons: peers.length === 0 ? [] : reasons,
|
|
160
|
+
autoSyncEnabled: this.started,
|
|
161
|
+
generatedAt: new Date().toISOString(),
|
|
162
|
+
peers,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
tenantStatus(tenantId) {
|
|
166
|
+
return {
|
|
167
|
+
tenantId,
|
|
168
|
+
generatedAt: new Date().toISOString(),
|
|
169
|
+
peers: [...this.replicators.values()].map((peer) => ({
|
|
170
|
+
name: peer.name,
|
|
171
|
+
tenant: peer.tenantSnapshot(tenantId),
|
|
172
|
+
})),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// --------------------------------------------------------------- actions
|
|
176
|
+
/**
|
|
177
|
+
* Dispatch an action as a job across the peers the scope selects.
|
|
178
|
+
*
|
|
179
|
+
* Records the request in the audit log first, with the job id, so an action
|
|
180
|
+
* that is issued is on record even if the node dies mid-run.
|
|
181
|
+
*/
|
|
182
|
+
runAction(kind, scope, requestedBy) {
|
|
183
|
+
const targets = this.targetsFor(scope);
|
|
184
|
+
if (targets.length === 0) {
|
|
185
|
+
this.audit.append({
|
|
186
|
+
at: new Date().toISOString(),
|
|
187
|
+
actor: requestedBy,
|
|
188
|
+
action: kind,
|
|
189
|
+
scope,
|
|
190
|
+
jobId: null,
|
|
191
|
+
outcome: "rejected",
|
|
192
|
+
detail: scope.peer ? "unknown peer" : "no peers configured",
|
|
193
|
+
});
|
|
194
|
+
return {
|
|
195
|
+
accepted: false,
|
|
196
|
+
error: scope.peer ? `Unknown peer: ${scope.peer}` : "No peers configured",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const job = this.jobs.start(kind, scope, requestedBy, async (ctx) => {
|
|
200
|
+
const result = {
|
|
201
|
+
transferredEntries: 0,
|
|
202
|
+
transferredBytes: 0,
|
|
203
|
+
rejectedEntries: 0,
|
|
204
|
+
tenantsProcessed: 0,
|
|
205
|
+
databasesProcessed: 0,
|
|
206
|
+
};
|
|
207
|
+
let completed = 0;
|
|
208
|
+
const peerScope = { tenantId: scope.tenantId, dbId: scope.dbId };
|
|
209
|
+
for (const replicator of targets) {
|
|
210
|
+
switch (kind) {
|
|
211
|
+
case "sync":
|
|
212
|
+
case "retry-rejected": {
|
|
213
|
+
const run = kind === "sync"
|
|
214
|
+
? await replicator.syncNow(peerScope)
|
|
215
|
+
: await replicator.retryRejected(peerScope);
|
|
216
|
+
result.transferredEntries += run.pushed + run.pulled;
|
|
217
|
+
result.rejectedEntries += run.rejected;
|
|
218
|
+
result.tenantsProcessed += run.tenants;
|
|
219
|
+
result.databasesProcessed += run.databases;
|
|
220
|
+
if (run.errors.length > 0) {
|
|
221
|
+
throw new Error(run.errors.join("; "));
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case "refresh-intersection": {
|
|
226
|
+
const shared = await replicator.refreshIntersection();
|
|
227
|
+
result.tenantsProcessed += shared.length;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "pause":
|
|
231
|
+
replicator.pause();
|
|
232
|
+
break;
|
|
233
|
+
case "resume":
|
|
234
|
+
replicator.resume();
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
completed += 1;
|
|
238
|
+
ctx.progress(completed, targets.length);
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
});
|
|
242
|
+
this.audit.append({
|
|
243
|
+
at: new Date().toISOString(),
|
|
244
|
+
actor: requestedBy,
|
|
245
|
+
action: kind,
|
|
246
|
+
scope,
|
|
247
|
+
jobId: job.id,
|
|
248
|
+
outcome: "accepted",
|
|
249
|
+
});
|
|
250
|
+
return { accepted: true, job };
|
|
251
|
+
}
|
|
252
|
+
targetsFor(scope) {
|
|
253
|
+
if (!scope.peer)
|
|
254
|
+
return [...this.replicators.values()];
|
|
255
|
+
const replicator = this.replicators.get(scope.peer);
|
|
256
|
+
return replicator ? [replicator] : [];
|
|
257
|
+
}
|
|
258
|
+
/** Record a peer CRUD change; the caller has already persisted it. */
|
|
259
|
+
auditPeerChange(action, peerName, actor) {
|
|
260
|
+
this.audit.append({
|
|
261
|
+
at: new Date().toISOString(),
|
|
262
|
+
actor,
|
|
263
|
+
action,
|
|
264
|
+
scope: { peer: peerName },
|
|
265
|
+
jobId: null,
|
|
266
|
+
outcome: "accepted",
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Mirrors of one tenant, for `GET /.well-known/mindoodb-cluster`.
|
|
271
|
+
*
|
|
272
|
+
* Only peers that actually hold the requested tenant are listed — a client
|
|
273
|
+
* asking about tenant A must not learn that this server also replicates
|
|
274
|
+
* tenants B and C to some other node.
|
|
275
|
+
*/
|
|
276
|
+
mirrorsForTenant(tenantId) {
|
|
277
|
+
const mirrors = [];
|
|
278
|
+
for (const replicator of this.replicators.values()) {
|
|
279
|
+
// No snapshot for this tenant means the Bloom intersection did not put it
|
|
280
|
+
// on both sides, so the peer is not a mirror a client could fail over to.
|
|
281
|
+
if (!replicator.tenantSnapshot(tenantId))
|
|
282
|
+
continue;
|
|
283
|
+
const entry = replicator.topologyEntry();
|
|
284
|
+
if (!entry.url)
|
|
285
|
+
continue;
|
|
286
|
+
mirrors.push({ name: entry.name, url: entry.url, role: entry.role });
|
|
287
|
+
}
|
|
288
|
+
return mirrors;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.ClusterManager = ClusterManager;
|
|
292
|
+
//# sourceMappingURL=ClusterManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClusterManager.js","sourceRoot":"","sources":["../../../src/server/peer/ClusterManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer (server-to-server) authentication for `/system/peer/*`.
|
|
3
|
+
*
|
|
4
|
+
* Same Ed25519 challenge-response as the tenant sync handshake, but the key
|
|
5
|
+
* lookup goes to `trusted-servers.json` instead of a tenant directory. The
|
|
6
|
+
* issued JWT is deliberately a **different token class** from the system-admin
|
|
7
|
+
* JWT: `/system/peer/*` accepts only peer tokens and `/system/cluster/*` only
|
|
8
|
+
* admin tokens, so a peer can never administer this node and an admin can never
|
|
9
|
+
* write as a peer.
|
|
10
|
+
*/
|
|
11
|
+
import type { CryptoAdapter } from "../../core/crypto/CryptoAdapter";
|
|
12
|
+
import type { TrustedServer } from "../types";
|
|
13
|
+
export interface PeerTokenPayload {
|
|
14
|
+
/** Trusted-server name that authenticated. */
|
|
15
|
+
sub: string;
|
|
16
|
+
/** The Ed25519 key that signed the challenge, i.e. the peer's identity. */
|
|
17
|
+
publicsignkey: string;
|
|
18
|
+
/** Marks this as a peer token; absent/false tokens are rejected by peer routes. */
|
|
19
|
+
peer: true;
|
|
20
|
+
iat: number;
|
|
21
|
+
exp: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class PeerAuthService {
|
|
24
|
+
private readonly cryptoAdapter;
|
|
25
|
+
/** Live view of `trusted-servers.json` — re-read on every handshake. */
|
|
26
|
+
private readonly listTrustedServers;
|
|
27
|
+
private challenges;
|
|
28
|
+
/** Challenge creation is unauthenticated, so the map is bounded (DoS guard). */
|
|
29
|
+
private static readonly MAX_CHALLENGES;
|
|
30
|
+
private readonly jwtSecret;
|
|
31
|
+
private readonly challengeExpirationMs;
|
|
32
|
+
private readonly tokenExpirationMs;
|
|
33
|
+
constructor(cryptoAdapter: CryptoAdapter,
|
|
34
|
+
/** Live view of `trusted-servers.json` — re-read on every handshake. */
|
|
35
|
+
listTrustedServers: () => TrustedServer[], options?: {
|
|
36
|
+
jwtSecret?: Uint8Array;
|
|
37
|
+
challengeExpirationMs?: number;
|
|
38
|
+
tokenExpirationMs?: number;
|
|
39
|
+
});
|
|
40
|
+
/** Resolve a trusted server by its signing key (the canonical identity). */
|
|
41
|
+
findTrustedServerByKey(publicsignkey: string): TrustedServer | null;
|
|
42
|
+
/**
|
|
43
|
+
* Issue a challenge for a peer identified by its signing key.
|
|
44
|
+
*
|
|
45
|
+
* @throws Error("Unknown peer") when the key is not in trusted-servers.json.
|
|
46
|
+
*/
|
|
47
|
+
generateChallenge(publicsignkey: string): Promise<string>;
|
|
48
|
+
authenticate(challenge: string, signature: Uint8Array): Promise<{
|
|
49
|
+
success: boolean;
|
|
50
|
+
token?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Validate a peer JWT. Returns null unless the token carries `peer: true`
|
|
55
|
+
* *and* the signing key is still trusted — revoking trust must cut off
|
|
56
|
+
* outstanding tokens, not just future handshakes.
|
|
57
|
+
*/
|
|
58
|
+
validateToken(token: string): Promise<PeerTokenPayload | null>;
|
|
59
|
+
private verifySignature;
|
|
60
|
+
private generateToken;
|
|
61
|
+
private cleanupExpiredChallenges;
|
|
62
|
+
private evictChallengesOverCap;
|
|
63
|
+
private base64UrlEncode;
|
|
64
|
+
private base64UrlDecode;
|
|
65
|
+
private uint8ArrayToBase64Url;
|
|
66
|
+
private base64UrlToUint8Array;
|
|
67
|
+
private base64ToUint8Array;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=PeerAuthService.d.ts.map
|