@underpostnet/cyberia 3.2.90 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +16 -0
- package/.github/workflows/coverall.cyberia.ci.yml +87 -0
- package/.github/workflows/cyberia-client.cd.yml +1 -18
- package/.github/workflows/cyberia-server.cd.yml +1 -18
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.cd.yml +3 -78
- package/.github/workflows/ghpkg.ci.yml +7 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +1 -9
- package/CHANGELOG.md +110 -1
- package/CLI-HELP.md +139 -9
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +1 -1
- package/bin/build.js +7 -5
- package/bin/cyberia.js +73 -53
- package/bin/deploy.js +1 -1
- package/bin/index.js +73 -53
- package/compose.env +16 -0
- package/conf.js +2 -0
- package/deploy/dd-cyberia/deploy.sh +42 -0
- package/deploy/dd-cyberia/init.sh +63 -0
- package/deploy/dd-cyberia/sync-deploy.sh +194 -0
- package/deploy/lib/logging.sh +96 -0
- package/deploy/pwa-microservices-template/deploy.sh +72 -0
- package/deploy/release/deploy.sh +62 -0
- package/deployment.yaml +1 -1
- package/docker-compose.yml +31 -37
- package/hardhat/package-lock.json +9 -9
- package/hardhat/package.json +3 -3
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +52 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/package.json +2 -2
- package/scripts/audit-selinux.sh +64 -0
- package/scripts/coverall-test-cyberia.sh +24 -0
- package/scripts/coverall-test.sh +24 -0
- package/scripts/gpu-diag.sh +0 -0
- package/scripts/ip-info.sh +0 -0
- package/scripts/k3s-node-setup.sh +18 -15
- package/scripts/kubeadm-node-setup.sh +12 -23
- package/scripts/link-local-underpost-cli.sh +0 -0
- package/scripts/lxd-vm-setup.sh +0 -0
- package/scripts/maas-nat-firewalld.sh +0 -0
- package/scripts/nat-iptables.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +0 -0
- package/scripts/rocky-kickstart.sh +25 -9
- package/scripts/test-monitor.sh +1 -1
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +8 -1
- package/src/api/cyberia-instance/cyberia-instance.controller.js +3 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +13 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +43 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -3
- package/src/api/object-layer/object-layer.model.js +204 -3
- package/src/api/object-layer/object-layer.service.js +1 -14
- package/src/cli/baremetal.js +1 -2
- package/src/cli/cloud-init.js +1 -1
- package/src/cli/cluster.js +73 -68
- package/src/cli/db.js +9 -2
- package/src/cli/deploy.js +21 -5
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/env.js +1 -1
- package/src/cli/image.js +0 -1
- package/src/cli/index.js +121 -9
- package/src/cli/lxd.js +1 -1
- package/src/cli/monitor.js +1 -1
- package/src/cli/release.js +57 -22
- package/src/cli/repository.js +11 -9
- package/src/cli/run.js +36 -9
- package/src/cli/ssh.js +198 -77
- package/src/cli/system.js +26 -13
- package/src/cli/test.js +1 -1
- package/src/cli/vultr.js +583 -0
- package/src/cli/wireguard.js +2125 -0
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +33 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
- package/src/client/services/object-layer/object-layer.management.js +4 -4
- package/src/client-builder/client-build.js +20 -14
- package/src/db/mongo/MongooseDB.js +4 -0
- package/src/index.js +25 -1
- package/src/projects/cyberia/catalog-cyberia.js +5 -0
- package/src/projects/cyberia/generate-saga.js +14 -23
- package/src/projects/cyberia/instance-data.js +21 -2
- package/src/projects/cyberia/object-layer.js +11 -21
- package/src/projects/underpost/catalog-underpost.js +4 -1
- package/src/runtime/cyberia-client/Dockerfile +1 -1
- package/src/runtime/cyberia-client/Dockerfile.dev +1 -1
- package/src/runtime/cyberia-server/Dockerfile +1 -1
- package/src/runtime/cyberia-server/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +1 -1
- package/src/runtime/engine-cyberia/compose.env +16 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
- package/src/server/backup.js +1 -1
- package/src/server/conf.js +18 -108
- package/src/server/cron.js +249 -51
- package/src/server/dns.js +100 -6
- package/src/server/environment.js +98 -0
- package/src/server/forward-proxy.js +549 -0
- package/src/server/middlewares.js +56 -1
- package/src/server/process.js +0 -1
- package/src/server/selinux.js +185 -0
- package/src/server/systemd.js +205 -0
- package/src/server/underpost-compression.js +186 -0
- package/src/server/underpost-gateway.js +20 -10
- package/src/server/underpost-ingress.js +18 -2
- package/test/cyberia-instance-conf-defaults.test.js +1 -0
- package/test/selinux.test.js +71 -0
- package/test/underpost-gateway.test.js +41 -0
- package/test/underpost-ingress.test.js +52 -0
- package/test/wireguard-edge.test.js +1177 -0
package/bin/cyberia.js
CHANGED
|
@@ -256,6 +256,17 @@ try {
|
|
|
256
256
|
/** @type {import('mongoose').Model} */
|
|
257
257
|
const Ipfs = DataBaseProviderService.getModel('ipfs', { host, path });
|
|
258
258
|
|
|
259
|
+
// Idempotent repair, run only before flows that write: collapses legacy
|
|
260
|
+
// duplicates and upgrades the data.item.id index to unique so every
|
|
261
|
+
// later write has exactly one document to land on. Read-only
|
|
262
|
+
// subcommands stay side-effect free — findByItemId already resolves the
|
|
263
|
+
// same canonical document whether or not duplicates are still present.
|
|
264
|
+
if (options.import || options.importTypes || options.drop || options.generate) {
|
|
265
|
+
const { removedIds, indexUpgraded } = await ObjectLayer.ensureUniqueItemIdIndex();
|
|
266
|
+
if (removedIds.length > 0) logger.warn(`Removed ${removedIds.length} duplicate ObjectLayer document(s)`);
|
|
267
|
+
if (indexUpgraded) logger.info('Upgraded data.item.id index to unique');
|
|
268
|
+
}
|
|
269
|
+
|
|
259
270
|
if (options.drop) {
|
|
260
271
|
// Parse comma-separated item IDs for targeted drop; if none provided, drop everything
|
|
261
272
|
const dropItemIds = itemId
|
|
@@ -439,7 +450,7 @@ try {
|
|
|
439
450
|
});
|
|
440
451
|
|
|
441
452
|
// Check if an ObjectLayer with the same item.id already exists (upsert by item ID)
|
|
442
|
-
const existingOL = await ObjectLayer.
|
|
453
|
+
const existingOL = await ObjectLayer.findByItemId(currentItemId);
|
|
443
454
|
let objectLayer;
|
|
444
455
|
|
|
445
456
|
if (existingOL) {
|
|
@@ -457,7 +468,6 @@ try {
|
|
|
457
468
|
let stagingFileDoc = null;
|
|
458
469
|
let stagingAtlasDoc = null;
|
|
459
470
|
let stagingCid = '';
|
|
460
|
-
let stagingSha256 = '';
|
|
461
471
|
try {
|
|
462
472
|
const itemKey = currentItemId;
|
|
463
473
|
|
|
@@ -546,8 +556,7 @@ try {
|
|
|
546
556
|
stagingData.render.cid = importItemCid;
|
|
547
557
|
stagingData.render.metadataCid = importItemMetadataCid;
|
|
548
558
|
|
|
549
|
-
// Pin data JSON to IPFS
|
|
550
|
-
stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
559
|
+
// Pin data JSON to IPFS
|
|
551
560
|
try {
|
|
552
561
|
const ipfsDataResult = await IpfsClient.addJsonToIpfs(
|
|
553
562
|
stagingData,
|
|
@@ -585,10 +594,9 @@ try {
|
|
|
585
594
|
// Create the new RenderFrames doc (only now touches DB)
|
|
586
595
|
const newRenderFrames = await ObjectLayerRenderFrames.create(objectLayerRenderFramesData);
|
|
587
596
|
|
|
588
|
-
// Single atomic
|
|
589
|
-
await ObjectLayer.
|
|
597
|
+
// Single atomic upsert on the natural key; empty staged CIDs keep their stored value
|
|
598
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
590
599
|
data: stagingData,
|
|
591
|
-
sha256: stagingSha256,
|
|
592
600
|
cid: stagingCid,
|
|
593
601
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
594
602
|
atlasSpriteSheetId: stagingAtlasDoc._id,
|
|
@@ -599,14 +607,13 @@ try {
|
|
|
599
607
|
await ObjectLayerRenderFrames.findByIdAndDelete(oldRenderFramesId);
|
|
600
608
|
}
|
|
601
609
|
|
|
602
|
-
logger.info(`[cut-over] Live document ${
|
|
610
|
+
logger.info(`[cut-over] Live document ${objectLayer._id} updated atomically`);
|
|
603
611
|
} else {
|
|
604
612
|
// Rollback: only File/AtlasSpriteSheet were written, clean those up
|
|
605
613
|
if (stagingFileDoc) await File.findByIdAndDelete(stagingFileDoc._id);
|
|
606
614
|
logger.warn(`[cut-over] Staging rolled back for ${currentItemId}, live document preserved`);
|
|
615
|
+
objectLayer = await ObjectLayer.findByItemId(currentItemId);
|
|
607
616
|
}
|
|
608
|
-
|
|
609
|
-
objectLayer = await ObjectLayer.findById(existingOL._id);
|
|
610
617
|
} else {
|
|
611
618
|
// ── New item: stage everything before creating (same cut-over pattern) ──
|
|
612
619
|
logger.info(`ObjectLayer '${currentItemId}' is new, staging creation...`);
|
|
@@ -621,7 +628,6 @@ try {
|
|
|
621
628
|
let stagingFileDoc = null;
|
|
622
629
|
let stagingAtlasDoc = null;
|
|
623
630
|
let stagingCid = '';
|
|
624
|
-
let stagingSha256 = '';
|
|
625
631
|
try {
|
|
626
632
|
const { buffer, metadata } = await AtlasSpriteSheetGenerator.generateAtlas(
|
|
627
633
|
objectLayerRenderFramesData,
|
|
@@ -705,7 +711,6 @@ try {
|
|
|
705
711
|
stagingData.render.cid = importItemCid;
|
|
706
712
|
stagingData.render.metadataCid = importItemMetadataCid;
|
|
707
713
|
|
|
708
|
-
stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
709
714
|
try {
|
|
710
715
|
const ipfsDataResult = await IpfsClient.addJsonToIpfs(
|
|
711
716
|
stagingData,
|
|
@@ -738,9 +743,8 @@ try {
|
|
|
738
743
|
|
|
739
744
|
if (cutoverReady) {
|
|
740
745
|
const newRenderFrames = await ObjectLayerRenderFrames.create(objectLayerRenderFramesData);
|
|
741
|
-
objectLayer = await ObjectLayer.
|
|
746
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
742
747
|
data: stagingData,
|
|
743
|
-
sha256: stagingSha256,
|
|
744
748
|
cid: stagingCid,
|
|
745
749
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
746
750
|
atlasSpriteSheetId: stagingAtlasDoc._id,
|
|
@@ -842,7 +846,7 @@ try {
|
|
|
842
846
|
|
|
843
847
|
if (shouldGenerateAtlas) {
|
|
844
848
|
// Check if an ObjectLayer with the same item.id already exists (upsert by item ID)
|
|
845
|
-
const existingOL = await ObjectLayer.
|
|
849
|
+
const existingOL = await ObjectLayer.findByItemId(objectLayerId);
|
|
846
850
|
let objectLayer;
|
|
847
851
|
|
|
848
852
|
if (existingOL) {
|
|
@@ -860,7 +864,6 @@ try {
|
|
|
860
864
|
let stagingFileDoc = null;
|
|
861
865
|
let stagingAtlasDoc = null;
|
|
862
866
|
let stagingCid = '';
|
|
863
|
-
let stagingSha256 = '';
|
|
864
867
|
try {
|
|
865
868
|
const itemKey = objectLayerId;
|
|
866
869
|
|
|
@@ -948,8 +951,7 @@ try {
|
|
|
948
951
|
stagingData.render.cid = importAtlasCid;
|
|
949
952
|
stagingData.render.metadataCid = importAtlasMetadataCid;
|
|
950
953
|
|
|
951
|
-
// Pin data JSON to IPFS
|
|
952
|
-
stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
954
|
+
// Pin data JSON to IPFS
|
|
953
955
|
try {
|
|
954
956
|
const ipfsDataResult = await IpfsClient.addJsonToIpfs(
|
|
955
957
|
stagingData,
|
|
@@ -985,9 +987,8 @@ try {
|
|
|
985
987
|
const oldRenderFramesId = existingOL.objectLayerRenderFramesId;
|
|
986
988
|
const newRenderFrames = await ObjectLayerRenderFrames.create(entry.objectLayerRenderFramesData);
|
|
987
989
|
|
|
988
|
-
await ObjectLayer.
|
|
990
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
989
991
|
data: stagingData,
|
|
990
|
-
sha256: stagingSha256,
|
|
991
992
|
cid: stagingCid,
|
|
992
993
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
993
994
|
atlasSpriteSheetId: stagingAtlasDoc._id,
|
|
@@ -996,13 +997,12 @@ try {
|
|
|
996
997
|
if (oldRenderFramesId) {
|
|
997
998
|
await ObjectLayerRenderFrames.findByIdAndDelete(oldRenderFramesId);
|
|
998
999
|
}
|
|
999
|
-
logger.info(`[cut-over] Live document ${
|
|
1000
|
+
logger.info(`[cut-over] Live document ${objectLayer._id} updated atomically`);
|
|
1000
1001
|
} else {
|
|
1001
1002
|
if (stagingFileDoc) await File.findByIdAndDelete(stagingFileDoc._id);
|
|
1002
1003
|
logger.warn(`[cut-over] Staging rolled back for ${objectLayerId}, live document preserved`);
|
|
1004
|
+
objectLayer = await ObjectLayer.findByItemId(objectLayerId);
|
|
1003
1005
|
}
|
|
1004
|
-
|
|
1005
|
-
objectLayer = await ObjectLayer.findById(existingOL._id);
|
|
1006
1006
|
} else {
|
|
1007
1007
|
// ── New item: stage everything before creating (same cut-over pattern) ──
|
|
1008
1008
|
logger.info(`ObjectLayer '${objectLayerId}' is new, staging creation...`);
|
|
@@ -1017,7 +1017,6 @@ try {
|
|
|
1017
1017
|
let stagingFileDoc = null;
|
|
1018
1018
|
let stagingAtlasDoc = null;
|
|
1019
1019
|
let stagingCid = '';
|
|
1020
|
-
let stagingSha256 = '';
|
|
1021
1020
|
try {
|
|
1022
1021
|
const { buffer, metadata } = await AtlasSpriteSheetGenerator.generateAtlas(
|
|
1023
1022
|
entry.objectLayerRenderFramesData,
|
|
@@ -1101,7 +1100,6 @@ try {
|
|
|
1101
1100
|
stagingData.render.cid = importAtlasCid;
|
|
1102
1101
|
stagingData.render.metadataCid = importAtlasMetadataCid;
|
|
1103
1102
|
|
|
1104
|
-
stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
1105
1103
|
try {
|
|
1106
1104
|
const ipfsDataResult = await IpfsClient.addJsonToIpfs(
|
|
1107
1105
|
stagingData,
|
|
@@ -1134,9 +1132,8 @@ try {
|
|
|
1134
1132
|
|
|
1135
1133
|
if (cutoverReady) {
|
|
1136
1134
|
const newRenderFrames = await ObjectLayerRenderFrames.create(entry.objectLayerRenderFramesData);
|
|
1137
|
-
objectLayer = await ObjectLayer.
|
|
1135
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
1138
1136
|
data: stagingData,
|
|
1139
|
-
sha256: stagingSha256,
|
|
1140
1137
|
cid: stagingCid,
|
|
1141
1138
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
1142
1139
|
atlasSpriteSheetId: stagingAtlasDoc._id,
|
|
@@ -1159,7 +1156,7 @@ try {
|
|
|
1159
1156
|
if (existingItemIds.has(objectLayerId)) continue;
|
|
1160
1157
|
|
|
1161
1158
|
// --import all: create documents without atlas generation
|
|
1162
|
-
const existingOL = await ObjectLayer.
|
|
1159
|
+
const existingOL = await ObjectLayer.findByItemId(objectLayerId);
|
|
1163
1160
|
let objectLayer;
|
|
1164
1161
|
|
|
1165
1162
|
if (existingOL) {
|
|
@@ -1172,15 +1169,13 @@ try {
|
|
|
1172
1169
|
if (!stagingData.render) stagingData.render = {};
|
|
1173
1170
|
stagingData.render.cid = '';
|
|
1174
1171
|
stagingData.render.metadataCid = '';
|
|
1175
|
-
const stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
1176
1172
|
|
|
1177
1173
|
// Atomic cut-over: create new RenderFrames, swap live doc, delete old
|
|
1178
1174
|
const newRenderFrames = await ObjectLayerRenderFrames.create(entry.objectLayerRenderFramesData);
|
|
1179
1175
|
const oldRenderFramesId = existingOL.objectLayerRenderFramesId;
|
|
1180
1176
|
|
|
1181
|
-
await ObjectLayer.
|
|
1177
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
1182
1178
|
data: stagingData,
|
|
1183
|
-
sha256: stagingSha256,
|
|
1184
1179
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
1185
1180
|
});
|
|
1186
1181
|
|
|
@@ -1188,20 +1183,17 @@ try {
|
|
|
1188
1183
|
await ObjectLayerRenderFrames.findByIdAndDelete(oldRenderFramesId);
|
|
1189
1184
|
}
|
|
1190
1185
|
|
|
1191
|
-
objectLayer
|
|
1192
|
-
logger.info(`[cut-over] Live document ${existingOL._id} updated atomically (atlas skipped)`);
|
|
1186
|
+
logger.info(`[cut-over] Live document ${objectLayer._id} updated atomically (atlas skipped)`);
|
|
1193
1187
|
} else {
|
|
1194
|
-
// New item
|
|
1188
|
+
// New item (no atlas for bulk import)
|
|
1195
1189
|
const stagingData = JSON.parse(JSON.stringify(entry.data));
|
|
1196
1190
|
if (!stagingData.render) stagingData.render = {};
|
|
1197
1191
|
stagingData.render.cid = '';
|
|
1198
1192
|
stagingData.render.metadataCid = '';
|
|
1199
|
-
const stagingSha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
1200
1193
|
|
|
1201
1194
|
const newRenderFrames = await ObjectLayerRenderFrames.create(entry.objectLayerRenderFramesData);
|
|
1202
|
-
objectLayer = await ObjectLayer.
|
|
1195
|
+
objectLayer = await ObjectLayer.upsertByItemId({
|
|
1203
1196
|
data: stagingData,
|
|
1204
|
-
sha256: stagingSha256,
|
|
1205
1197
|
objectLayerRenderFramesId: newRenderFrames._id,
|
|
1206
1198
|
});
|
|
1207
1199
|
}
|
|
@@ -1231,9 +1223,7 @@ try {
|
|
|
1231
1223
|
logger.info(`Showing frame for item: ${itemId}, direction: ${direction}, frame: ${frameIndexNum}`);
|
|
1232
1224
|
|
|
1233
1225
|
// Find ObjectLayer by item-id
|
|
1234
|
-
const objectLayer = await ObjectLayer.
|
|
1235
|
-
'objectLayerRenderFramesId',
|
|
1236
|
-
);
|
|
1226
|
+
const objectLayer = await ObjectLayer.findByItemId(itemId).populate('objectLayerRenderFramesId');
|
|
1237
1227
|
|
|
1238
1228
|
if (!objectLayer) {
|
|
1239
1229
|
logger.error(`ObjectLayer not found for item-id: ${itemId}`);
|
|
@@ -1317,9 +1307,7 @@ try {
|
|
|
1317
1307
|
}
|
|
1318
1308
|
|
|
1319
1309
|
// Find ObjectLayer by item-id
|
|
1320
|
-
const objectLayer = await ObjectLayer.
|
|
1321
|
-
'objectLayerRenderFramesId',
|
|
1322
|
-
);
|
|
1310
|
+
const objectLayer = await ObjectLayer.findByItemId(itemId).populate('objectLayerRenderFramesId');
|
|
1323
1311
|
|
|
1324
1312
|
if (!objectLayer) {
|
|
1325
1313
|
logger.error(`ObjectLayer not found for item-id: ${itemId}`);
|
|
@@ -1453,7 +1441,7 @@ try {
|
|
|
1453
1441
|
logger.info(`Looking up atlas sprite sheet for item: ${itemId}`);
|
|
1454
1442
|
|
|
1455
1443
|
// Find ObjectLayer by item-id
|
|
1456
|
-
const objectLayer = await ObjectLayer.
|
|
1444
|
+
const objectLayer = await ObjectLayer.findByItemId(itemId);
|
|
1457
1445
|
|
|
1458
1446
|
if (!objectLayer) {
|
|
1459
1447
|
logger.error(`ObjectLayer not found for item-id: ${itemId}`);
|
|
@@ -2695,6 +2683,12 @@ try {
|
|
|
2695
2683
|
|
|
2696
2684
|
logger.info('Importing instance', { code: instanceCode, backupDir });
|
|
2697
2685
|
|
|
2686
|
+
// Idempotent: a backup restore writes object layers, so collapse any
|
|
2687
|
+
// legacy duplicates and make the data.item.id index unique first.
|
|
2688
|
+
const { removedIds, indexUpgraded } = await ObjectLayer.ensureUniqueItemIdIndex();
|
|
2689
|
+
if (removedIds.length > 0) logger.warn(`Removed ${removedIds.length} duplicate ObjectLayer document(s)`);
|
|
2690
|
+
if (indexUpgraded) logger.info('Upgraded data.item.id index to unique');
|
|
2691
|
+
|
|
2698
2692
|
// Item ids belonging to this instance (collected from imported object
|
|
2699
2693
|
// layers + the instance doc) — used to backfill missing skills from the
|
|
2700
2694
|
// canonical DefaultSkillConfig when the backup predates the skill model.
|
|
@@ -3029,12 +3023,21 @@ try {
|
|
|
3029
3023
|
let olCount = 0;
|
|
3030
3024
|
for (const file of olFiles) {
|
|
3031
3025
|
const olData = fs.readJsonSync(`${olDir}/${file}`);
|
|
3032
|
-
|
|
3033
|
-
if (
|
|
3034
|
-
|
|
3026
|
+
const olItemId = olData.data?.item?.id;
|
|
3027
|
+
if (!olItemId) {
|
|
3028
|
+
logger.warn(`Skipping object-layer file '${file}' — missing data.item.id`);
|
|
3029
|
+
continue;
|
|
3035
3030
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3031
|
+
// preserveUUID only applies to items this database does not have yet.
|
|
3032
|
+
// An item that already exists keeps its own _id and simply absorbs the
|
|
3033
|
+
// backup's attribute values, so a restore can never fork data.item.id
|
|
3034
|
+
// into two documents. sha256 needs no separate cleanup: it is derived
|
|
3035
|
+
// from data, so an identical hash implies the same item id.
|
|
3036
|
+
const existingOL = await ObjectLayer.findByItemId(olItemId);
|
|
3037
|
+
if (!existingOL && olData._id) await ObjectLayer.deleteOne({ _id: olData._id });
|
|
3038
|
+
|
|
3039
|
+
await ObjectLayer.upsertByItemId(olData);
|
|
3040
|
+
importedItemIds.add(olItemId);
|
|
3038
3041
|
olCount++;
|
|
3039
3042
|
}
|
|
3040
3043
|
logger.info(`Imported ${olCount} ObjectLayer document(s)`);
|
|
@@ -5026,6 +5029,19 @@ try {
|
|
|
5026
5029
|
shellExec(`gh workflow run ${id}.cd.yml -R underpostnet/${id} -f job=deploy`);
|
|
5027
5030
|
});
|
|
5028
5031
|
|
|
5032
|
+
runner
|
|
5033
|
+
.command('test')
|
|
5034
|
+
.option('--n-con <connections>', 'Number of concurrent WebSocket connections')
|
|
5035
|
+
.option('--duration <ms>', 'Load test duration in milliseconds')
|
|
5036
|
+
.option('--tap-freq <seconds>', 'Frequency of tap events in seconds')
|
|
5037
|
+
.action((options) => {
|
|
5038
|
+
shellExec(`CYBERIA_LOAD_WS_URL=ws://localhost:8081/ws \
|
|
5039
|
+
CYBERIA_LOAD_CONNECTIONS=${options.nCon ?? 40} \
|
|
5040
|
+
CYBERIA_LOAD_TAP_FREQUENCY=${options.tapFreq ?? 5} \
|
|
5041
|
+
CYBERIA_LOAD_DURATION_MS=${options.duration ?? 1000 * 60 * 30} \
|
|
5042
|
+
c8 mocha test/cyberia-load.test.js`);
|
|
5043
|
+
});
|
|
5044
|
+
|
|
5029
5045
|
runner
|
|
5030
5046
|
.command('docker-image [id]')
|
|
5031
5047
|
.option('--load-tar', 'Load a pre-built image tar archive into the enabled target(s) without building.')
|
|
@@ -5033,7 +5049,7 @@ try {
|
|
|
5033
5049
|
// no funca
|
|
5034
5050
|
if (options.loadTar) {
|
|
5035
5051
|
for (const imageId of dockerImageIds)
|
|
5036
|
-
if (imageId === id || id === '.') shellExec(`docker load -i ./${imageId}-dev_v3.
|
|
5052
|
+
if (imageId === id || id === '.') shellExec(`docker load -i ./${imageId}-dev_v3.3.0.tar`);
|
|
5037
5053
|
return;
|
|
5038
5054
|
}
|
|
5039
5055
|
switch (id) {
|
|
@@ -5044,7 +5060,7 @@ node bin/build dd-cyberia --update-private
|
|
|
5044
5060
|
node bin image --path src/runtime/engine-cyberia \
|
|
5045
5061
|
--docker-compose --pull-base --build \
|
|
5046
5062
|
--dockerfile-name Dockerfile.dev \
|
|
5047
|
-
--image-name engine-cyberia-dev:v3.
|
|
5063
|
+
--image-name engine-cyberia-dev:v3.3.0 \
|
|
5048
5064
|
--image-out-path .
|
|
5049
5065
|
`);
|
|
5050
5066
|
break;
|
|
@@ -5055,7 +5071,7 @@ cp -f src/runtime/cyberia-server/Dockerfile.dev cyberia-server/Dockerfile.dev
|
|
|
5055
5071
|
node bin image --path cyberia-server \
|
|
5056
5072
|
--docker-compose --pull-base --build \
|
|
5057
5073
|
--dockerfile-name Dockerfile.dev \
|
|
5058
|
-
--image-name cyberia-server-dev:v3.
|
|
5074
|
+
--image-name cyberia-server-dev:v3.3.0 \
|
|
5059
5075
|
--image-out-path .
|
|
5060
5076
|
`);
|
|
5061
5077
|
break;
|
|
@@ -5065,7 +5081,7 @@ cp -f src/runtime/cyberia-client/Dockerfile.dev cyberia-client/Dockerfile.dev
|
|
|
5065
5081
|
node bin image --path cyberia-client \
|
|
5066
5082
|
--docker-compose --pull-base --build \
|
|
5067
5083
|
--dockerfile-name Dockerfile.dev \
|
|
5068
|
-
--image-name cyberia-client-dev:v3.
|
|
5084
|
+
--image-name cyberia-client-dev:v3.3.0 \
|
|
5069
5085
|
--image-out-path .
|
|
5070
5086
|
`);
|
|
5071
5087
|
break;
|
|
@@ -5626,8 +5642,12 @@ node bin image --path cyberia-client \
|
|
|
5626
5642
|
|
|
5627
5643
|
// Copy canonical doc sources into the generated project READMEs.
|
|
5628
5644
|
// Edit the canonical sources; never hand-edit these generated outputs.
|
|
5645
|
+
fs.copySync('./deploy/lib', './cyberia-server/deploy/lib', { overwrite: true });
|
|
5646
|
+
fs.copySync('./deploy/lib', './cyberia-client/deploy/lib', { overwrite: true });
|
|
5629
5647
|
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-CLIENT.md', './cyberia-client/README.md');
|
|
5648
|
+
fs.copySync('./deploy/cyberia-client', './cyberia-client/deploy');
|
|
5630
5649
|
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-SERVER.md', './cyberia-server/README.md');
|
|
5650
|
+
fs.copySync('./deploy/cyberia-server', './cyberia-server/deploy');
|
|
5631
5651
|
fs.copyFileSync(
|
|
5632
5652
|
'./.github/workflows/cyberia-client.cd.yml',
|
|
5633
5653
|
'./cyberia-client/.github/workflows/cyberia-client.cd.yml',
|
package/bin/deploy.js
CHANGED
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
addWsConf,
|
|
15
15
|
buildWsSrc,
|
|
16
16
|
cloneSrcComponents,
|
|
17
|
-
writeEnv,
|
|
18
17
|
buildCliDoc,
|
|
19
18
|
loadConf,
|
|
20
19
|
} from '../src/server/conf.js';
|
|
20
|
+
import { writeEnv } from '../src/server/environment.js';
|
|
21
21
|
import colors from 'colors';
|
|
22
22
|
import { program } from '../src/cli/index.js';
|
|
23
23
|
import { timer, getCapVariableName } from '../src/client/components/core/CommonJs.js';
|