@abtnode/core 1.17.5-beta-20251209-090953-3a59e7ac → 1.17.5-beta-20251214-122206-29056e8c
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/lib/api/team.js +71 -3
- package/lib/blocklet/manager/disk.js +51 -40
- package/lib/blocklet/manager/helper/blue-green-get-componentids.js +11 -16
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +118 -82
- package/lib/blocklet/migration-dist/migration.cjs +1 -1
- package/lib/migrations/index.js +4 -4
- package/lib/monitor/blocklet-runtime-monitor.js +3 -5
- package/lib/states/audit-log.js +34 -9
- package/lib/states/blocklet-child.js +193 -0
- package/lib/states/blocklet-extras.js +63 -1
- package/lib/states/blocklet.js +292 -11
- package/lib/states/index.js +4 -1
- package/lib/states/notification.js +4 -2
- package/lib/util/blocklet.js +112 -42
- package/lib/util/migration-sqlite-to-postgres.js +240 -6
- package/package.json +39 -39
- package/lib/blocklet/manager/helper/blue-green-update-blocklet-status.js +0 -18
package/lib/states/blocklet.js
CHANGED
|
@@ -145,6 +145,8 @@ const getExternalPortsFromMeta = (meta) =>
|
|
|
145
145
|
const formatBlocklet = (blocklet, phase, dek) => {
|
|
146
146
|
if (phase === 'onRead') {
|
|
147
147
|
blocklet.status = getBlockletStatus(blocklet);
|
|
148
|
+
// Ensure children exists before forEachBlockletSync accesses it
|
|
149
|
+
blocklet.children = blocklet.children || [];
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
forEachBlockletSync(blocklet, (b) => {
|
|
@@ -253,6 +255,199 @@ class BlockletState extends BaseState {
|
|
|
253
255
|
// @didMap: { [did: string]: metaDid: string }
|
|
254
256
|
this.didMap = new Map();
|
|
255
257
|
this.statusLocks = new Map();
|
|
258
|
+
|
|
259
|
+
// BlockletChildState instance passed from outside
|
|
260
|
+
this.BlockletChildState = config.BlockletChildState || null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Load children for a blocklet
|
|
265
|
+
* @param {string} blockletId - The blocklet ID
|
|
266
|
+
* @returns {Promise<Array>} - Array of children
|
|
267
|
+
*/
|
|
268
|
+
async loadChildren(blockletId) {
|
|
269
|
+
if (!this.BlockletChildState || !blockletId) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const children = await this.BlockletChildState.getChildrenByParentId(blockletId);
|
|
274
|
+
// Ensure children is always an array
|
|
275
|
+
if (!Array.isArray(children)) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return children.map((child) => {
|
|
280
|
+
// Ensure meta is an object and has required fields
|
|
281
|
+
const meta = child.meta || {};
|
|
282
|
+
if (!meta.did) {
|
|
283
|
+
logger.warn('loadChildren: child missing meta.did', { childId: child.id, parentBlockletId: blockletId });
|
|
284
|
+
}
|
|
285
|
+
if (!meta.name && !meta.bundleName) {
|
|
286
|
+
logger.warn('loadChildren: child missing meta.name and meta.bundleName', {
|
|
287
|
+
childId: child.id,
|
|
288
|
+
childDid: meta.did,
|
|
289
|
+
parentBlockletId: blockletId,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const childObj = {
|
|
294
|
+
id: child.id,
|
|
295
|
+
mountPoint: child.mountPoint,
|
|
296
|
+
meta,
|
|
297
|
+
bundleSource: child.bundleSource || {},
|
|
298
|
+
source: child.source || 0,
|
|
299
|
+
deployedFrom: child.deployedFrom || '',
|
|
300
|
+
mode: child.mode || 'production',
|
|
301
|
+
status: child.status || 0,
|
|
302
|
+
ports: child.ports || {},
|
|
303
|
+
environments: child.environments || [],
|
|
304
|
+
children: child.children || [],
|
|
305
|
+
migratedFrom: child.migratedFrom || [],
|
|
306
|
+
installedAt: child.installedAt,
|
|
307
|
+
startedAt: child.startedAt,
|
|
308
|
+
stoppedAt: child.stoppedAt,
|
|
309
|
+
pausedAt: child.pausedAt,
|
|
310
|
+
operator: child.operator,
|
|
311
|
+
inProgressStart: child.inProgressStart,
|
|
312
|
+
greenStatus: child.greenStatus,
|
|
313
|
+
greenPorts: child.greenPorts,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Recursively load children for this child
|
|
317
|
+
if (child.children && child.children.length > 0) {
|
|
318
|
+
// Note: children array now contains IDs, need to load them
|
|
319
|
+
// This will be handled by forEachComponentV2 or similar methods
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return childObj;
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Save children to BlockletChild table
|
|
328
|
+
* @param {string} blockletId - The parent blocklet ID
|
|
329
|
+
* @param {string} blockletDid - The parent blocklet DID
|
|
330
|
+
* @param {Array} children - Array of children to save
|
|
331
|
+
*/
|
|
332
|
+
async saveChildren(blockletId, blockletDid, children) {
|
|
333
|
+
if (!this.BlockletChildState) {
|
|
334
|
+
logger.warn('BlockletChildState is not initialized, cannot save children');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!blockletId || !blockletDid) {
|
|
339
|
+
logger.warn('saveChildren called with invalid blockletId or blockletDid', { blockletId, blockletDid });
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!children || children.length === 0) {
|
|
344
|
+
// If no children provided, delete all existing children
|
|
345
|
+
await this.BlockletChildState.deleteByParentId(blockletId);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Get existing children
|
|
350
|
+
const existingChildren = await this.BlockletChildState.getChildrenByParentId(blockletId);
|
|
351
|
+
const existingChildrenMap = new Map();
|
|
352
|
+
existingChildren.forEach((child) => {
|
|
353
|
+
existingChildrenMap.set(child.childDid, child);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Track which children should be kept
|
|
357
|
+
const childrenToKeep = new Set();
|
|
358
|
+
|
|
359
|
+
// Process each child: update if exists, insert if new
|
|
360
|
+
for (const child of children) {
|
|
361
|
+
const childDid = child.meta?.did;
|
|
362
|
+
if (!childDid) {
|
|
363
|
+
logger.warn('saveChildren: child missing meta.did, skipping', {
|
|
364
|
+
blockletId,
|
|
365
|
+
blockletDid,
|
|
366
|
+
child: child.meta?.name || 'unknown',
|
|
367
|
+
});
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
childrenToKeep.add(childDid);
|
|
372
|
+
|
|
373
|
+
const existingChild = existingChildrenMap.get(childDid);
|
|
374
|
+
const updates = {};
|
|
375
|
+
|
|
376
|
+
// Only update fields that have changed or are necessary
|
|
377
|
+
// Fields that should be updated: meta, bundleSource, source, deployedFrom, mode, ports, children, migratedFrom
|
|
378
|
+
// Fields that should be preserved if not provided: status, installedAt, startedAt, stoppedAt, pausedAt, operator, inProgressStart, greenStatus, greenPorts
|
|
379
|
+
|
|
380
|
+
// Always update these fields
|
|
381
|
+
if (child.mountPoint !== undefined) updates.mountPoint = child.mountPoint;
|
|
382
|
+
if (child.meta !== undefined) updates.meta = child.meta;
|
|
383
|
+
if (child.bundleSource !== undefined) updates.bundleSource = child.bundleSource;
|
|
384
|
+
if (child.source !== undefined) updates.source = child.source;
|
|
385
|
+
if (child.deployedFrom !== undefined) updates.deployedFrom = child.deployedFrom;
|
|
386
|
+
if (child.mode !== undefined) updates.mode = child.mode;
|
|
387
|
+
if (child.ports !== undefined) updates.ports = child.ports;
|
|
388
|
+
if (child.environments !== undefined) updates.environments = child.environments;
|
|
389
|
+
|
|
390
|
+
// Only update status-related fields if explicitly provided
|
|
391
|
+
if (child.status !== undefined) updates.status = child.status;
|
|
392
|
+
// Note: installedAt should only be set on first install, never updated
|
|
393
|
+
if (child.startedAt !== undefined) updates.startedAt = child.startedAt;
|
|
394
|
+
if (child.stoppedAt !== undefined) updates.stoppedAt = child.stoppedAt;
|
|
395
|
+
if (child.pausedAt !== undefined) updates.pausedAt = child.pausedAt;
|
|
396
|
+
if (child.operator !== undefined) updates.operator = child.operator;
|
|
397
|
+
if (child.inProgressStart !== undefined) updates.inProgressStart = child.inProgressStart;
|
|
398
|
+
if (child.greenStatus !== undefined) updates.greenStatus = child.greenStatus;
|
|
399
|
+
if (child.greenPorts !== undefined) updates.greenPorts = child.greenPorts;
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
if (existingChild) {
|
|
403
|
+
// Update existing child only if there are changes
|
|
404
|
+
if (Object.keys(updates).length > 0) {
|
|
405
|
+
await this.BlockletChildState.update({ id: existingChild.id }, { $set: updates });
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
// Insert new child
|
|
409
|
+
await this.BlockletChildState.insert({
|
|
410
|
+
parentBlockletId: blockletId,
|
|
411
|
+
parentBlockletDid: blockletDid,
|
|
412
|
+
childDid,
|
|
413
|
+
mountPoint: child.mountPoint,
|
|
414
|
+
meta: child.meta,
|
|
415
|
+
bundleSource: child.bundleSource,
|
|
416
|
+
source: child.source || 0,
|
|
417
|
+
deployedFrom: child.deployedFrom || '',
|
|
418
|
+
mode: child.mode || 'production',
|
|
419
|
+
status: child.status || 0,
|
|
420
|
+
ports: child.ports || {},
|
|
421
|
+
environments: child.environments || [],
|
|
422
|
+
installedAt: new Date(),
|
|
423
|
+
startedAt: child.startedAt,
|
|
424
|
+
stoppedAt: child.stoppedAt,
|
|
425
|
+
pausedAt: child.pausedAt,
|
|
426
|
+
operator: child.operator,
|
|
427
|
+
inProgressStart: child.inProgressStart,
|
|
428
|
+
greenStatus: child.greenStatus,
|
|
429
|
+
greenPorts: child.greenPorts,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
} catch (error) {
|
|
433
|
+
logger.error('saveChildren: failed to save child', {
|
|
434
|
+
blockletId,
|
|
435
|
+
blockletDid,
|
|
436
|
+
childDid,
|
|
437
|
+
isUpdate: !!existingChild,
|
|
438
|
+
error: error.message,
|
|
439
|
+
});
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Delete children that are no longer in the list
|
|
445
|
+
const childrenToDelete = existingChildren.filter((child) => !childrenToKeep.has(child.childDid));
|
|
446
|
+
if (childrenToDelete.length > 0) {
|
|
447
|
+
for (const childToDelete of childrenToDelete) {
|
|
448
|
+
await this.BlockletChildState.remove({ id: childToDelete.id });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
256
451
|
}
|
|
257
452
|
|
|
258
453
|
/**
|
|
@@ -268,7 +463,15 @@ class BlockletState extends BaseState {
|
|
|
268
463
|
}
|
|
269
464
|
|
|
270
465
|
const doc = await this.findOne({ $or: getConditions(did) });
|
|
271
|
-
|
|
466
|
+
if (!doc) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Load children from BlockletChild table
|
|
471
|
+
const children = await this.loadChildren(doc.id);
|
|
472
|
+
doc.children = children;
|
|
473
|
+
|
|
474
|
+
return formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null);
|
|
272
475
|
}
|
|
273
476
|
|
|
274
477
|
async getBlockletMetaDid(did) {
|
|
@@ -304,7 +507,16 @@ class BlockletState extends BaseState {
|
|
|
304
507
|
|
|
305
508
|
async getBlocklets(query = {}, projection = {}, sort = { createdAt: -1 }) {
|
|
306
509
|
const docs = await this.find(query, projection, sort);
|
|
307
|
-
|
|
510
|
+
const result = [];
|
|
511
|
+
|
|
512
|
+
for (const doc of docs.filter(Boolean)) {
|
|
513
|
+
// Load children for each blocklet
|
|
514
|
+
const children = await this.loadChildren(doc.id);
|
|
515
|
+
doc.children = children;
|
|
516
|
+
result.push(formatBlocklet(doc, 'onRead', this.config.dek));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return result;
|
|
308
520
|
}
|
|
309
521
|
|
|
310
522
|
async deleteBlocklet(did) {
|
|
@@ -313,6 +525,11 @@ class BlockletState extends BaseState {
|
|
|
313
525
|
throw new CustomError(404, `Try to remove non-existing blocklet ${did}`);
|
|
314
526
|
}
|
|
315
527
|
|
|
528
|
+
// Delete children from BlockletChild table
|
|
529
|
+
if (this.BlockletChildState) {
|
|
530
|
+
await this.BlockletChildState.deleteByParentId(doc.id);
|
|
531
|
+
}
|
|
532
|
+
|
|
316
533
|
await this.remove({ id: doc.id });
|
|
317
534
|
|
|
318
535
|
this.didMap.delete(doc.meta?.did);
|
|
@@ -376,7 +593,6 @@ class BlockletState extends BaseState {
|
|
|
376
593
|
deployedFrom,
|
|
377
594
|
ports,
|
|
378
595
|
environments: [],
|
|
379
|
-
children,
|
|
380
596
|
migratedFrom,
|
|
381
597
|
externalSk,
|
|
382
598
|
externalSkSource,
|
|
@@ -384,6 +600,10 @@ class BlockletState extends BaseState {
|
|
|
384
600
|
});
|
|
385
601
|
|
|
386
602
|
doc = await this.findOne({ id: doc.id });
|
|
603
|
+
doc.children = children;
|
|
604
|
+
|
|
605
|
+
// Save children to BlockletChild table
|
|
606
|
+
await this.saveChildren(doc.id, doc.meta.did, children);
|
|
387
607
|
|
|
388
608
|
this.emit('add', doc);
|
|
389
609
|
|
|
@@ -403,6 +623,15 @@ class BlockletState extends BaseState {
|
|
|
403
623
|
const formatted = formatBlocklet(omit(cloneDeep(updates), ['vaults']), 'onUpdate', this.config.dek);
|
|
404
624
|
const [, [updated]] = await this.update({ id: doc.id }, { $set: formatted });
|
|
405
625
|
|
|
626
|
+
// If children are being updated, set them on the updated object before calculating status
|
|
627
|
+
// This ensures getBlockletStatus can correctly calculate status based on children
|
|
628
|
+
if (updates.children !== undefined) {
|
|
629
|
+
updated.children = updates.children;
|
|
630
|
+
if (this.BlockletChildState) {
|
|
631
|
+
await this.saveChildren(updated.id, updated.meta.did, updates.children);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
406
635
|
updated.status = getBlockletStatus(updated);
|
|
407
636
|
|
|
408
637
|
return updated;
|
|
@@ -475,10 +704,13 @@ class BlockletState extends BaseState {
|
|
|
475
704
|
meta: omit(sanitized, ['htmlAst']),
|
|
476
705
|
source,
|
|
477
706
|
deployedFrom,
|
|
478
|
-
children,
|
|
479
707
|
ports,
|
|
480
708
|
});
|
|
481
709
|
|
|
710
|
+
// Save children to BlockletChild table
|
|
711
|
+
await this.saveChildren(newDoc.id, newDoc.meta.did, children);
|
|
712
|
+
newDoc.children = children;
|
|
713
|
+
|
|
482
714
|
this.emit('upgrade', newDoc);
|
|
483
715
|
return newDoc;
|
|
484
716
|
} finally {
|
|
@@ -630,9 +862,19 @@ class BlockletState extends BaseState {
|
|
|
630
862
|
});
|
|
631
863
|
|
|
632
864
|
if (actuallyRefreshedDids.length > 0) {
|
|
633
|
-
await this.updateBlocklet(did, {
|
|
634
|
-
|
|
635
|
-
|
|
865
|
+
await this.updateBlocklet(did, {});
|
|
866
|
+
|
|
867
|
+
// Only update ports/greenPorts to avoid overwriting status during concurrent operations
|
|
868
|
+
if (this.BlockletChildState) {
|
|
869
|
+
for (const component of blocklet.children) {
|
|
870
|
+
if (actuallyRefreshedDids.includes(component.meta?.did)) {
|
|
871
|
+
await this.BlockletChildState.updateChildPorts(blocklet.id, component.meta.did, {
|
|
872
|
+
ports: component.ports,
|
|
873
|
+
greenPorts: component.greenPorts,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
636
878
|
}
|
|
637
879
|
|
|
638
880
|
return {
|
|
@@ -643,7 +885,7 @@ class BlockletState extends BaseState {
|
|
|
643
885
|
}
|
|
644
886
|
|
|
645
887
|
async getServices() {
|
|
646
|
-
const blocklets = await this.getBlocklets({}, { meta: 1,
|
|
888
|
+
const blocklets = await this.getBlocklets({}, { meta: 1, ports: 1 });
|
|
647
889
|
const services = [];
|
|
648
890
|
|
|
649
891
|
blocklets.forEach((blocklet) => {
|
|
@@ -681,7 +923,7 @@ class BlockletState extends BaseState {
|
|
|
681
923
|
* @return {Object} { <did> : { interfaceName } }
|
|
682
924
|
*/
|
|
683
925
|
async groupAllInterfaces() {
|
|
684
|
-
const blocklets = await this.getBlocklets({}, { meta: 1
|
|
926
|
+
const blocklets = await this.getBlocklets({}, { meta: 1 });
|
|
685
927
|
const result = {};
|
|
686
928
|
const fillResult = (component, { id }) => {
|
|
687
929
|
const { interfaces } = component.meta;
|
|
@@ -701,6 +943,22 @@ class BlockletState extends BaseState {
|
|
|
701
943
|
return result;
|
|
702
944
|
}
|
|
703
945
|
|
|
946
|
+
/**
|
|
947
|
+
* Reset the blocklet state by clearing all blocklets and their children
|
|
948
|
+
* This overrides BaseState.reset() to handle foreign key constraints
|
|
949
|
+
*/
|
|
950
|
+
async reset() {
|
|
951
|
+
// First, delete all children to avoid foreign key constraint errors
|
|
952
|
+
if (this.BlockletChildState) {
|
|
953
|
+
const allBlocklets = await this.getBlocklets({}, { id: 1 });
|
|
954
|
+
for (const blocklet of allBlocklets) {
|
|
955
|
+
await this.BlockletChildState.deleteByParentId(blocklet.id);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
// Then call parent reset to clear the blocklets table
|
|
959
|
+
return super.reset();
|
|
960
|
+
}
|
|
961
|
+
|
|
704
962
|
/**
|
|
705
963
|
* @param {Did} did blocklet did
|
|
706
964
|
* @param {BlockletStatus} status blocklet status
|
|
@@ -736,6 +994,8 @@ class BlockletState extends BaseState {
|
|
|
736
994
|
return res;
|
|
737
995
|
}
|
|
738
996
|
|
|
997
|
+
// Collect components to update
|
|
998
|
+
const componentsToUpdate = [];
|
|
739
999
|
for (const component of doc.children || []) {
|
|
740
1000
|
if (component.meta.group === BlockletGroup.gateway) {
|
|
741
1001
|
continue;
|
|
@@ -745,6 +1005,9 @@ class BlockletState extends BaseState {
|
|
|
745
1005
|
continue;
|
|
746
1006
|
}
|
|
747
1007
|
|
|
1008
|
+
componentsToUpdate.push(component.meta.did);
|
|
1009
|
+
|
|
1010
|
+
// Update in-memory for return value
|
|
748
1011
|
component[isGreen ? 'greenStatus' : 'status'] = status;
|
|
749
1012
|
if (isGreenAndBlue) {
|
|
750
1013
|
component.greenStatus = status;
|
|
@@ -764,14 +1027,32 @@ class BlockletState extends BaseState {
|
|
|
764
1027
|
|
|
765
1028
|
const shouldSetStatus = status === BlockletStatus.downloading || status === BlockletStatus.waiting;
|
|
766
1029
|
const updateData = {
|
|
767
|
-
children: doc.children,
|
|
768
1030
|
operator,
|
|
769
1031
|
};
|
|
770
1032
|
if (shouldSetStatus) {
|
|
771
1033
|
updateData.status = status;
|
|
772
1034
|
}
|
|
773
1035
|
|
|
1036
|
+
// Update blocklet without children to avoid overwriting status during concurrent operations
|
|
774
1037
|
const res = await this.updateBlocklet(did, updateData);
|
|
1038
|
+
|
|
1039
|
+
// Update each component's status individually using BlockletChildState
|
|
1040
|
+
if (this.BlockletChildState && componentsToUpdate.length > 0) {
|
|
1041
|
+
for (const componentDid of componentsToUpdate) {
|
|
1042
|
+
await this.BlockletChildState.updateChildStatus(doc.id, componentDid, {
|
|
1043
|
+
status,
|
|
1044
|
+
isGreen,
|
|
1045
|
+
isGreenAndBlue,
|
|
1046
|
+
operator,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const children = await this.loadChildren(doc.id);
|
|
1052
|
+
|
|
1053
|
+
res.children = children;
|
|
1054
|
+
// Recalculate status after children are loaded with updated status
|
|
1055
|
+
res.status = getBlockletStatus(res);
|
|
775
1056
|
return res;
|
|
776
1057
|
} finally {
|
|
777
1058
|
await lock.releaseLock(lockName);
|
|
@@ -902,7 +1183,7 @@ class BlockletState extends BaseState {
|
|
|
902
1183
|
}
|
|
903
1184
|
|
|
904
1185
|
async _getOccupiedPorts() {
|
|
905
|
-
const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, meta: 1
|
|
1186
|
+
const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, meta: 1 });
|
|
906
1187
|
|
|
907
1188
|
const occupiedExternalPorts = new Map();
|
|
908
1189
|
const occupiedInternalPorts = new Map();
|
package/lib/states/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:states');
|
|
|
4
4
|
|
|
5
5
|
const NodeState = require('./node');
|
|
6
6
|
const BlockletState = require('./blocklet');
|
|
7
|
+
const BlockletChildState = require('./blocklet-child');
|
|
7
8
|
const NotificationState = require('./notification');
|
|
8
9
|
const SiteState = require('./site');
|
|
9
10
|
const AccessKeyState = require('./access-key');
|
|
@@ -33,7 +34,8 @@ const init = (dataDirs, config = {}) => {
|
|
|
33
34
|
const notificationState = new NotificationState(models.Notification, config, models);
|
|
34
35
|
|
|
35
36
|
const nodeState = new NodeState(models.Server, config, dataDirs, notificationState);
|
|
36
|
-
const
|
|
37
|
+
const blockletChildState = new BlockletChildState(models.BlockletChild, config);
|
|
38
|
+
const blockletState = new BlockletState(models.Blocklet, { ...config, BlockletChildState: blockletChildState });
|
|
37
39
|
const siteState = new SiteState(models.Site, config);
|
|
38
40
|
const accessKeyState = new AccessKeyState(models.AccessKey, config);
|
|
39
41
|
const webhookState = new WebhookState(models.WebHook, config);
|
|
@@ -52,6 +54,7 @@ const init = (dataDirs, config = {}) => {
|
|
|
52
54
|
return {
|
|
53
55
|
node: nodeState,
|
|
54
56
|
blocklet: blockletState,
|
|
57
|
+
blockletChild: blockletChildState,
|
|
55
58
|
notification: notificationState,
|
|
56
59
|
site: siteState,
|
|
57
60
|
accessKey: accessKeyState,
|
|
@@ -965,11 +965,13 @@ class NotificationState extends BaseState {
|
|
|
965
965
|
throw new Error('Invalid since format. Expected format: "1h", "2h", "24h", etc.');
|
|
966
966
|
}
|
|
967
967
|
|
|
968
|
-
|
|
968
|
+
let hours = parseInt(sinceMatch[1], 10);
|
|
969
969
|
|
|
970
970
|
// 验证范围:最小 1h,最大 24h
|
|
971
971
|
if (hours < 1 || hours > 24) {
|
|
972
|
-
|
|
972
|
+
logger.warn('The since parameter must be between 1h and 24h.');
|
|
973
|
+
// 限制 hours 在 1-24 范围内
|
|
974
|
+
hours = Math.min(Math.max(hours, 1), 24);
|
|
973
975
|
}
|
|
974
976
|
|
|
975
977
|
// 计算时间范围
|
package/lib/util/blocklet.js
CHANGED
|
@@ -416,36 +416,6 @@ const getBlockletConfigObj = (blocklet, { excludeSecure } = {}) => {
|
|
|
416
416
|
|
|
417
417
|
return obj;
|
|
418
418
|
};
|
|
419
|
-
/**
|
|
420
|
-
* set 'configs', configObj', 'environmentObj' to blocklet TODO
|
|
421
|
-
* @param {*} blocklet
|
|
422
|
-
* @param {*} configs
|
|
423
|
-
*/
|
|
424
|
-
const fillBlockletConfigs = (blocklet, configs) => {
|
|
425
|
-
blocklet.configs = configs || [];
|
|
426
|
-
blocklet.configObj = getBlockletConfigObj(blocklet);
|
|
427
|
-
blocklet.environments = blocklet.environments || [];
|
|
428
|
-
blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
|
|
429
|
-
acc[x.key] = templateReplace(x.value, blocklet);
|
|
430
|
-
return acc;
|
|
431
|
-
}, {});
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const ensureBlockletExpanded = async (_meta, appDir) => {
|
|
435
|
-
const bundlePath = path.join(appDir, BLOCKLET_BUNDLE_FILE);
|
|
436
|
-
if (fs.existsSync(bundlePath)) {
|
|
437
|
-
try {
|
|
438
|
-
const nodeModulesPath = path.join(appDir, 'node_modules');
|
|
439
|
-
if (fs.existsSync(nodeModulesPath)) {
|
|
440
|
-
fs.removeSync(nodeModulesPath);
|
|
441
|
-
}
|
|
442
|
-
await expandBundle(bundlePath, appDir);
|
|
443
|
-
fs.removeSync(bundlePath);
|
|
444
|
-
} catch (err) {
|
|
445
|
-
throw new Error(`Failed to expand blocklet bundle: ${err.message}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
419
|
|
|
450
420
|
const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
|
|
451
421
|
const { did, name, title, description } = blocklet.meta;
|
|
@@ -562,6 +532,81 @@ const getComponentSystemEnvironments = (blocklet) => {
|
|
|
562
532
|
};
|
|
563
533
|
};
|
|
564
534
|
|
|
535
|
+
/**
|
|
536
|
+
* set 'configs', configObj', 'environmentObj' to blocklet TODO
|
|
537
|
+
* @param {*} blocklet
|
|
538
|
+
* @param {*} configs
|
|
539
|
+
* @param {*} options - Optional: { rootBlocklet, nodeInfo, dataDirs }
|
|
540
|
+
*/
|
|
541
|
+
const fillBlockletConfigs = (blocklet, configs, options = {}) => {
|
|
542
|
+
blocklet.configs = configs || [];
|
|
543
|
+
blocklet.configObj = getBlockletConfigObj(blocklet);
|
|
544
|
+
blocklet.environments = blocklet.environments || [];
|
|
545
|
+
blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
|
|
546
|
+
acc[x.key] = templateReplace(x.value, blocklet);
|
|
547
|
+
return acc;
|
|
548
|
+
}, {});
|
|
549
|
+
|
|
550
|
+
// After migration: ensure all component system environments are set from blocklet.env if available
|
|
551
|
+
// This ensures children loaded from blocklet_children table have all required env vars in environmentObj
|
|
552
|
+
if (blocklet.env) {
|
|
553
|
+
try {
|
|
554
|
+
const componentSystemEnvs = getComponentSystemEnvironments(blocklet);
|
|
555
|
+
// Only set env vars that are not already set
|
|
556
|
+
Object.entries(componentSystemEnvs).forEach(([key, value]) => {
|
|
557
|
+
if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
|
|
558
|
+
blocklet.environmentObj[key] = value;
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
} catch (error) {
|
|
562
|
+
// If getting component system environments fails, log warning but continue
|
|
563
|
+
logger.warn('fillBlockletConfigs: failed to get component system environments', {
|
|
564
|
+
blockletDid: blocklet.meta?.did,
|
|
565
|
+
error: error.message,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// For children: also set app-level environment variables from root blocklet
|
|
571
|
+
// This ensures children have app-level env vars like BLOCKLET_APP_ID
|
|
572
|
+
const { rootBlocklet, nodeInfo, dataDirs } = options;
|
|
573
|
+
if (rootBlocklet && nodeInfo && dataDirs && blocklet !== rootBlocklet) {
|
|
574
|
+
try {
|
|
575
|
+
const appSystemEnvs = getAppSystemEnvironments(rootBlocklet, nodeInfo, dataDirs);
|
|
576
|
+
const appOverwrittenEnvs = getAppOverwrittenEnvironments(rootBlocklet, nodeInfo);
|
|
577
|
+
// Only set env vars that are not already set
|
|
578
|
+
Object.entries({ ...appSystemEnvs, ...appOverwrittenEnvs }).forEach(([key, value]) => {
|
|
579
|
+
if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
|
|
580
|
+
blocklet.environmentObj[key] = value;
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
} catch (error) {
|
|
584
|
+
// If getting app system environments fails, log warning but continue
|
|
585
|
+
logger.warn('fillBlockletConfigs: failed to get app system environments', {
|
|
586
|
+
blockletDid: blocklet.meta?.did,
|
|
587
|
+
rootDid: rootBlocklet.meta?.did,
|
|
588
|
+
error: error.message,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const ensureBlockletExpanded = async (_meta, appDir) => {
|
|
595
|
+
const bundlePath = path.join(appDir, BLOCKLET_BUNDLE_FILE);
|
|
596
|
+
if (fs.existsSync(bundlePath)) {
|
|
597
|
+
try {
|
|
598
|
+
const nodeModulesPath = path.join(appDir, 'node_modules');
|
|
599
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
600
|
+
fs.removeSync(nodeModulesPath);
|
|
601
|
+
}
|
|
602
|
+
await expandBundle(bundlePath, appDir);
|
|
603
|
+
fs.removeSync(bundlePath);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
throw new Error(`Failed to expand blocklet bundle: ${err.message}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
|
|
565
610
|
const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors, isGreen = false) => {
|
|
566
611
|
const root = (ancestors || [])[0] || blocklet;
|
|
567
612
|
|
|
@@ -1847,6 +1892,7 @@ const formatBlockletTheme = (rawTheme) => {
|
|
|
1847
1892
|
themeConfig = {
|
|
1848
1893
|
...concept.themeConfig,
|
|
1849
1894
|
prefer: concept.prefer,
|
|
1895
|
+
name: concept.name,
|
|
1850
1896
|
};
|
|
1851
1897
|
} else {
|
|
1852
1898
|
// 兼容旧数据
|
|
@@ -1855,6 +1901,7 @@ const formatBlockletTheme = (rawTheme) => {
|
|
|
1855
1901
|
dark: rawTheme.dark || {},
|
|
1856
1902
|
common: rawTheme.common || {},
|
|
1857
1903
|
prefer: rawTheme.prefer || 'system',
|
|
1904
|
+
name: rawTheme.name || 'Default',
|
|
1858
1905
|
};
|
|
1859
1906
|
}
|
|
1860
1907
|
}
|
|
@@ -1970,21 +2017,44 @@ const _getBlocklet = async ({
|
|
|
1970
2017
|
|
|
1971
2018
|
await forEachBlocklet(blocklet, async (component, { id, level, ancestors }) => {
|
|
1972
2019
|
// component env
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2020
|
+
try {
|
|
2021
|
+
// Validate component has required meta fields for getComponentDirs
|
|
2022
|
+
if (!component.meta) {
|
|
2023
|
+
throw new Error(`Component missing meta field: ${component.meta?.did || id}`);
|
|
2024
|
+
}
|
|
2025
|
+
if (!component.meta.name && !component.meta.bundleName) {
|
|
2026
|
+
throw new Error(
|
|
2027
|
+
`Component missing meta.name and meta.bundleName: ${component.meta.did || id}. ` +
|
|
2028
|
+
'This may indicate a migration issue with blocklet_children table.'
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
component.env = {
|
|
2033
|
+
id,
|
|
2034
|
+
name: getComponentName(component, ancestors),
|
|
2035
|
+
processId: getComponentProcessId(component, ancestors),
|
|
2036
|
+
...getComponentDirs(component, {
|
|
2037
|
+
dataDirs,
|
|
2038
|
+
ensure: ensureIntegrity,
|
|
2039
|
+
ancestors,
|
|
2040
|
+
e2eMode: level === 0 ? e2eMode : false,
|
|
2041
|
+
}),
|
|
2042
|
+
};
|
|
2043
|
+
} catch (error) {
|
|
2044
|
+
logger.error('Failed to set component env in _getBlocklet', {
|
|
2045
|
+
componentDid: component.meta?.did,
|
|
2046
|
+
componentName: component.meta?.name,
|
|
2047
|
+
componentBundleName: component.meta?.bundleName,
|
|
2048
|
+
error: error.message,
|
|
2049
|
+
stack: error.stack,
|
|
2050
|
+
});
|
|
2051
|
+
throw error;
|
|
2052
|
+
}
|
|
1984
2053
|
|
|
1985
2054
|
// component config
|
|
1986
2055
|
const configs = await states.blockletExtras.getConfigs([...ancestors.map((x) => x.meta.did), component.meta.did]);
|
|
1987
|
-
|
|
2056
|
+
const rootBlocklet = ancestors.length > 0 ? ancestors[0] : blocklet;
|
|
2057
|
+
fillBlockletConfigs(component, configs, { rootBlocklet, nodeInfo, dataDirs });
|
|
1988
2058
|
});
|
|
1989
2059
|
|
|
1990
2060
|
if (getOptionalComponents) {
|