@twin.org/synchronised-storage-service 0.0.1-next.9 → 0.0.3-next.2
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/dist/es/data/verifiableStorageKeys.json +5 -0
- package/dist/es/entities/syncSnapshotEntry.js +93 -0
- package/dist/es/entities/syncSnapshotEntry.js.map +1 -0
- package/dist/es/helpers/blobStorageHelper.js +185 -0
- package/dist/es/helpers/blobStorageHelper.js.map +1 -0
- package/dist/es/helpers/changeSetHelper.js +215 -0
- package/dist/es/helpers/changeSetHelper.js.map +1 -0
- package/dist/es/helpers/localSyncStateHelper.js +384 -0
- package/dist/es/helpers/localSyncStateHelper.js.map +1 -0
- package/dist/es/helpers/remoteSyncStateHelper.js +560 -0
- package/dist/es/helpers/remoteSyncStateHelper.js.map +1 -0
- package/dist/es/helpers/versions.js +6 -0
- package/dist/es/helpers/versions.js.map +1 -0
- package/dist/es/index.js +13 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/ISyncPointerStore.js +4 -0
- package/dist/es/models/ISyncPointerStore.js.map +1 -0
- package/dist/es/models/ISyncSnapshot.js +4 -0
- package/dist/es/models/ISyncSnapshot.js.map +1 -0
- package/dist/es/models/ISyncState.js +2 -0
- package/dist/es/models/ISyncState.js.map +1 -0
- package/dist/es/models/ISynchronisedStorageServiceConfig.js +4 -0
- package/dist/es/models/ISynchronisedStorageServiceConfig.js.map +1 -0
- package/dist/es/models/ISynchronisedStorageServiceConstructorOptions.js +2 -0
- package/dist/es/models/ISynchronisedStorageServiceConstructorOptions.js.map +1 -0
- package/dist/es/restEntryPoints.js +10 -0
- package/dist/es/restEntryPoints.js.map +1 -0
- package/dist/es/schema.js +11 -0
- package/dist/es/schema.js.map +1 -0
- package/dist/es/synchronisedStorageRoutes.js +142 -0
- package/dist/es/synchronisedStorageRoutes.js.map +1 -0
- package/dist/es/synchronisedStorageService.js +512 -0
- package/dist/es/synchronisedStorageService.js.map +1 -0
- package/dist/types/entities/syncSnapshotEntry.d.ts +3 -3
- package/dist/types/helpers/blobStorageHelper.d.ts +3 -3
- package/dist/types/helpers/changeSetHelper.d.ts +16 -32
- package/dist/types/helpers/localSyncStateHelper.d.ts +11 -11
- package/dist/types/helpers/remoteSyncStateHelper.d.ts +18 -14
- package/dist/types/index.d.ts +10 -10
- package/dist/types/models/ISyncState.d.ts +1 -1
- package/dist/types/models/ISynchronisedStorageServiceConfig.d.ts +7 -8
- package/dist/types/models/ISynchronisedStorageServiceConstructorOptions.d.ts +6 -6
- package/dist/types/synchronisedStorageRoutes.d.ts +1 -1
- package/dist/types/synchronisedStorageService.d.ts +17 -21
- package/docs/architecture.md +168 -12
- package/docs/changelog.md +149 -0
- package/docs/open-api/spec.json +62 -57
- package/docs/reference/classes/SyncSnapshotEntry.md +4 -10
- package/docs/reference/classes/SynchronisedStorageService.md +38 -50
- package/docs/reference/interfaces/ISynchronisedStorageServiceConfig.md +11 -17
- package/docs/reference/interfaces/ISynchronisedStorageServiceConstructorOptions.md +8 -8
- package/locales/en.json +7 -15
- package/package.json +26 -9
- package/dist/cjs/index.cjs +0 -2235
- package/dist/esm/index.mjs +0 -2227
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { BlobStorageConnectorFactory } from "@twin.org/blob-storage-models";
|
|
2
|
+
import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
3
|
+
import { BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, UnauthorizedError } from "@twin.org/core";
|
|
4
|
+
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
5
|
+
import { SynchronisedStorageTopics } from "@twin.org/synchronised-storage-models";
|
|
6
|
+
import { TrustHelper } from "@twin.org/trust-models";
|
|
7
|
+
import { VaultConnectorFactory, VaultKeyType } from "@twin.org/vault-models";
|
|
8
|
+
import { VerifiableStorageConnectorFactory } from "@twin.org/verifiable-storage-models";
|
|
9
|
+
import verifiableStorageKeys from "./data/verifiableStorageKeys.json" with { type: "json" };
|
|
10
|
+
import { BlobStorageHelper } from "./helpers/blobStorageHelper.js";
|
|
11
|
+
import { ChangeSetHelper } from "./helpers/changeSetHelper.js";
|
|
12
|
+
import { LocalSyncStateHelper } from "./helpers/localSyncStateHelper.js";
|
|
13
|
+
import { RemoteSyncStateHelper } from "./helpers/remoteSyncStateHelper.js";
|
|
14
|
+
/**
|
|
15
|
+
* Class for performing synchronised storage operations.
|
|
16
|
+
*/
|
|
17
|
+
export class SynchronisedStorageService {
|
|
18
|
+
/**
|
|
19
|
+
* Runtime name for the class.
|
|
20
|
+
*/
|
|
21
|
+
static CLASS_NAME = "SynchronisedStorageService";
|
|
22
|
+
/**
|
|
23
|
+
* The default interval to check for entity updates.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
static _DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES = 5;
|
|
27
|
+
/**
|
|
28
|
+
* The default interval to perform consolidation.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
static _DEFAULT_CONSOLIDATION_INTERVAL_MINUTES = 60;
|
|
32
|
+
/**
|
|
33
|
+
* The default size of a consolidation batch.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
static _DEFAULT_CONSOLIDATION_BATCH_SIZE = 100;
|
|
37
|
+
/**
|
|
38
|
+
* The default max number of consolidations to keep in storage.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
static _DEFAULT_MAX_CONSOLIDATIONS = 5;
|
|
42
|
+
/**
|
|
43
|
+
* The logging component to use for logging.
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
_logging;
|
|
47
|
+
/**
|
|
48
|
+
* The event bus component.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
_eventBusComponent;
|
|
52
|
+
/**
|
|
53
|
+
* The vault connector.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
_vaultConnector;
|
|
57
|
+
/**
|
|
58
|
+
* The storage connector for the sync snapshot entries.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
_localSyncSnapshotEntryEntityStorage;
|
|
62
|
+
/**
|
|
63
|
+
* The blob storage connector to use for remote sync states.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
_blobStorageConnector;
|
|
67
|
+
/**
|
|
68
|
+
* The verifiable storage connector to use for storing sync pointers.
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
_verifiableSyncPointerStorageConnector;
|
|
72
|
+
/**
|
|
73
|
+
* The task scheduler component.
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
_taskSchedulerComponent;
|
|
77
|
+
/**
|
|
78
|
+
* The synchronised storage service to use when this is not a trusted node.
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
_trustedSynchronisedStorageComponent;
|
|
82
|
+
/**
|
|
83
|
+
* The blob storage helper.
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
_blobStorageHelper;
|
|
87
|
+
/**
|
|
88
|
+
* The trust component.
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
_trustComponent;
|
|
92
|
+
/**
|
|
93
|
+
* The change set helper.
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
_changeSetHelper;
|
|
97
|
+
/**
|
|
98
|
+
* The local sync state helper to use for applying changesets.
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
_localSyncStateHelper;
|
|
102
|
+
/**
|
|
103
|
+
* The remote sync state helper to use for applying changesets.
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
_remoteSyncStateHelper;
|
|
107
|
+
/**
|
|
108
|
+
* The options for the connector.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
_config;
|
|
112
|
+
/**
|
|
113
|
+
* The synchronised storage key to use for the remote synchronised storage.
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
_synchronisedStorageKey;
|
|
117
|
+
/**
|
|
118
|
+
* The flag to determine if the service has been started.
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
_serviceStarted;
|
|
122
|
+
/**
|
|
123
|
+
* The active storage keys for the synchronised storage service.
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
_activeStorageKeys;
|
|
127
|
+
/**
|
|
128
|
+
* The identity of the node this connector is running on.
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
_nodeId;
|
|
132
|
+
/**
|
|
133
|
+
* Create a new instance of SynchronisedStorageService.
|
|
134
|
+
* @param options The options for the service.
|
|
135
|
+
*/
|
|
136
|
+
constructor(options) {
|
|
137
|
+
Guards.object(SynchronisedStorageService.CLASS_NAME, "options", options);
|
|
138
|
+
Guards.object(SynchronisedStorageService.CLASS_NAME, "options.config", options.config);
|
|
139
|
+
Guards.stringValue(SynchronisedStorageService.CLASS_NAME, "options.config.verifiableStorageKeyId", options.config.verifiableStorageKeyId);
|
|
140
|
+
this._eventBusComponent = ComponentFactory.get(options.eventBusComponentType ?? "event-bus");
|
|
141
|
+
this._logging = ComponentFactory.getIfExists(options.loggingComponentType ?? "logging");
|
|
142
|
+
this._vaultConnector = VaultConnectorFactory.get(options.vaultConnectorType ?? "vault");
|
|
143
|
+
this._localSyncSnapshotEntryEntityStorage = EntityStorageConnectorFactory.get(options.syncSnapshotStorageConnectorType ?? "sync-snapshot-entry");
|
|
144
|
+
this._verifiableSyncPointerStorageConnector = VerifiableStorageConnectorFactory.get(options.verifiableStorageConnectorType ?? "verifiable-storage");
|
|
145
|
+
this._blobStorageConnector = BlobStorageConnectorFactory.get(options.blobStorageConnectorType ?? "blob-storage");
|
|
146
|
+
this._taskSchedulerComponent = ComponentFactory.get(options.taskSchedulerComponentType ?? "task-scheduler");
|
|
147
|
+
this._trustComponent = ComponentFactory.get(options?.trustComponentType ?? "trust");
|
|
148
|
+
// If this is empty we assume the local node has the rights to write to the verifiable storage.
|
|
149
|
+
let isTrustedNode = true;
|
|
150
|
+
if (!Is.empty(options.trustedSynchronisedStorageComponentType)) {
|
|
151
|
+
isTrustedNode = false;
|
|
152
|
+
// If it is set then we used the trusted component to send changesets to
|
|
153
|
+
this._trustedSynchronisedStorageComponent =
|
|
154
|
+
ComponentFactory.get(options.trustedSynchronisedStorageComponentType);
|
|
155
|
+
}
|
|
156
|
+
this._config = {
|
|
157
|
+
entityUpdateIntervalMinutes: options.config.entityUpdateIntervalMinutes ??
|
|
158
|
+
SynchronisedStorageService._DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES,
|
|
159
|
+
consolidationIntervalMinutes: options.config.consolidationIntervalMinutes ??
|
|
160
|
+
SynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MINUTES,
|
|
161
|
+
consolidationBatchSize: options.config.consolidationBatchSize ??
|
|
162
|
+
SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE,
|
|
163
|
+
maxConsolidations: options.config.maxConsolidations ?? SynchronisedStorageService._DEFAULT_MAX_CONSOLIDATIONS,
|
|
164
|
+
blobStorageEncryptionKeyId: options.config.blobStorageEncryptionKeyId ?? "synchronised-storage-blob-encryption-key",
|
|
165
|
+
verifiableStorageKeyId: options.config.verifiableStorageKeyId,
|
|
166
|
+
overrideTrustGeneratorType: options.config.overrideTrustGeneratorType ?? ""
|
|
167
|
+
};
|
|
168
|
+
this._synchronisedStorageKey =
|
|
169
|
+
verifiableStorageKeys[options.config.verifiableStorageKeyId] ?? options.config.verifiableStorageKeyId;
|
|
170
|
+
Guards.stringValue(SynchronisedStorageService.CLASS_NAME, "synchronisedStorageKey", this._synchronisedStorageKey);
|
|
171
|
+
this._blobStorageHelper = new BlobStorageHelper(this._logging, this._vaultConnector, this._blobStorageConnector, this._config.blobStorageEncryptionKeyId, isTrustedNode);
|
|
172
|
+
this._changeSetHelper = new ChangeSetHelper(this._logging, this._eventBusComponent, this._blobStorageHelper);
|
|
173
|
+
this._localSyncStateHelper = new LocalSyncStateHelper(this._logging, this._localSyncSnapshotEntryEntityStorage, this._changeSetHelper);
|
|
174
|
+
this._remoteSyncStateHelper = new RemoteSyncStateHelper(this._logging, this._eventBusComponent, this._verifiableSyncPointerStorageConnector, this._blobStorageHelper, this._changeSetHelper, isTrustedNode, this._config.maxConsolidations);
|
|
175
|
+
this._serviceStarted = false;
|
|
176
|
+
this._activeStorageKeys = {};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Returns the class name of the component.
|
|
180
|
+
* @returns The class name of the component.
|
|
181
|
+
*/
|
|
182
|
+
className() {
|
|
183
|
+
return SynchronisedStorageService.CLASS_NAME;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The component needs to be started when the node is initialized.
|
|
187
|
+
* @param nodeLoggingComponentType The node logging component type.
|
|
188
|
+
* @returns Nothing.
|
|
189
|
+
*/
|
|
190
|
+
async start(nodeLoggingComponentType) {
|
|
191
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
192
|
+
ContextIdHelper.guard(contextIds, ContextIdKeys.Node);
|
|
193
|
+
this._nodeId = contextIds[ContextIdKeys.Node];
|
|
194
|
+
this._remoteSyncStateHelper.setNodeId(this._nodeId);
|
|
195
|
+
this._changeSetHelper.setNodeId(this._nodeId);
|
|
196
|
+
this._remoteSyncStateHelper.setSynchronisedStorageKey(this._synchronisedStorageKey);
|
|
197
|
+
this._serviceStarted = true;
|
|
198
|
+
// If this is not a trusted node we need to request the decryption key from a trusted node
|
|
199
|
+
if (!Is.empty(this._trustedSynchronisedStorageComponent)) {
|
|
200
|
+
const trustPayload = await this._trustComponent.generate(this._nodeId, this._config.overrideTrustGeneratorType.length > 0
|
|
201
|
+
? this._config.overrideTrustGeneratorType
|
|
202
|
+
: undefined);
|
|
203
|
+
const decryptionKey = await this._trustedSynchronisedStorageComponent.getDecryptionKey(trustPayload);
|
|
204
|
+
// If the key exists remove it and get a new one, in case the key has been rotated
|
|
205
|
+
const existingKey = await this._vaultConnector.getKey(this._config.blobStorageEncryptionKeyId);
|
|
206
|
+
if (!Is.empty(existingKey)) {
|
|
207
|
+
await this._vaultConnector.removeKey(this._config.blobStorageEncryptionKeyId);
|
|
208
|
+
}
|
|
209
|
+
await this._vaultConnector.addKey(this._config.blobStorageEncryptionKeyId, VaultKeyType.ChaCha20Poly1305, Converter.base64ToBytes(decryptionKey));
|
|
210
|
+
}
|
|
211
|
+
await this._eventBusComponent.subscribe(SynchronisedStorageTopics.RegisterStorageKey, async (event) => this.registerStorageKey(event.data));
|
|
212
|
+
await this._eventBusComponent.subscribe(SynchronisedStorageTopics.LocalItemChange, async (event) => {
|
|
213
|
+
// Make sure the change event is from this node
|
|
214
|
+
if (Is.stringValue(this._nodeId) && this._nodeId === event.data.nodeId) {
|
|
215
|
+
await this._localSyncStateHelper.addLocalChange(event.data.storageKey, event.data.operation, event.data.id);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
await this._remoteSyncStateHelper.start();
|
|
219
|
+
// If there are already storage keys registered, we need to activate them
|
|
220
|
+
for (const storageKey in this._activeStorageKeys) {
|
|
221
|
+
await this.activateStorageKey(storageKey);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The component needs to be stopped when the node is closed.
|
|
226
|
+
* @param nodeLoggingComponentType The node logging component type.
|
|
227
|
+
* @returns Nothing.
|
|
228
|
+
*/
|
|
229
|
+
async stop(nodeLoggingComponentType) {
|
|
230
|
+
for (const storageKey in this._activeStorageKeys) {
|
|
231
|
+
this._activeStorageKeys[storageKey] = false;
|
|
232
|
+
await this._taskSchedulerComponent.removeTask(`synchronised-storage-update-${storageKey}`);
|
|
233
|
+
await this._taskSchedulerComponent.removeTask(`synchronised-storage-consolidation-${storageKey}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the decryption key for the synchronised storage.
|
|
238
|
+
* This is used to decrypt the data stored in the synchronised storage.
|
|
239
|
+
* @param trustPayload Trust payload to verify the requesters identity.
|
|
240
|
+
* @returns The decryption key.
|
|
241
|
+
*/
|
|
242
|
+
async getDecryptionKey(trustPayload) {
|
|
243
|
+
if (!Is.empty(this._trustedSynchronisedStorageComponent)) {
|
|
244
|
+
throw new GeneralError(SynchronisedStorageService.CLASS_NAME, "notTrustedNode");
|
|
245
|
+
}
|
|
246
|
+
const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "getDecryptionKey");
|
|
247
|
+
await this._logging?.log({
|
|
248
|
+
level: "info",
|
|
249
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
250
|
+
message: "decryptionKeyRequest",
|
|
251
|
+
data: {
|
|
252
|
+
nodeId: trustInfo.identity
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
const key = await this._vaultConnector.getKey(this._config.blobStorageEncryptionKeyId);
|
|
256
|
+
if (Is.undefined(key.privateKey)) {
|
|
257
|
+
throw new UnauthorizedError(SynchronisedStorageService.CLASS_NAME, "decryptionKeyNotFound");
|
|
258
|
+
}
|
|
259
|
+
return Converter.bytesToBase64(key.privateKey);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Synchronise a set of changes from an untrusted node, assumes this is a trusted node.
|
|
263
|
+
* @param syncChangeSet The change set to synchronise.
|
|
264
|
+
* @param trustPayload Trust payload to verify the requesters identity.
|
|
265
|
+
* @returns Nothing.
|
|
266
|
+
*/
|
|
267
|
+
async syncChangeSet(syncChangeSet, trustPayload) {
|
|
268
|
+
if (!Is.empty(this._trustedSynchronisedStorageComponent)) {
|
|
269
|
+
throw new GeneralError(SynchronisedStorageService.CLASS_NAME, "notTrustedNode");
|
|
270
|
+
}
|
|
271
|
+
Guards.object(SynchronisedStorageService.CLASS_NAME, "syncChangeSet", syncChangeSet);
|
|
272
|
+
const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "syncChangeSet");
|
|
273
|
+
await this._logging?.log({
|
|
274
|
+
level: "info",
|
|
275
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
276
|
+
message: "syncChangeSetForRemoteNode",
|
|
277
|
+
data: {
|
|
278
|
+
changeSetStorageId: syncChangeSet.id,
|
|
279
|
+
nodeId: trustInfo.identity
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
const copy = await this._changeSetHelper.copyChangeset(syncChangeSet);
|
|
283
|
+
if (!Is.empty(copy)) {
|
|
284
|
+
// Apply the changes to this node
|
|
285
|
+
await this._changeSetHelper.applyChangeset(copy.syncChangeSet);
|
|
286
|
+
// And update the sync state with the latest changes
|
|
287
|
+
await this._remoteSyncStateHelper.addChangeSetToSyncState(copy.syncChangeSet.storageKey, copy.changeSetStorageId);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Start the sync with further updates after an interval.
|
|
292
|
+
* @param storageKey The storage key to sync.
|
|
293
|
+
* @returns Nothing.
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
async startEntitySync(storageKey) {
|
|
297
|
+
try {
|
|
298
|
+
await this._logging?.log({
|
|
299
|
+
level: "info",
|
|
300
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
301
|
+
message: "startEntitySync",
|
|
302
|
+
data: {
|
|
303
|
+
storageKey
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
// First we check for remote changes
|
|
307
|
+
await this.updateFromRemoteSyncState(storageKey);
|
|
308
|
+
// Now send any updates we have to the remote storage
|
|
309
|
+
await this.updateFromLocalSyncState(storageKey);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
await this._logging?.log({
|
|
313
|
+
level: "error",
|
|
314
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
315
|
+
message: "entitySyncFailed",
|
|
316
|
+
error: BaseError.fromError(error)
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check for updates in the remote storage.
|
|
322
|
+
* @param storageKey The storage key to check for updates.
|
|
323
|
+
* @returns Nothing.
|
|
324
|
+
* @internal
|
|
325
|
+
*/
|
|
326
|
+
async updateFromRemoteSyncState(storageKey) {
|
|
327
|
+
await this._logging?.log({
|
|
328
|
+
level: "info",
|
|
329
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
330
|
+
message: "updateFromRemoteSyncState",
|
|
331
|
+
data: {
|
|
332
|
+
storageKey
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Get the verifiable sync pointer store from the verifiable storage
|
|
336
|
+
const verifiableSyncPointerStore = await this._remoteSyncStateHelper.getVerifiableSyncPointerStore();
|
|
337
|
+
if (!Is.empty(verifiableSyncPointerStore.syncPointers[storageKey])) {
|
|
338
|
+
// Load the sync state from the remote blob storage using the sync pointer
|
|
339
|
+
// to load the sync state
|
|
340
|
+
const remoteSyncState = await this._remoteSyncStateHelper.getSyncState(verifiableSyncPointerStore.syncPointers[storageKey]);
|
|
341
|
+
// If we got the sync state we can try and sync from it
|
|
342
|
+
if (!Is.undefined(remoteSyncState)) {
|
|
343
|
+
await this._localSyncStateHelper.applySyncState(storageKey, remoteSyncState);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Find any local updates and send them to the remote storage.
|
|
349
|
+
* @returns Nothing.
|
|
350
|
+
* @internal
|
|
351
|
+
*/
|
|
352
|
+
async updateFromLocalSyncState(storageKey) {
|
|
353
|
+
await this._logging?.log({
|
|
354
|
+
level: "info",
|
|
355
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
356
|
+
message: "updateFromLocalSyncState",
|
|
357
|
+
data: {
|
|
358
|
+
storageKey
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const localChangeSnapshots = await this._localSyncStateHelper.getSnapshots(storageKey, true);
|
|
362
|
+
if (localChangeSnapshots.length > 0) {
|
|
363
|
+
const localChangeSnapshot = localChangeSnapshots[0];
|
|
364
|
+
if (Is.arrayValue(localChangeSnapshot.changes)) {
|
|
365
|
+
await this._remoteSyncStateHelper.buildChangeSet(storageKey, localChangeSnapshot.changes, async (syncChangeSet, changeSetStorageId) => {
|
|
366
|
+
if (Is.empty(syncChangeSet) && Is.empty(changeSetStorageId)) {
|
|
367
|
+
await this._logging?.log({
|
|
368
|
+
level: "info",
|
|
369
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
370
|
+
message: "builtStorageChangeSetNone",
|
|
371
|
+
data: {
|
|
372
|
+
storageKey
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
await this._logging?.log({
|
|
378
|
+
level: "info",
|
|
379
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
380
|
+
message: "builtStorageChangeSet",
|
|
381
|
+
data: {
|
|
382
|
+
storageKey,
|
|
383
|
+
changeSetStorageId
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
// Send the local changes to the remote storage if we are a trusted node
|
|
387
|
+
if (Is.empty(this._trustedSynchronisedStorageComponent) &&
|
|
388
|
+
Is.stringValue(changeSetStorageId)) {
|
|
389
|
+
// If we are a trusted node, we can add the change set to the sync state
|
|
390
|
+
// and remove the local change snapshot
|
|
391
|
+
await this._remoteSyncStateHelper.addChangeSetToSyncState(storageKey, changeSetStorageId);
|
|
392
|
+
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
393
|
+
}
|
|
394
|
+
else if (!Is.empty(this._trustedSynchronisedStorageComponent) &&
|
|
395
|
+
Is.object(syncChangeSet) &&
|
|
396
|
+
Is.stringValue(this._nodeId)) {
|
|
397
|
+
// If we are not a trusted node, we need to send the changes to the trusted node
|
|
398
|
+
// and then remove the local change snapshot
|
|
399
|
+
await this._logging?.log({
|
|
400
|
+
level: "info",
|
|
401
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
402
|
+
message: "sendingChangeSetToTrustedNode",
|
|
403
|
+
data: {
|
|
404
|
+
storageKey,
|
|
405
|
+
changeSetStorageId
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
const trustPayload = await this._trustComponent.generate(this._nodeId, this._config.overrideTrustGeneratorType.length > 0
|
|
409
|
+
? this._config.overrideTrustGeneratorType
|
|
410
|
+
: undefined);
|
|
411
|
+
await this._trustedSynchronisedStorageComponent.syncChangeSet(syncChangeSet, trustPayload);
|
|
412
|
+
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
await this._logging?.log({
|
|
419
|
+
level: "info",
|
|
420
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
421
|
+
message: "updateFromLocalSyncStateNoChanges",
|
|
422
|
+
data: {
|
|
423
|
+
storageKey
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Start the consolidation sync.
|
|
431
|
+
* @param storageKey The storage key to consolidate.
|
|
432
|
+
* @returns Nothing.
|
|
433
|
+
* @internal
|
|
434
|
+
*/
|
|
435
|
+
async startConsolidationSync(storageKey) {
|
|
436
|
+
try {
|
|
437
|
+
// If we are going to perform a consolidation first take any local updates
|
|
438
|
+
// we have and create a changeset from them, so that anybody applying
|
|
439
|
+
// just changes since a consolidation can use the changeset
|
|
440
|
+
// and skip the consolidation
|
|
441
|
+
await this.updateFromLocalSyncState(storageKey);
|
|
442
|
+
// Now start the consolidation
|
|
443
|
+
await this._remoteSyncStateHelper.consolidationStart(storageKey, this._config.consolidationBatchSize ??
|
|
444
|
+
SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
await this._logging?.log({
|
|
448
|
+
level: "error",
|
|
449
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
450
|
+
message: "consolidationSyncFailed",
|
|
451
|
+
error: BaseError.fromError(error)
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Register a new sync type.
|
|
457
|
+
* @param syncRegisterStorageKey The sync register type to register.
|
|
458
|
+
* @internal
|
|
459
|
+
*/
|
|
460
|
+
async registerStorageKey(syncRegisterStorageKey) {
|
|
461
|
+
await this._logging?.log({
|
|
462
|
+
level: "info",
|
|
463
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
464
|
+
message: "registerStorageKey",
|
|
465
|
+
data: {
|
|
466
|
+
storageKey: syncRegisterStorageKey.storageKey
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
if (Is.empty(this._activeStorageKeys[syncRegisterStorageKey.storageKey])) {
|
|
470
|
+
this._activeStorageKeys[syncRegisterStorageKey.storageKey] = false;
|
|
471
|
+
if (this._serviceStarted) {
|
|
472
|
+
await this.activateStorageKey(syncRegisterStorageKey.storageKey);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Activate a storage key.
|
|
478
|
+
* @param storageKey The storage key to activate.
|
|
479
|
+
* @internal
|
|
480
|
+
*/
|
|
481
|
+
async activateStorageKey(storageKey) {
|
|
482
|
+
if (!Is.empty(this._activeStorageKeys[storageKey]) && !this._activeStorageKeys[storageKey]) {
|
|
483
|
+
await this._logging?.log({
|
|
484
|
+
level: "info",
|
|
485
|
+
source: SynchronisedStorageService.CLASS_NAME,
|
|
486
|
+
message: "activateStorageKey",
|
|
487
|
+
data: {
|
|
488
|
+
storageKey
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
this._activeStorageKeys[storageKey] = true;
|
|
492
|
+
if (this._config.entityUpdateIntervalMinutes > 0) {
|
|
493
|
+
await this._taskSchedulerComponent.addTask(`synchronised-storage-update-${storageKey}`, [
|
|
494
|
+
{
|
|
495
|
+
nextTriggerTime: Date.now(),
|
|
496
|
+
intervalMinutes: this._config.entityUpdateIntervalMinutes
|
|
497
|
+
}
|
|
498
|
+
], async () => this.startEntitySync(storageKey));
|
|
499
|
+
}
|
|
500
|
+
if (!Is.empty(this._trustedSynchronisedStorageComponent) &&
|
|
501
|
+
this._config.consolidationIntervalMinutes > 0) {
|
|
502
|
+
await this._taskSchedulerComponent.addTask(`synchronised-storage-consolidation-${storageKey}`, [
|
|
503
|
+
{
|
|
504
|
+
nextTriggerTime: Date.now(),
|
|
505
|
+
intervalMinutes: this._config.consolidationIntervalMinutes
|
|
506
|
+
}
|
|
507
|
+
], async () => this.startConsolidationSync(storageKey));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
//# sourceMappingURL=synchronisedStorageService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synchronisedStorageService.js","sourceRoot":"","sources":["../../src/synchronisedStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITaskSchedulerComponent } from \"@twin.org/background-task-models\";\nimport {\n\tBlobStorageConnectorFactory,\n\ttype IBlobStorageConnector\n} from \"@twin.org/blob-storage-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tUnauthorizedError\n} from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IEventBusComponent } from \"@twin.org/event-bus-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype ISyncChangeSet,\n\ttype ISynchronisedStorageComponent,\n\ttype ISyncItemChange,\n\ttype ISyncRegisterStorageKey,\n\tSynchronisedStorageTopics\n} from \"@twin.org/synchronised-storage-models\";\nimport { type ITrustComponent, TrustHelper } from \"@twin.org/trust-models\";\nimport { type IVaultConnector, VaultConnectorFactory, VaultKeyType } from \"@twin.org/vault-models\";\nimport {\n\ttype IVerifiableStorageConnector,\n\tVerifiableStorageConnectorFactory\n} from \"@twin.org/verifiable-storage-models\";\nimport verifiableStorageKeys from \"./data/verifiableStorageKeys.json\" with { type: \"json\" };\nimport type { SyncSnapshotEntry } from \"./entities/syncSnapshotEntry.js\";\nimport { BlobStorageHelper } from \"./helpers/blobStorageHelper.js\";\nimport { ChangeSetHelper } from \"./helpers/changeSetHelper.js\";\nimport { LocalSyncStateHelper } from \"./helpers/localSyncStateHelper.js\";\nimport { RemoteSyncStateHelper } from \"./helpers/remoteSyncStateHelper.js\";\nimport type { ISynchronisedStorageServiceConfig } from \"./models/ISynchronisedStorageServiceConfig.js\";\nimport type { ISynchronisedStorageServiceConstructorOptions } from \"./models/ISynchronisedStorageServiceConstructorOptions.js\";\n\n/**\n * Class for performing synchronised storage operations.\n */\nexport class SynchronisedStorageService implements ISynchronisedStorageComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<SynchronisedStorageService>();\n\n\t/**\n\t * The default interval to check for entity updates.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES: number = 5;\n\n\t/**\n\t * The default interval to perform consolidation.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_CONSOLIDATION_INTERVAL_MINUTES: number = 60;\n\n\t/**\n\t * The default size of a consolidation batch.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_CONSOLIDATION_BATCH_SIZE: number = 100;\n\n\t/**\n\t * The default max number of consolidations to keep in storage.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_MAX_CONSOLIDATIONS: number = 5;\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The event bus component.\n\t * @internal\n\t */\n\tprivate readonly _eventBusComponent: IEventBusComponent;\n\n\t/**\n\t * The vault connector.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The storage connector for the sync snapshot entries.\n\t * @internal\n\t */\n\tprivate readonly _localSyncSnapshotEntryEntityStorage: IEntityStorageConnector<SyncSnapshotEntry>;\n\n\t/**\n\t * The blob storage connector to use for remote sync states.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageConnector: IBlobStorageConnector;\n\n\t/**\n\t * The verifiable storage connector to use for storing sync pointers.\n\t * @internal\n\t */\n\tprivate readonly _verifiableSyncPointerStorageConnector: IVerifiableStorageConnector;\n\n\t/**\n\t * The task scheduler component.\n\t * @internal\n\t */\n\tprivate readonly _taskSchedulerComponent: ITaskSchedulerComponent;\n\n\t/**\n\t * The synchronised storage service to use when this is not a trusted node.\n\t * @internal\n\t */\n\tprivate readonly _trustedSynchronisedStorageComponent?: ISynchronisedStorageComponent;\n\n\t/**\n\t * The blob storage helper.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageHelper: BlobStorageHelper;\n\n\t/**\n\t * The trust component.\n\t * @internal\n\t */\n\tprivate readonly _trustComponent: ITrustComponent;\n\n\t/**\n\t * The change set helper.\n\t * @internal\n\t */\n\tprivate readonly _changeSetHelper: ChangeSetHelper;\n\n\t/**\n\t * The local sync state helper to use for applying changesets.\n\t * @internal\n\t */\n\tprivate readonly _localSyncStateHelper: LocalSyncStateHelper;\n\n\t/**\n\t * The remote sync state helper to use for applying changesets.\n\t * @internal\n\t */\n\tprivate readonly _remoteSyncStateHelper: RemoteSyncStateHelper;\n\n\t/**\n\t * The options for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: Required<ISynchronisedStorageServiceConfig>;\n\n\t/**\n\t * The synchronised storage key to use for the remote synchronised storage.\n\t * @internal\n\t */\n\tprivate readonly _synchronisedStorageKey: string;\n\n\t/**\n\t * The flag to determine if the service has been started.\n\t * @internal\n\t */\n\tprivate _serviceStarted: boolean;\n\n\t/**\n\t * The active storage keys for the synchronised storage service.\n\t * @internal\n\t */\n\tprivate readonly _activeStorageKeys: { [storageKey: string]: boolean };\n\n\t/**\n\t * The identity of the node this connector is running on.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of SynchronisedStorageService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options: ISynchronisedStorageServiceConstructorOptions) {\n\t\tGuards.object<ISynchronisedStorageServiceConstructorOptions>(\n\t\t\tSynchronisedStorageService.CLASS_NAME,\n\t\t\tnameof(options),\n\t\t\toptions\n\t\t);\n\t\tGuards.object<ISynchronisedStorageServiceConfig>(\n\t\t\tSynchronisedStorageService.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tSynchronisedStorageService.CLASS_NAME,\n\t\t\tnameof(options.config.verifiableStorageKeyId),\n\t\t\toptions.config.verifiableStorageKeyId\n\t\t);\n\n\t\tthis._eventBusComponent = ComponentFactory.get(options.eventBusComponentType ?? \"event-bus\");\n\t\tthis._logging = ComponentFactory.getIfExists(options.loggingComponentType ?? \"logging\");\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options.vaultConnectorType ?? \"vault\");\n\n\t\tthis._localSyncSnapshotEntryEntityStorage = EntityStorageConnectorFactory.get<\n\t\t\tIEntityStorageConnector<SyncSnapshotEntry>\n\t\t>(options.syncSnapshotStorageConnectorType ?? \"sync-snapshot-entry\");\n\n\t\tthis._verifiableSyncPointerStorageConnector = VerifiableStorageConnectorFactory.get(\n\t\t\toptions.verifiableStorageConnectorType ?? \"verifiable-storage\"\n\t\t);\n\n\t\tthis._blobStorageConnector = BlobStorageConnectorFactory.get(\n\t\t\toptions.blobStorageConnectorType ?? \"blob-storage\"\n\t\t);\n\n\t\tthis._taskSchedulerComponent = ComponentFactory.get(\n\t\t\toptions.taskSchedulerComponentType ?? \"task-scheduler\"\n\t\t);\n\n\t\tthis._trustComponent = ComponentFactory.get<ITrustComponent>(\n\t\t\toptions?.trustComponentType ?? \"trust\"\n\t\t);\n\n\t\t// If this is empty we assume the local node has the rights to write to the verifiable storage.\n\t\tlet isTrustedNode = true;\n\t\tif (!Is.empty(options.trustedSynchronisedStorageComponentType)) {\n\t\t\tisTrustedNode = false;\n\n\t\t\t// If it is set then we used the trusted component to send changesets to\n\t\t\tthis._trustedSynchronisedStorageComponent =\n\t\t\t\tComponentFactory.get<ISynchronisedStorageComponent>(\n\t\t\t\t\toptions.trustedSynchronisedStorageComponentType\n\t\t\t\t);\n\t\t}\n\n\t\tthis._config = {\n\t\t\tentityUpdateIntervalMinutes:\n\t\t\t\toptions.config.entityUpdateIntervalMinutes ??\n\t\t\t\tSynchronisedStorageService._DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES,\n\t\t\tconsolidationIntervalMinutes:\n\t\t\t\toptions.config.consolidationIntervalMinutes ??\n\t\t\t\tSynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MINUTES,\n\t\t\tconsolidationBatchSize:\n\t\t\t\toptions.config.consolidationBatchSize ??\n\t\t\t\tSynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE,\n\t\t\tmaxConsolidations:\n\t\t\t\toptions.config.maxConsolidations ?? SynchronisedStorageService._DEFAULT_MAX_CONSOLIDATIONS,\n\t\t\tblobStorageEncryptionKeyId:\n\t\t\t\toptions.config.blobStorageEncryptionKeyId ?? \"synchronised-storage-blob-encryption-key\",\n\t\t\tverifiableStorageKeyId: options.config.verifiableStorageKeyId,\n\t\t\toverrideTrustGeneratorType: options.config.overrideTrustGeneratorType ?? \"\"\n\t\t};\n\n\t\tthis._synchronisedStorageKey =\n\t\t\tverifiableStorageKeys[\n\t\t\t\toptions.config.verifiableStorageKeyId as keyof typeof verifiableStorageKeys\n\t\t\t] ?? options.config.verifiableStorageKeyId;\n\n\t\tGuards.stringValue(\n\t\t\tSynchronisedStorageService.CLASS_NAME,\n\t\t\t\"synchronisedStorageKey\",\n\t\t\tthis._synchronisedStorageKey\n\t\t);\n\n\t\tthis._blobStorageHelper = new BlobStorageHelper(\n\t\t\tthis._logging,\n\t\t\tthis._vaultConnector,\n\t\t\tthis._blobStorageConnector,\n\t\t\tthis._config.blobStorageEncryptionKeyId,\n\t\t\tisTrustedNode\n\t\t);\n\n\t\tthis._changeSetHelper = new ChangeSetHelper(\n\t\t\tthis._logging,\n\t\t\tthis._eventBusComponent,\n\t\t\tthis._blobStorageHelper\n\t\t);\n\n\t\tthis._localSyncStateHelper = new LocalSyncStateHelper(\n\t\t\tthis._logging,\n\t\t\tthis._localSyncSnapshotEntryEntityStorage,\n\t\t\tthis._changeSetHelper\n\t\t);\n\n\t\tthis._remoteSyncStateHelper = new RemoteSyncStateHelper(\n\t\t\tthis._logging,\n\t\t\tthis._eventBusComponent,\n\t\t\tthis._verifiableSyncPointerStorageConnector,\n\t\t\tthis._blobStorageHelper,\n\t\t\tthis._changeSetHelper,\n\t\t\tisTrustedNode,\n\t\t\tthis._config.maxConsolidations\n\t\t);\n\n\t\tthis._serviceStarted = false;\n\t\tthis._activeStorageKeys = {};\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn SynchronisedStorageService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The component needs to be started when the node is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\n\t\tthis._remoteSyncStateHelper.setNodeId(this._nodeId);\n\t\tthis._changeSetHelper.setNodeId(this._nodeId);\n\n\t\tthis._remoteSyncStateHelper.setSynchronisedStorageKey(this._synchronisedStorageKey);\n\t\tthis._serviceStarted = true;\n\n\t\t// If this is not a trusted node we need to request the decryption key from a trusted node\n\t\tif (!Is.empty(this._trustedSynchronisedStorageComponent)) {\n\t\t\tconst trustPayload = await this._trustComponent.generate(\n\t\t\t\tthis._nodeId,\n\t\t\t\tthis._config.overrideTrustGeneratorType.length > 0\n\t\t\t\t\t? this._config.overrideTrustGeneratorType\n\t\t\t\t\t: undefined\n\t\t\t);\n\n\t\t\tconst decryptionKey =\n\t\t\t\tawait this._trustedSynchronisedStorageComponent.getDecryptionKey(trustPayload);\n\n\t\t\t// If the key exists remove it and get a new one, in case the key has been rotated\n\t\t\tconst existingKey = await this._vaultConnector.getKey(\n\t\t\t\tthis._config.blobStorageEncryptionKeyId\n\t\t\t);\n\n\t\t\tif (!Is.empty(existingKey)) {\n\t\t\t\tawait this._vaultConnector.removeKey(this._config.blobStorageEncryptionKeyId);\n\t\t\t}\n\n\t\t\tawait this._vaultConnector.addKey(\n\t\t\t\tthis._config.blobStorageEncryptionKeyId,\n\t\t\t\tVaultKeyType.ChaCha20Poly1305,\n\t\t\t\tConverter.base64ToBytes(decryptionKey)\n\t\t\t);\n\t\t}\n\n\t\tawait this._eventBusComponent.subscribe<ISyncRegisterStorageKey>(\n\t\t\tSynchronisedStorageTopics.RegisterStorageKey,\n\t\t\tasync event => this.registerStorageKey(event.data)\n\t\t);\n\n\t\tawait this._eventBusComponent.subscribe<ISyncItemChange>(\n\t\t\tSynchronisedStorageTopics.LocalItemChange,\n\t\t\tasync event => {\n\t\t\t\t// Make sure the change event is from this node\n\t\t\t\tif (Is.stringValue(this._nodeId) && this._nodeId === event.data.nodeId) {\n\t\t\t\t\tawait this._localSyncStateHelper.addLocalChange(\n\t\t\t\t\t\tevent.data.storageKey,\n\t\t\t\t\t\tevent.data.operation,\n\t\t\t\t\t\tevent.data.id\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\n\t\tawait this._remoteSyncStateHelper.start();\n\n\t\t// If there are already storage keys registered, we need to activate them\n\t\tfor (const storageKey in this._activeStorageKeys) {\n\t\t\tawait this.activateStorageKey(storageKey);\n\t\t}\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tfor (const storageKey in this._activeStorageKeys) {\n\t\t\tthis._activeStorageKeys[storageKey] = false;\n\t\t\tawait this._taskSchedulerComponent.removeTask(`synchronised-storage-update-${storageKey}`);\n\t\t\tawait this._taskSchedulerComponent.removeTask(\n\t\t\t\t`synchronised-storage-consolidation-${storageKey}`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get the decryption key for the synchronised storage.\n\t * This is used to decrypt the data stored in the synchronised storage.\n\t * @param trustPayload Trust payload to verify the requesters identity.\n\t * @returns The decryption key.\n\t */\n\tpublic async getDecryptionKey(trustPayload: unknown): Promise<string> {\n\t\tif (!Is.empty(this._trustedSynchronisedStorageComponent)) {\n\t\t\tthrow new GeneralError(SynchronisedStorageService.CLASS_NAME, \"notTrustedNode\");\n\t\t}\n\n\t\tconst trustInfo = await TrustHelper.verifyTrust(\n\t\t\tthis._trustComponent,\n\t\t\ttrustPayload,\n\t\t\t\"getDecryptionKey\"\n\t\t);\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\tmessage: \"decryptionKeyRequest\",\n\t\t\tdata: {\n\t\t\t\tnodeId: trustInfo.identity\n\t\t\t}\n\t\t});\n\n\t\tconst key = await this._vaultConnector.getKey(this._config.blobStorageEncryptionKeyId);\n\t\tif (Is.undefined(key.privateKey)) {\n\t\t\tthrow new UnauthorizedError(SynchronisedStorageService.CLASS_NAME, \"decryptionKeyNotFound\");\n\t\t}\n\n\t\treturn Converter.bytesToBase64(key.privateKey);\n\t}\n\n\t/**\n\t * Synchronise a set of changes from an untrusted node, assumes this is a trusted node.\n\t * @param syncChangeSet The change set to synchronise.\n\t * @param trustPayload Trust payload to verify the requesters identity.\n\t * @returns Nothing.\n\t */\n\tpublic async syncChangeSet(syncChangeSet: ISyncChangeSet, trustPayload: unknown): Promise<void> {\n\t\tif (!Is.empty(this._trustedSynchronisedStorageComponent)) {\n\t\t\tthrow new GeneralError(SynchronisedStorageService.CLASS_NAME, \"notTrustedNode\");\n\t\t}\n\n\t\tGuards.object<ISyncChangeSet>(\n\t\t\tSynchronisedStorageService.CLASS_NAME,\n\t\t\tnameof(syncChangeSet),\n\t\t\tsyncChangeSet\n\t\t);\n\t\tconst trustInfo = await TrustHelper.verifyTrust(\n\t\t\tthis._trustComponent,\n\t\t\ttrustPayload,\n\t\t\t\"syncChangeSet\"\n\t\t);\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\tmessage: \"syncChangeSetForRemoteNode\",\n\t\t\tdata: {\n\t\t\t\tchangeSetStorageId: syncChangeSet.id,\n\t\t\t\tnodeId: trustInfo.identity\n\t\t\t}\n\t\t});\n\n\t\tconst copy = await this._changeSetHelper.copyChangeset(syncChangeSet);\n\n\t\tif (!Is.empty(copy)) {\n\t\t\t// Apply the changes to this node\n\t\t\tawait this._changeSetHelper.applyChangeset(copy.syncChangeSet);\n\n\t\t\t// And update the sync state with the latest changes\n\t\t\tawait this._remoteSyncStateHelper.addChangeSetToSyncState(\n\t\t\t\tcopy.syncChangeSet.storageKey,\n\t\t\t\tcopy.changeSetStorageId\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Start the sync with further updates after an interval.\n\t * @param storageKey The storage key to sync.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async startEntitySync(storageKey: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\tmessage: \"startEntitySync\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// First we check for remote changes\n\t\t\tawait this.updateFromRemoteSyncState(storageKey);\n\n\t\t\t// Now send any updates we have to the remote storage\n\t\t\tawait this.updateFromLocalSyncState(storageKey);\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\tmessage: \"entitySyncFailed\",\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Check for updates in the remote storage.\n\t * @param storageKey The storage key to check for updates.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async updateFromRemoteSyncState(storageKey: string): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\tmessage: \"updateFromRemoteSyncState\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\n\t\t// Get the verifiable sync pointer store from the verifiable storage\n\t\tconst verifiableSyncPointerStore =\n\t\t\tawait this._remoteSyncStateHelper.getVerifiableSyncPointerStore();\n\n\t\tif (!Is.empty(verifiableSyncPointerStore.syncPointers[storageKey])) {\n\t\t\t// Load the sync state from the remote blob storage using the sync pointer\n\t\t\t// to load the sync state\n\t\t\tconst remoteSyncState = await this._remoteSyncStateHelper.getSyncState(\n\t\t\t\tverifiableSyncPointerStore.syncPointers[storageKey]\n\t\t\t);\n\n\t\t\t// If we got the sync state we can try and sync from it\n\t\t\tif (!Is.undefined(remoteSyncState)) {\n\t\t\t\tawait this._localSyncStateHelper.applySyncState(storageKey, remoteSyncState);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find any local updates and send them to the remote storage.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async updateFromLocalSyncState(storageKey: string): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\tmessage: \"updateFromLocalSyncState\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\n\t\tconst localChangeSnapshots = await this._localSyncStateHelper.getSnapshots(storageKey, true);\n\n\t\tif (localChangeSnapshots.length > 0) {\n\t\t\tconst localChangeSnapshot = localChangeSnapshots[0];\n\n\t\t\tif (Is.arrayValue(localChangeSnapshot.changes)) {\n\t\t\t\tawait this._remoteSyncStateHelper.buildChangeSet(\n\t\t\t\t\tstorageKey,\n\t\t\t\t\tlocalChangeSnapshot.changes,\n\t\t\t\t\tasync (syncChangeSet, changeSetStorageId) => {\n\t\t\t\t\t\tif (Is.empty(syncChangeSet) && Is.empty(changeSetStorageId)) {\n\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\t\t\t\t\tmessage: \"builtStorageChangeSetNone\",\n\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\tstorageKey\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\t\t\t\t\tmessage: \"builtStorageChangeSet\",\n\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\t\t\t\tchangeSetStorageId\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t// Send the local changes to the remote storage if we are a trusted node\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tIs.empty(this._trustedSynchronisedStorageComponent) &&\n\t\t\t\t\t\t\t\tIs.stringValue(changeSetStorageId)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// If we are a trusted node, we can add the change set to the sync state\n\t\t\t\t\t\t\t\t// and remove the local change snapshot\n\t\t\t\t\t\t\t\tawait this._remoteSyncStateHelper.addChangeSetToSyncState(\n\t\t\t\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\t\t\t\tchangeSetStorageId\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tawait this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\t!Is.empty(this._trustedSynchronisedStorageComponent) &&\n\t\t\t\t\t\t\t\tIs.object(syncChangeSet) &&\n\t\t\t\t\t\t\t\tIs.stringValue(this._nodeId)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// If we are not a trusted node, we need to send the changes to the trusted node\n\t\t\t\t\t\t\t\t// and then remove the local change snapshot\n\t\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\t\t\t\t\t\tmessage: \"sendingChangeSetToTrustedNode\",\n\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\t\t\t\t\tchangeSetStorageId\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tconst trustPayload = await this._trustComponent.generate(\n\t\t\t\t\t\t\t\t\tthis._nodeId,\n\t\t\t\t\t\t\t\t\tthis._config.overrideTrustGeneratorType.length > 0\n\t\t\t\t\t\t\t\t\t\t? this._config.overrideTrustGeneratorType\n\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tawait this._trustedSynchronisedStorageComponent.syncChangeSet(\n\t\t\t\t\t\t\t\t\tsyncChangeSet,\n\t\t\t\t\t\t\t\t\ttrustPayload\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tawait this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\t\tmessage: \"updateFromLocalSyncStateNoChanges\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tstorageKey\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Start the consolidation sync.\n\t * @param storageKey The storage key to consolidate.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async startConsolidationSync(storageKey: string): Promise<void> {\n\t\ttry {\n\t\t\t// If we are going to perform a consolidation first take any local updates\n\t\t\t// we have and create a changeset from them, so that anybody applying\n\t\t\t// just changes since a consolidation can use the changeset\n\t\t\t// and skip the consolidation\n\t\t\tawait this.updateFromLocalSyncState(storageKey);\n\n\t\t\t// Now start the consolidation\n\t\t\tawait this._remoteSyncStateHelper.consolidationStart(\n\t\t\t\tstorageKey,\n\t\t\t\tthis._config.consolidationBatchSize ??\n\t\t\t\t\tSynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\tmessage: \"consolidationSyncFailed\",\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Register a new sync type.\n\t * @param syncRegisterStorageKey The sync register type to register.\n\t * @internal\n\t */\n\tprivate async registerStorageKey(syncRegisterStorageKey: ISyncRegisterStorageKey): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\tmessage: \"registerStorageKey\",\n\t\t\tdata: {\n\t\t\t\tstorageKey: syncRegisterStorageKey.storageKey\n\t\t\t}\n\t\t});\n\n\t\tif (Is.empty(this._activeStorageKeys[syncRegisterStorageKey.storageKey])) {\n\t\t\tthis._activeStorageKeys[syncRegisterStorageKey.storageKey] = false;\n\n\t\t\tif (this._serviceStarted) {\n\t\t\t\tawait this.activateStorageKey(syncRegisterStorageKey.storageKey);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Activate a storage key.\n\t * @param storageKey The storage key to activate.\n\t * @internal\n\t */\n\tprivate async activateStorageKey(storageKey: string): Promise<void> {\n\t\tif (!Is.empty(this._activeStorageKeys[storageKey]) && !this._activeStorageKeys[storageKey]) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: SynchronisedStorageService.CLASS_NAME,\n\t\t\t\tmessage: \"activateStorageKey\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis._activeStorageKeys[storageKey] = true;\n\n\t\t\tif (this._config.entityUpdateIntervalMinutes > 0) {\n\t\t\t\tawait this._taskSchedulerComponent.addTask(\n\t\t\t\t\t`synchronised-storage-update-${storageKey}`,\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnextTriggerTime: Date.now(),\n\t\t\t\t\t\t\tintervalMinutes: this._config.entityUpdateIntervalMinutes\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\tasync () => this.startEntitySync(storageKey)\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!Is.empty(this._trustedSynchronisedStorageComponent) &&\n\t\t\t\tthis._config.consolidationIntervalMinutes > 0\n\t\t\t) {\n\t\t\t\tawait this._taskSchedulerComponent.addTask(\n\t\t\t\t\t`synchronised-storage-consolidation-${storageKey}`,\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnextTriggerTime: Date.now(),\n\t\t\t\t\t\t\tintervalMinutes: this._config.consolidationIntervalMinutes\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\tasync () => this.startConsolidationSync(storageKey)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { ISyncChange
|
|
1
|
+
import type { ISyncChange } from "@twin.org/synchronised-storage-models";
|
|
2
2
|
/**
|
|
3
3
|
* Class representing an entry for the sync snapshot.
|
|
4
4
|
*/
|
|
5
|
-
export declare class SyncSnapshotEntry
|
|
5
|
+
export declare class SyncSnapshotEntry {
|
|
6
6
|
/**
|
|
7
7
|
* The id for the snapshot.
|
|
8
8
|
*/
|
|
@@ -42,5 +42,5 @@ export declare class SyncSnapshotEntry<T extends ISynchronisedEntity = ISynchron
|
|
|
42
42
|
/**
|
|
43
43
|
* The changes that were made in this snapshot, if this is a local snapshot.
|
|
44
44
|
*/
|
|
45
|
-
changes?: ISyncChange
|
|
45
|
+
changes?: ISyncChange[];
|
|
46
46
|
}
|
|
@@ -8,16 +8,16 @@ export declare class BlobStorageHelper {
|
|
|
8
8
|
/**
|
|
9
9
|
* Runtime name for the class.
|
|
10
10
|
*/
|
|
11
|
-
readonly CLASS_NAME: string;
|
|
11
|
+
static readonly CLASS_NAME: string;
|
|
12
12
|
/**
|
|
13
13
|
* Create a new instance of BlobStorageHelper.
|
|
14
|
-
* @param
|
|
14
|
+
* @param logging The logging component to use for logging.
|
|
15
15
|
* @param vaultConnector The vault connector to use for for the encryption key.
|
|
16
16
|
* @param blobStorageConnector The blob storage component to use.
|
|
17
17
|
* @param blobStorageEncryptionKeyId The id of the vault key to use for encrypting/decrypting blobs.
|
|
18
18
|
* @param isTrustedNode Is this a trusted node.
|
|
19
19
|
*/
|
|
20
|
-
constructor(
|
|
20
|
+
constructor(logging: ILoggingComponent | undefined, vaultConnector: IVaultConnector, blobStorageConnector: IBlobStorageConnector, blobStorageEncryptionKeyId: string, isTrustedNode: boolean);
|
|
21
21
|
/**
|
|
22
22
|
* Load a blob from storage.
|
|
23
23
|
* @param blobId The id of the blob to apply.
|