@underpostnet/cyberia 3.3.73 → 3.3.77
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 +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +10 -0
- 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.ci.yml +1 -1
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/AGENTS.md +61 -12
- package/CHANGELOG.md +159 -161
- package/CLI-HELP.md +20 -13
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +2 -2
- package/README.md +28 -28
- package/bin/build.js +96 -21
- package/bin/cyberia.js +1247 -1373
- package/bin/index.js +1247 -1373
- package/compose.env +17 -5
- package/conf.js +7 -4
- package/deploy/cyberia-client/deploy.sh +10 -11
- package/deploy/cyberia-client/package.sh +0 -1
- package/deploy/cyberia-client/state.sh +3 -2
- package/deploy/cyberia-server/deploy.sh +10 -11
- package/deploy/cyberia-server/package.sh +0 -1
- package/deploy/cyberia-server/state.sh +3 -2
- package/deploy/dd-cyberia/deploy.sh +5 -5
- package/deploy/dd-cyberia/init.sh +15 -14
- package/deploy/dd-cyberia/package.sh +0 -1
- package/deploy/dd-cyberia/state.sh +2 -2
- package/deploy/dd-cyberia/sync-deploy.sh +139 -75
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +92 -76
- package/deploy/lib/host.sh +28 -19
- package/deploy/lib/state.sh +4 -1
- package/deploy/pwa-microservices-template/deploy.sh +10 -8
- package/deploy/release/deploy.sh +0 -2
- package/deployment.yaml +1 -1
- package/docker-compose.yml +23 -17
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +2 -2
- package/hardhat/package.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- package/package.json +19 -17
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
- package/src/api/cyberia-action/cyberia-action.router.js +2 -2
- package/src/api/cyberia-action/cyberia-action.service.js +3 -4
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
- package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
- package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
- package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
- package/src/api/cyberia-map/cyberia-map.service.js +6 -2
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
- package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +10 -3
- package/src/api/object-layer/object-layer.service.js +113 -120
- package/src/cli/app.js +12 -1
- package/src/cli/client.js +2 -2
- package/src/cli/cluster.js +80 -18
- package/src/cli/deploy.js +215 -34
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +158 -342
- package/src/cli/index.js +22 -10
- package/src/cli/release.js +3 -1
- package/src/cli/repository.js +35 -9
- package/src/cli/run.js +80 -199
- package/src/cli/test.js +4 -0
- package/src/cli/wireguard.js +32 -31
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +8 -1
- package/src/client/components/core/Docs.js +77 -100
- package/src/client/components/core/Modal.js +77 -23
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +1 -1
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- package/src/client/ssr/views/Cyberia404.js +3 -3
- package/src/client-builder/client-build-docs.js +52 -52
- package/src/client-builder/client-build.js +67 -44
- package/src/client-builder/client-bundle.js +347 -0
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/grpc/cyberia/grpc-server.js +5 -7
- package/src/index.js +1 -1
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +5 -14
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +3 -2
- package/src/projects/cyberia/gemini-client.js +3 -3
- package/src/projects/cyberia/generate-saga.js +4 -11
- package/src/projects/cyberia/hot-reload-trigger.js +17 -28
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +177 -257
- package/src/projects/cyberia/map-preview-generator.js +32 -68
- package/src/projects/cyberia/object-layer.js +152 -319
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +2 -18
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/runtime/cyberia-client/Dockerfile +8 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -2
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -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 +2 -2
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/server/build/coverage.js +113 -42
- package/src/server/build/package.js +55 -12
- package/src/server/build/testing.js +59 -9
- package/src/server/network/middlewares.js +8 -2
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/ops/logger.js +70 -35
- package/src/server/runtime/conf.js +40 -9
- package/src/server/runtime/start.js +11 -2
- package/src/server/storage/data-query.js +16 -0
- package/src/server/storage/downloader.js +62 -14
- package/src/server/storage/zip.js +153 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
- package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
- package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/client-build-docs.test.js +97 -70
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-resolution.test.js +58 -7
- package/test/unit/coverage-artifact.test.js +120 -29
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +76 -5
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/logger-redaction.test.js +29 -0
- package/test/unit/package.test.js +70 -16
- package/test/unit/prepare-host.test.js +84 -1
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
- package/test/unit/test-tiers.test.js +27 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +11 -3
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
|
|
4
|
+
import { loggerFactory } from '../../server/ops/logger.js';
|
|
5
|
+
import { createPinRecord } from '../../api/ipfs/ipfs.service.js';
|
|
6
|
+
import { IpfsClient } from './ipfs-client.js';
|
|
7
|
+
import { documentFileIds } from '../../api/file/file.ref.js';
|
|
8
|
+
import { ATLAS_FILE_FIELDS, AtlasSpriteSheetStore, atlasMfsPaths } from './atlas-sprite-sheet-store.js';
|
|
9
|
+
import { DEFAULT_ATLAS_UPSCALE_FACTOR } from './atlas-sprite-sheet-generator.js';
|
|
10
|
+
import { ObjectLayerEngine } from './object-layer.js';
|
|
11
|
+
|
|
12
|
+
const logger = loggerFactory(import.meta);
|
|
13
|
+
|
|
14
|
+
/* The `files/` name each atlas render is exported under; one per registered File field, so an
|
|
15
|
+
* export overwrites its own previous copy instead of leaving one behind. */
|
|
16
|
+
const ATLAS_BACKUP_FILE_PREFIX = { fileId: 'atlas', minifyFileId: 'atlas-minify', idlePreviewFileId: 'atlas-idle' };
|
|
17
|
+
export const atlasBackupFileKey = (field, itemKey) => `${ATLAS_BACKUP_FILE_PREFIX[field]}-${itemKey}`;
|
|
18
|
+
|
|
19
|
+
/* A File's bytes as the export wrote them: base64, or a serialised Buffer. */
|
|
20
|
+
const decodeFileData = (data) => {
|
|
21
|
+
if (!data) return data;
|
|
22
|
+
if (data.$base64) return Buffer.from(data.$base64, 'base64');
|
|
23
|
+
if (data.type === 'Buffer' && Array.isArray(data.data)) return Buffer.from(data.data);
|
|
24
|
+
return data;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const readJsonIfPresent = (file) => (fs.existsSync(file) ? fs.readJsonSync(file) : null);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Reads everything an instance backup holds for one object layer.
|
|
31
|
+
*
|
|
32
|
+
* The object layer names its render frames and atlas by `_id`; the atlas names its render
|
|
33
|
+
* Files the same way. Files are matched on that `_id`, never on file name, so a backup that
|
|
34
|
+
* renamed its renders still resolves. The IPFS payloads are the raw bytes behind the three
|
|
35
|
+
* CIDs the item references, when the backup carried them.
|
|
36
|
+
*
|
|
37
|
+
* @param {{backupDir: string, itemId: string}} params
|
|
38
|
+
* @returns {{
|
|
39
|
+
* objectLayer: object, renderFrames: object|null, atlas: object|null,
|
|
40
|
+
* files: object[], payloads: Map<string, Buffer>,
|
|
41
|
+
* }}
|
|
42
|
+
* @throws {Error} When the backup has no object layer for the item.
|
|
43
|
+
*/
|
|
44
|
+
export function readObjectLayerBackup({ backupDir, itemId }) {
|
|
45
|
+
const objectLayer = readJsonIfPresent(path.join(backupDir, 'object-layers', `${itemId}.json`));
|
|
46
|
+
if (!objectLayer) throw new Error(`Backup at ${backupDir} has no object layer '${itemId}'`);
|
|
47
|
+
|
|
48
|
+
const renderFrames = readJsonIfPresent(path.join(backupDir, 'render-frames', `${itemId}.json`));
|
|
49
|
+
const atlas = readJsonIfPresent(path.join(backupDir, 'atlas-sprite-sheets', `${itemId}.json`));
|
|
50
|
+
|
|
51
|
+
const wanted = new Set(documentFileIds(atlas ? [atlas] : [], ATLAS_FILE_FIELDS));
|
|
52
|
+
const filesDir = path.join(backupDir, 'files');
|
|
53
|
+
const files = [];
|
|
54
|
+
if (wanted.size > 0 && fs.existsSync(filesDir)) {
|
|
55
|
+
for (const name of fs.readdirSync(filesDir)) {
|
|
56
|
+
if (!name.endsWith('.json')) continue;
|
|
57
|
+
const doc = fs.readJsonSync(path.join(filesDir, name));
|
|
58
|
+
if (!wanted.has(String(doc._id))) continue;
|
|
59
|
+
files.push({ ...doc, data: decodeFileData(doc.data) });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const payloads = new Map();
|
|
64
|
+
const contentDir = path.join(backupDir, 'ipfs', 'content');
|
|
65
|
+
for (const cid of [objectLayer.cid, objectLayer.data?.render?.cid, objectLayer.data?.render?.metadataCid]) {
|
|
66
|
+
if (!cid) continue;
|
|
67
|
+
const payload = path.join(contentDir, `${cid}.bin`);
|
|
68
|
+
if (fs.existsSync(payload)) payloads.set(cid, fs.readFileSync(payload));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { objectLayer, renderFrames, atlas, files, payloads };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Re-adds one payload and pins it under its canonical path. Returns the CID IPFS assigned, or
|
|
75
|
+
* the backup's own when IPFS is unreachable, so the documents still point at something real. */
|
|
76
|
+
const restorePayload = async ({ cid, payload, mfsPath, resourceType, options }) => {
|
|
77
|
+
if (!payload) return cid;
|
|
78
|
+
const added = await IpfsClient.addToIpfs(payload, path.basename(mfsPath), mfsPath);
|
|
79
|
+
if (!added?.cid) {
|
|
80
|
+
logger.warn(`IPFS add failed for ${mfsPath}; keeping the backup CID ${cid}`);
|
|
81
|
+
return cid;
|
|
82
|
+
}
|
|
83
|
+
if (added.cid !== cid) logger.warn(`IPFS assigned ${added.cid} to ${mfsPath}; the backup said ${cid}`);
|
|
84
|
+
await createPinRecord({ cid: added.cid, resourceType, mfsPath, options });
|
|
85
|
+
return added.cid;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Restores one object layer from an instance backup, making the backup the authority for that
|
|
90
|
+
* item id: its render frames, atlas, atlas render Files, IPFS pins and payloads, and the static
|
|
91
|
+
* frame PNGs, replacing whatever the database holds under that id.
|
|
92
|
+
*
|
|
93
|
+
* Payloads go to IPFS first, so the documents are written with the CIDs IPFS actually
|
|
94
|
+
* assigned. `_id`s are kept, so references between the restored documents stay intact.
|
|
95
|
+
*
|
|
96
|
+
* @param {{backupDir: string, itemId: string, options: {host: string, path: string}}} params
|
|
97
|
+
* @returns {Promise<{itemId: string, files: number, renderFrames: boolean, atlas: boolean, pins: number, staticFiles: number}>}
|
|
98
|
+
*/
|
|
99
|
+
export async function restoreObjectLayerBackup({ backupDir, itemId, options }) {
|
|
100
|
+
const { objectLayer, renderFrames, atlas, files, payloads } = readObjectLayerBackup({ backupDir, itemId });
|
|
101
|
+
const ObjectLayer = DataBaseProviderService.getModel('ObjectLayer', options);
|
|
102
|
+
const ObjectLayerRenderFrames = DataBaseProviderService.getModel('ObjectLayerRenderFrames', options);
|
|
103
|
+
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
104
|
+
const File = DataBaseProviderService.getModel('File', options);
|
|
105
|
+
|
|
106
|
+
// 1. IPFS, so every CID below is one the node will serve.
|
|
107
|
+
const mfs = atlasMfsPaths(itemId);
|
|
108
|
+
const targets = [
|
|
109
|
+
{
|
|
110
|
+
key: 'cid',
|
|
111
|
+
get: () => objectLayer.cid,
|
|
112
|
+
set: (v) => (objectLayer.cid = v),
|
|
113
|
+
mfsPath: `/object-layer/${itemId}/${itemId}_data.json`,
|
|
114
|
+
resourceType: 'object-layer-data',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: 'render',
|
|
118
|
+
get: () => objectLayer.data?.render?.cid,
|
|
119
|
+
set: (v) => (objectLayer.data.render.cid = v),
|
|
120
|
+
mfsPath: mfs.png,
|
|
121
|
+
resourceType: 'atlas-sprite-sheet',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
key: 'metadata',
|
|
125
|
+
get: () => objectLayer.data?.render?.metadataCid,
|
|
126
|
+
set: (v) => (objectLayer.data.render.metadataCid = v),
|
|
127
|
+
mfsPath: mfs.metadata,
|
|
128
|
+
resourceType: 'atlas-metadata',
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
let pins = 0;
|
|
132
|
+
for (const target of targets) {
|
|
133
|
+
const cid = target.get();
|
|
134
|
+
if (!cid) continue;
|
|
135
|
+
const finalCid = await restorePayload({
|
|
136
|
+
cid,
|
|
137
|
+
payload: payloads.get(cid),
|
|
138
|
+
mfsPath: target.mfsPath,
|
|
139
|
+
resourceType: target.resourceType,
|
|
140
|
+
options,
|
|
141
|
+
});
|
|
142
|
+
if (payloads.has(cid)) pins++;
|
|
143
|
+
target.set(finalCid);
|
|
144
|
+
if (target.key === 'render' && atlas) atlas.cid = finalCid;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 2. Documents, leaves first so every reference resolves as it lands.
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
await File.deleteOne({ _id: file._id });
|
|
150
|
+
await File.create(file);
|
|
151
|
+
}
|
|
152
|
+
if (renderFrames?._id) {
|
|
153
|
+
await ObjectLayerRenderFrames.deleteOne({ _id: renderFrames._id });
|
|
154
|
+
await ObjectLayerRenderFrames.create(renderFrames);
|
|
155
|
+
}
|
|
156
|
+
if (atlas) {
|
|
157
|
+
await AtlasSpriteSheet.deleteOne({ _id: atlas._id });
|
|
158
|
+
await AtlasSpriteSheet.deleteOne({ 'metadata.itemKey': itemId });
|
|
159
|
+
await AtlasSpriteSheet.create(atlas);
|
|
160
|
+
// A backup from before the still existed restores without one; cut it from the render now.
|
|
161
|
+
await AtlasSpriteSheetStore.syncIdlePreview({ itemKey: itemId, options });
|
|
162
|
+
await AtlasSpriteSheetStore.pruneOrphanRenders({ options });
|
|
163
|
+
}
|
|
164
|
+
// An item the database already has keeps its own _id and takes the backup's values, so an
|
|
165
|
+
// item id can never fork into two documents.
|
|
166
|
+
const existing = await ObjectLayer.findByItemId(itemId);
|
|
167
|
+
if (!existing && objectLayer._id) await ObjectLayer.deleteOne({ _id: objectLayer._id });
|
|
168
|
+
await ObjectLayer.upsertByItemId(objectLayer);
|
|
169
|
+
|
|
170
|
+
// An atlas restored without its minified render cannot be served, and a render cut from the
|
|
171
|
+
// frames alone is refused wherever the stored layout moved: regenerate the whole atlas from
|
|
172
|
+
// the frames just restored, and relink the object layer to it.
|
|
173
|
+
if (atlas && !atlas.minifyFileId && renderFrames) {
|
|
174
|
+
const live = await ObjectLayer.findByItemId(itemId);
|
|
175
|
+
const { atlasDoc, atlasCid, atlasMetadataCid } = await AtlasSpriteSheetStore.persist({
|
|
176
|
+
itemKey: itemId,
|
|
177
|
+
objectLayerRenderFrames: renderFrames,
|
|
178
|
+
options,
|
|
179
|
+
});
|
|
180
|
+
live.atlasSpriteSheetId = atlasDoc._id;
|
|
181
|
+
if (!live.data.render) live.data.render = {};
|
|
182
|
+
live.data.render.cid = atlasCid;
|
|
183
|
+
live.data.render.metadataCid = atlasMetadataCid;
|
|
184
|
+
live.markModified('data.render');
|
|
185
|
+
await live.save();
|
|
186
|
+
await ObjectLayerEngine.computeAndSaveFinalSha256({ objectLayer: live, options });
|
|
187
|
+
logger.info(`Rebuilt the atlas of '${itemId}': the backup carried no minified render`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 3. The static frame PNGs the web client serves, from the same render frames.
|
|
191
|
+
let staticFiles = 0;
|
|
192
|
+
const itemType = objectLayer.data?.item?.type;
|
|
193
|
+
if (renderFrames && itemType) {
|
|
194
|
+
const written = await ObjectLayerEngine.writeStaticFrameAssets({
|
|
195
|
+
basePaths: ['./src/client/public/cyberia/', `./public/${options.host}${options.path}`],
|
|
196
|
+
itemType,
|
|
197
|
+
itemId,
|
|
198
|
+
objectLayerRenderFramesData: {
|
|
199
|
+
frames: renderFrames.frames || {},
|
|
200
|
+
colors: renderFrames.colors || [],
|
|
201
|
+
frame_duration: renderFrames.frame_duration ?? 100,
|
|
202
|
+
},
|
|
203
|
+
objectLayerData: objectLayer,
|
|
204
|
+
cellPixelDim: DEFAULT_ATLAS_UPSCALE_FACTOR,
|
|
205
|
+
});
|
|
206
|
+
staticFiles = written.length;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { itemId, files: files.length, renderFrames: !!renderFrames, atlas: !!atlas, pins, staticFiles };
|
|
210
|
+
}
|