@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
|
@@ -15,13 +15,18 @@ import { loggerFactory } from '../../server/ops/logger.js';
|
|
|
15
15
|
import { ObjectLayerRenderFramesDto } from '../object-layer-render-frames/object-layer-render-frames.model.js';
|
|
16
16
|
import { FileFactory } from '../file/file.service.js';
|
|
17
17
|
import fs from 'fs-extra';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import sharp from 'sharp';
|
|
21
|
+
import { validateStats } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
|
|
18
22
|
import { ObjectLayerDto } from './object-layer.model.js';
|
|
19
23
|
import { ObjectLayerEngine } from '../../projects/cyberia/object-layer.js';
|
|
20
24
|
import { shellExec } from '../../server/runtime/process.js';
|
|
21
25
|
import { DataQuery } from '../../server/storage/data-query.js';
|
|
22
26
|
import { AtlasSpriteSheetService } from '../atlas-sprite-sheet/atlas-sprite-sheet.service.js';
|
|
27
|
+
import { AtlasSpriteSheetStore } from '../../projects/cyberia/atlas-sprite-sheet-store.js';
|
|
23
28
|
import { IpfsClient } from '../../projects/cyberia/ipfs-client.js';
|
|
24
|
-
import {
|
|
29
|
+
import { removePinRecordsAndUnpin } from '../ipfs/ipfs.service.js';
|
|
25
30
|
|
|
26
31
|
/**
|
|
27
32
|
* Logger instance for this module.
|
|
@@ -45,8 +50,8 @@ class ObjectLayerService {
|
|
|
45
50
|
* - `/metadata/:itemType/:itemId` — Create an object layer from uploaded frames and metadata.
|
|
46
51
|
* - Default — Create an object layer directly from the request body.
|
|
47
52
|
*
|
|
48
|
-
* The `/metadata` and default routes delegate to {@link ObjectLayerEngine.
|
|
49
|
-
* for document
|
|
53
|
+
* The `/metadata` and default routes delegate to {@link ObjectLayerEngine.persistObjectLayerDocuments}
|
|
54
|
+
* for the document write, atlas generation, SHA-256 computation, and IPFS pinning.
|
|
50
55
|
*
|
|
51
56
|
* @async
|
|
52
57
|
* @function post
|
|
@@ -160,22 +165,12 @@ class ObjectLayerService {
|
|
|
160
165
|
metadataOverride: req.body,
|
|
161
166
|
});
|
|
162
167
|
|
|
163
|
-
const { objectLayer } = await ObjectLayerEngine.
|
|
168
|
+
const { objectLayer } = await ObjectLayerEngine.persistObjectLayerDocuments({
|
|
164
169
|
ObjectLayer,
|
|
165
170
|
ObjectLayerRenderFrames,
|
|
166
171
|
objectLayerRenderFramesData,
|
|
167
172
|
objectLayerData,
|
|
168
|
-
|
|
169
|
-
generateAtlas: true,
|
|
170
|
-
atlasServiceContext: {
|
|
171
|
-
req,
|
|
172
|
-
res,
|
|
173
|
-
options,
|
|
174
|
-
AtlasSpriteSheetService,
|
|
175
|
-
IpfsClient,
|
|
176
|
-
createPinRecord,
|
|
177
|
-
},
|
|
178
|
-
},
|
|
173
|
+
persistOptions: { options },
|
|
179
174
|
});
|
|
180
175
|
|
|
181
176
|
return objectLayer;
|
|
@@ -191,44 +186,32 @@ class ObjectLayerService {
|
|
|
191
186
|
bodyData.data.render.cid = '';
|
|
192
187
|
bodyData.data.render.metadataCid = '';
|
|
193
188
|
|
|
194
|
-
//
|
|
189
|
+
// Stage the atlas CIDs before the write, so the document is never stored without them.
|
|
195
190
|
if (bodyData.objectLayerRenderFramesId) {
|
|
196
191
|
const renderFramesDoc = await ObjectLayerRenderFrames.findById(bodyData.objectLayerRenderFramesId);
|
|
197
192
|
if (renderFramesDoc) {
|
|
198
193
|
try {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
203
|
-
const result = await AtlasSpriteSheetService.generate(
|
|
204
|
-
{ objectLayer: stagingOL, auth: req.auth },
|
|
205
|
-
res,
|
|
194
|
+
const { atlasDoc, atlasCid, atlasMetadataCid } = await AtlasSpriteSheetStore.persist({
|
|
195
|
+
itemKey: bodyData.data.item.id,
|
|
196
|
+
objectLayerRenderFrames: renderFramesDoc,
|
|
206
197
|
options,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
bodyData.data.render.
|
|
210
|
-
bodyData.
|
|
211
|
-
bodyData.atlasSpriteSheetId = result.atlasDoc._id;
|
|
198
|
+
});
|
|
199
|
+
bodyData.data.render.cid = atlasCid;
|
|
200
|
+
bodyData.data.render.metadataCid = atlasMetadataCid;
|
|
201
|
+
bodyData.atlasSpriteSheetId = atlasDoc._id;
|
|
212
202
|
} catch (atlasError) {
|
|
213
203
|
logger.error('Failed to auto-generate atlas for new ObjectLayer:', atlasError);
|
|
214
204
|
}
|
|
215
205
|
}
|
|
216
206
|
}
|
|
217
207
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
bodyData.cid = ipfsResult.cid;
|
|
225
|
-
await createPinRecord({ cid: ipfsResult.cid, resourceType: 'object-layer-data', mfsPath, options });
|
|
226
|
-
}
|
|
227
|
-
} catch (ipfsError) {
|
|
228
|
-
logger.warn('Failed to pin data JSON to IPFS:', ipfsError.message);
|
|
229
|
-
}
|
|
208
|
+
const dataCid = await ObjectLayerEngine.pinObjectLayerData({
|
|
209
|
+
data: bodyData.data,
|
|
210
|
+
itemId: bodyData.data.item.id,
|
|
211
|
+
options,
|
|
212
|
+
});
|
|
213
|
+
if (dataCid) bodyData.cid = dataCid;
|
|
230
214
|
|
|
231
|
-
// Atomic create/replace – ObjectLayer is fully populated with all CIDs
|
|
232
215
|
return await (await ObjectLayer.upsertByItemId(bodyData)).populate('objectLayerRenderFramesId');
|
|
233
216
|
};
|
|
234
217
|
|
|
@@ -257,25 +240,33 @@ class ObjectLayerService {
|
|
|
257
240
|
/** @type {import('./object-layer.model.js').ObjectLayerModel} */
|
|
258
241
|
const ObjectLayer = DataBaseProviderService.getModel('ObjectLayer', options);
|
|
259
242
|
|
|
260
|
-
//
|
|
243
|
+
// Object layers are addressed by item id, the key the editor navigates by.
|
|
244
|
+
const keyFilter = (key) => DataQuery.naturalKeyFilter('data.item.id', key);
|
|
245
|
+
|
|
246
|
+
// GET /search-item-ids - item identity, by prefix (`q`, for type-ahead) or by exact ids
|
|
247
|
+
// (`ids`, comma separated). Carries the item's type because that is what addresses its
|
|
248
|
+
// sprite directory; an id alone cannot be previewed.
|
|
261
249
|
if (req.path.startsWith('/search-item-ids')) {
|
|
262
250
|
const q = (req.query.q || '').trim();
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
251
|
+
const ids = String(req.query.ids || '')
|
|
252
|
+
.split(',')
|
|
253
|
+
.map((id) => id.trim())
|
|
254
|
+
.filter(Boolean);
|
|
255
|
+
if (!q && ids.length === 0) return { items: [] };
|
|
256
|
+
const query = ids.length
|
|
257
|
+
? { 'data.item.id': { $in: ids } }
|
|
258
|
+
: // Escape regex special characters for safe partial matching
|
|
259
|
+
{ 'data.item.id': { $regex: q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), $options: 'i' } };
|
|
260
|
+
const results = await ObjectLayer.find(query, { 'data.item.id': 1, 'data.item.type': 1, _id: 0 })
|
|
261
|
+
.limit(ids.length ? ids.length : 20)
|
|
271
262
|
.lean();
|
|
272
|
-
const
|
|
273
|
-
return {
|
|
263
|
+
const byId = new Map(results.map((r) => [r.data.item.id, r.data.item.type || '']));
|
|
264
|
+
return { items: [...byId].map(([id, type]) => ({ id, type })) };
|
|
274
265
|
}
|
|
275
266
|
|
|
276
267
|
// GET /frame-counts/:id - Get frame counts for each direction using numeric codes
|
|
277
268
|
if (req.path.startsWith('/frame-counts/')) {
|
|
278
|
-
const objectLayer = await ObjectLayer.
|
|
269
|
+
const objectLayer = await ObjectLayer.findOne(keyFilter(req.params.id))
|
|
279
270
|
.select(ObjectLayerDto.select.getMetadata())
|
|
280
271
|
.populate('objectLayerRenderFramesId');
|
|
281
272
|
if (!objectLayer) {
|
|
@@ -318,7 +309,7 @@ class ObjectLayerService {
|
|
|
318
309
|
|
|
319
310
|
// GET /render/:id - Get only render data for specific object layer
|
|
320
311
|
if (req.path.startsWith('/render/')) {
|
|
321
|
-
const objectLayer = await ObjectLayer.
|
|
312
|
+
const objectLayer = await ObjectLayer.findOne(keyFilter(req.params.id))
|
|
322
313
|
.select(ObjectLayerDto.select.getRender())
|
|
323
314
|
.populate('objectLayerRenderFramesId');
|
|
324
315
|
if (!objectLayer) {
|
|
@@ -333,7 +324,7 @@ class ObjectLayerService {
|
|
|
333
324
|
|
|
334
325
|
// GET /metadata/:id - Get only metadata (no render frames/colors) for specific object layer
|
|
335
326
|
if (req.path.startsWith('/metadata/')) {
|
|
336
|
-
const objectLayer = await ObjectLayer.
|
|
327
|
+
const objectLayer = await ObjectLayer.findOne(keyFilter(req.params.id))
|
|
337
328
|
.select(ObjectLayerDto.select.getMetadata())
|
|
338
329
|
.populate('objectLayerRenderFramesId', ObjectLayerRenderFramesDto.select.get());
|
|
339
330
|
if (!objectLayer) {
|
|
@@ -385,22 +376,14 @@ class ObjectLayerService {
|
|
|
385
376
|
}
|
|
386
377
|
|
|
387
378
|
// GET / - Get paginated list of object layers
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (objectLayer) {
|
|
397
|
-
logger.info(`ObjectLayerService.get - found record by id: ${id}`);
|
|
398
|
-
return { data: [objectLayer], total: 1, page: 1, totalPages: 1 };
|
|
399
|
-
}
|
|
400
|
-
logger.warn(`ObjectLayerService.get - record NOT found for id: ${id}`);
|
|
401
|
-
} catch (e) {
|
|
402
|
-
logger.warn(`Invalid ID format or not found: ${id}`);
|
|
403
|
-
}
|
|
379
|
+
const key = req.params.id || req.query.id;
|
|
380
|
+
if (key && key !== 'undefined' && !['render', 'metadata', 'frame-counts'].includes(key)) {
|
|
381
|
+
const objectLayer = await ObjectLayer.findOne(keyFilter(key))
|
|
382
|
+
.select(ObjectLayerDto.select.get())
|
|
383
|
+
.populate('atlasSpriteSheetId', 'cid')
|
|
384
|
+
.populate('objectLayerRenderFramesId', ObjectLayerRenderFramesDto.select.get());
|
|
385
|
+
if (objectLayer) return { data: [objectLayer], total: 1, page: 1, totalPages: 1 };
|
|
386
|
+
logger.warn(`ObjectLayerService.get - no object layer for key: ${key}`);
|
|
404
387
|
}
|
|
405
388
|
|
|
406
389
|
const { query, sort, skip, limit, page } = DataQuery.parse(req.query);
|
|
@@ -493,9 +476,14 @@ class ObjectLayerService {
|
|
|
493
476
|
// Create temporary output file path
|
|
494
477
|
const tempOutputPath = `${framesFolder}/${tmpOutputFileName}`;
|
|
495
478
|
|
|
479
|
+
// img2webp takes one frame size, and frames of one direction may differ: the game
|
|
480
|
+
// stretches each to the entity's cell box, so the preview stretches them to the
|
|
481
|
+
// largest one — nearest-neighbour, since a scaled frame is still pixel art.
|
|
482
|
+
const stagedFolder = await ObjectLayerService.stageUniformFrames(framesFolder, pngFiles);
|
|
483
|
+
|
|
496
484
|
try {
|
|
497
485
|
// Change to the frames directory and execute img2webp command
|
|
498
|
-
const cmd = `cd "${framesFolder}" && img2webp -d ${frameDuration} -loop 0 *.png -o ${
|
|
486
|
+
const cmd = `cd "${stagedFolder ?? framesFolder}" && img2webp -d ${frameDuration} -loop 0 *.png -o "${path.resolve(tempOutputPath)}"`;
|
|
499
487
|
|
|
500
488
|
logger.info(`Executing command: ${cmd}`);
|
|
501
489
|
|
|
@@ -546,9 +534,41 @@ class ObjectLayerService {
|
|
|
546
534
|
}
|
|
547
535
|
|
|
548
536
|
throw error;
|
|
537
|
+
} finally {
|
|
538
|
+
if (stagedFolder) await fs.remove(stagedFolder);
|
|
549
539
|
}
|
|
550
540
|
};
|
|
551
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Stages copies of the frames resized to one common size when they differ, or returns null
|
|
544
|
+
* when they already share one and can be encoded in place.
|
|
545
|
+
* @function stageUniformFrames
|
|
546
|
+
* @memberof ObjectLayerService
|
|
547
|
+
* @param {string} framesFolder - Directory holding the direction's PNG frames.
|
|
548
|
+
* @param {string[]} pngFiles - Frame file names, in animation order.
|
|
549
|
+
* @returns {Promise<string|null>} Temporary directory with the resized frames, or null.
|
|
550
|
+
*/
|
|
551
|
+
static stageUniformFrames = async (framesFolder, pngFiles) => {
|
|
552
|
+
const sizes = await Promise.all(
|
|
553
|
+
pngFiles.map(async (file) => {
|
|
554
|
+
const { width, height } = await sharp(`${framesFolder}/${file}`).metadata();
|
|
555
|
+
return { file, width, height };
|
|
556
|
+
}),
|
|
557
|
+
);
|
|
558
|
+
const width = Math.max(...sizes.map((size) => size.width));
|
|
559
|
+
const height = Math.max(...sizes.map((size) => size.height));
|
|
560
|
+
if (sizes.every((size) => size.width === width && size.height === height)) return null;
|
|
561
|
+
|
|
562
|
+
const stagedFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'ol-webp-'));
|
|
563
|
+
for (const { file } of sizes) {
|
|
564
|
+
await sharp(`${framesFolder}/${file}`)
|
|
565
|
+
.resize(width, height, { fit: 'fill', kernel: 'nearest' })
|
|
566
|
+
.toFile(`${stagedFolder}/${file}`);
|
|
567
|
+
}
|
|
568
|
+
logger.info(`Staged ${pngFiles.length} frame(s) at ${width}x${height} for webp encoding`);
|
|
569
|
+
return stagedFolder;
|
|
570
|
+
};
|
|
571
|
+
|
|
552
572
|
/**
|
|
553
573
|
* PUT handler for updating object layers, their frame images, and metadata.
|
|
554
574
|
*
|
|
@@ -557,8 +577,8 @@ class ObjectLayerService {
|
|
|
557
577
|
* - `/:id/metadata/:itemType/:itemId` — Update metadata and reprocess all frames.
|
|
558
578
|
* - `/:id` — Standard update from request body.
|
|
559
579
|
*
|
|
560
|
-
* The `/metadata` route delegates to {@link ObjectLayerEngine.
|
|
561
|
-
* for document
|
|
580
|
+
* The `/metadata` route delegates to {@link ObjectLayerEngine.persistObjectLayerDocuments}
|
|
581
|
+
* for the document write, atlas regeneration, SHA-256 computation, and IPFS pinning.
|
|
562
582
|
*
|
|
563
583
|
* @async
|
|
564
584
|
* @function put
|
|
@@ -652,7 +672,6 @@ class ObjectLayerService {
|
|
|
652
672
|
|
|
653
673
|
// PUT /:id/metadata/:itemType/:itemId - Update object layer metadata and reprocess all frames
|
|
654
674
|
if (req.path.includes('/metadata/')) {
|
|
655
|
-
const objectLayerId = req.params.id;
|
|
656
675
|
const itemType = req.params.itemType;
|
|
657
676
|
const itemId = req.params.itemId;
|
|
658
677
|
|
|
@@ -683,24 +702,12 @@ class ObjectLayerService {
|
|
|
683
702
|
metadataOverride: req.body,
|
|
684
703
|
});
|
|
685
704
|
|
|
686
|
-
|
|
687
|
-
const { objectLayer } = await ObjectLayerEngine.updateObjectLayerDocuments({
|
|
688
|
-
objectLayerId,
|
|
705
|
+
const { objectLayer } = await ObjectLayerEngine.persistObjectLayerDocuments({
|
|
689
706
|
ObjectLayer,
|
|
690
707
|
ObjectLayerRenderFrames,
|
|
691
708
|
objectLayerRenderFramesData,
|
|
692
709
|
objectLayerData,
|
|
693
|
-
|
|
694
|
-
generateAtlas: true,
|
|
695
|
-
atlasServiceContext: {
|
|
696
|
-
req,
|
|
697
|
-
res,
|
|
698
|
-
options,
|
|
699
|
-
AtlasSpriteSheetService,
|
|
700
|
-
IpfsClient,
|
|
701
|
-
createPinRecord,
|
|
702
|
-
},
|
|
703
|
-
},
|
|
710
|
+
persistOptions: { options },
|
|
704
711
|
});
|
|
705
712
|
|
|
706
713
|
return objectLayer;
|
|
@@ -715,25 +722,19 @@ class ObjectLayerService {
|
|
|
715
722
|
// Apply body updates to staging data in memory
|
|
716
723
|
const updateData = { ...req.body };
|
|
717
724
|
const stagingData = updateData.data || existingOL.data.toObject();
|
|
725
|
+
stagingData.stats = validateStats(stagingData.stats);
|
|
718
726
|
if (!stagingData.render) stagingData.render = {};
|
|
719
727
|
|
|
720
|
-
|
|
721
|
-
const renderFramesData = existingOL.objectLayerRenderFramesId;
|
|
722
|
-
if (renderFramesData) {
|
|
728
|
+
if (existingOL.objectLayerRenderFramesId) {
|
|
723
729
|
try {
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
};
|
|
728
|
-
const result = await AtlasSpriteSheetService.generate(
|
|
729
|
-
{ objectLayer: stagingOL, auth: req.auth },
|
|
730
|
-
res,
|
|
730
|
+
const { atlasDoc, atlasCid, atlasMetadataCid } = await AtlasSpriteSheetStore.persist({
|
|
731
|
+
itemKey: stagingData.item.id,
|
|
732
|
+
objectLayerRenderFrames: existingOL.objectLayerRenderFramesId,
|
|
731
733
|
options,
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
stagingData.render.
|
|
735
|
-
|
|
736
|
-
updateData.atlasSpriteSheetId = result.atlasDoc._id;
|
|
734
|
+
});
|
|
735
|
+
stagingData.render.cid = atlasCid;
|
|
736
|
+
stagingData.render.metadataCid = atlasMetadataCid;
|
|
737
|
+
updateData.atlasSpriteSheetId = atlasDoc._id;
|
|
737
738
|
} catch (atlasError) {
|
|
738
739
|
logger.error('Failed to auto-update atlas for ObjectLayer:', atlasError);
|
|
739
740
|
}
|
|
@@ -742,25 +743,17 @@ class ObjectLayerService {
|
|
|
742
743
|
updateData.data = stagingData;
|
|
743
744
|
updateData.sha256 = ObjectLayerEngine.computeSha256(stagingData);
|
|
744
745
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
updateData.cid = ipfsResult.cid;
|
|
752
|
-
await createPinRecord({ cid: ipfsResult.cid, resourceType: 'object-layer-data', mfsPath, options });
|
|
753
|
-
}
|
|
754
|
-
} catch (ipfsError) {
|
|
755
|
-
logger.warn('Failed to pin data JSON to IPFS:', ipfsError.message);
|
|
756
|
-
}
|
|
746
|
+
const dataCid = await ObjectLayerEngine.pinObjectLayerData({
|
|
747
|
+
data: stagingData,
|
|
748
|
+
itemId: stagingData.item.id,
|
|
749
|
+
options,
|
|
750
|
+
});
|
|
751
|
+
if (dataCid) updateData.cid = dataCid;
|
|
757
752
|
|
|
758
|
-
|
|
759
|
-
let updatedObjectLayer = await ObjectLayer.findByIdAndUpdate(req.params.id, updateData, {
|
|
753
|
+
return await ObjectLayer.findByIdAndUpdate(req.params.id, updateData, {
|
|
760
754
|
returnDocument: 'after',
|
|
755
|
+
runValidators: true,
|
|
761
756
|
}).populate('objectLayerRenderFramesId');
|
|
762
|
-
|
|
763
|
-
return updatedObjectLayer;
|
|
764
757
|
};
|
|
765
758
|
|
|
766
759
|
/**
|
package/src/cli/app.js
CHANGED
|
@@ -205,14 +205,25 @@ class UnderpostApp {
|
|
|
205
205
|
// The resolution loadConf performed, not `ociEnv()`, which forces the overlay on: a
|
|
206
206
|
// skipped overlay and an applied one were indistinguishable in the pod log.
|
|
207
207
|
const resolved = deployEnvContentFactory(deployId, context.env, `${context.args?.['sub-conf'] ?? ''}`.trim());
|
|
208
|
+
const inContainer = Underpost.state.isInsideContainer();
|
|
208
209
|
logger.info('Deployment environment loaded', {
|
|
209
210
|
deployId,
|
|
210
211
|
instanceId: instanceId || null,
|
|
211
212
|
source: envPath,
|
|
212
|
-
inContainer
|
|
213
|
+
inContainer,
|
|
213
214
|
ociOverlay: resolved.overlay,
|
|
214
215
|
keys: Object.keys(values).length,
|
|
215
216
|
});
|
|
217
|
+
// A container that resolves no overlay runs on the host-scoped endpoints of the base file —
|
|
218
|
+
// a loopback database, a local cache — and fails minutes later at the first connection,
|
|
219
|
+
// pointing at the endpoint rather than at the conf checkout that is missing the overlay.
|
|
220
|
+
// Named rather than thrown: a deployment is allowed to have no overlay for an environment.
|
|
221
|
+
if (inContainer && !instanceId && !resolved.overlay)
|
|
222
|
+
logger.warn('No OCI env overlay resolved; container is running on host-scoped values', {
|
|
223
|
+
deployId,
|
|
224
|
+
env: context.env,
|
|
225
|
+
expected: deployOciEnvFilePath(deployId, context.env),
|
|
226
|
+
});
|
|
216
227
|
return { source: envPath, keys: Object.keys(values).length };
|
|
217
228
|
},
|
|
218
229
|
|
package/src/cli/client.js
CHANGED
|
@@ -83,7 +83,7 @@ class UnderpostClient {
|
|
|
83
83
|
const env = deployEnvFactory(options, process.env.NODE_ENV || 'development');
|
|
84
84
|
process.env.NODE_ENV = env;
|
|
85
85
|
if (options.mergeZip) {
|
|
86
|
-
mergeClientBuildZip({
|
|
86
|
+
await mergeClientBuildZip({
|
|
87
87
|
buildPrefix: options.mergeZip,
|
|
88
88
|
logger,
|
|
89
89
|
});
|
|
@@ -91,7 +91,7 @@ class UnderpostClient {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (options.unzip) {
|
|
94
|
-
unzipClientBuild({
|
|
94
|
+
await unzipClientBuild({
|
|
95
95
|
buildPrefix: options.unzip,
|
|
96
96
|
logger,
|
|
97
97
|
});
|
package/src/cli/cluster.js
CHANGED
|
@@ -31,10 +31,18 @@ import {
|
|
|
31
31
|
} from '../server/network/underpost-ingress.js';
|
|
32
32
|
import { MONGODB_DEFAULT_REPLICA_COUNT } from '../db/mongo/MongooseDB.js';
|
|
33
33
|
import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
|
|
34
|
+
import { MongoExpress } from '../db/mongo/MongoExpress.js';
|
|
34
35
|
import os from 'os';
|
|
35
36
|
import fs from 'fs-extra';
|
|
36
37
|
import Underpost from '../index.js';
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @constant CERT_MANAGER_VERSION
|
|
41
|
+
* @description Pinned cert-manager chart version.
|
|
42
|
+
* @memberof UnderpostCluster
|
|
43
|
+
*/
|
|
44
|
+
const CERT_MANAGER_VERSION = 'v1.17.0';
|
|
45
|
+
|
|
38
46
|
const logger = loggerFactory(import.meta);
|
|
39
47
|
|
|
40
48
|
const GATEWAY_API_RELEASE = 'v1.5.1';
|
|
@@ -82,6 +90,7 @@ class UnderpostCluster {
|
|
|
82
90
|
* @param {object} [options] - Configuration options for cluster initialization.
|
|
83
91
|
* @param {boolean} [options.mongodb=false] - Deploy MongoDB.
|
|
84
92
|
* @param {boolean} [options.mongodb4=false] - Deploy MongoDB 4.4.
|
|
93
|
+
* @param {boolean} [options.mongoExpress=false] - Deploy the mongo-express web client for inspecting the MongoDB StatefulSet collections.
|
|
85
94
|
* @param {string} [options.serviceHost=''] - Set a custom host/IP for exposed MongoDB and Valkey clients.
|
|
86
95
|
* @param {boolean} [options.mariadb=false] - Deploy MariaDB.
|
|
87
96
|
* @param {boolean} [options.mysql=false] - Deploy MySQL.
|
|
@@ -129,6 +138,7 @@ class UnderpostCluster {
|
|
|
129
138
|
options = {
|
|
130
139
|
mongodb: false,
|
|
131
140
|
mongodb4: false,
|
|
141
|
+
mongoExpress: false,
|
|
132
142
|
serviceHost: '',
|
|
133
143
|
mariadb: false,
|
|
134
144
|
mysql: false,
|
|
@@ -509,6 +519,10 @@ class UnderpostCluster {
|
|
|
509
519
|
});
|
|
510
520
|
}
|
|
511
521
|
|
|
522
|
+
// After both StatefulSet branches, so one invocation can bring up MongoDB and its inspector
|
|
523
|
+
// and the client reads the auth mode from the set this run just applied.
|
|
524
|
+
if (options.mongoExpress) await MongoExpress.deploy({ namespace: options.namespace, underpostRoot, options });
|
|
525
|
+
|
|
512
526
|
// Installing one stack must never break the other. Whichever is already
|
|
513
527
|
// present decides whether this install takes the node's 80/443 for itself
|
|
514
528
|
// or joins the underpost ingress, so it is read before anything is applied.
|
|
@@ -663,22 +677,12 @@ EOF
|
|
|
663
677
|
}
|
|
664
678
|
}
|
|
665
679
|
|
|
666
|
-
if (options.certManager)
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
--create-namespace \
|
|
673
|
-
--version v1.17.0 \
|
|
674
|
-
--set crds.enabled=true`,
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
const letsEncName = 'letsencrypt-prod';
|
|
679
|
-
shellExec(`sudo kubectl delete ClusterIssuer ${letsEncName} --ignore-not-found`);
|
|
680
|
-
shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml -n ${options.namespace}`);
|
|
681
|
-
}
|
|
680
|
+
if (options.certManager)
|
|
681
|
+
Underpost.cluster.installCertManager({
|
|
682
|
+
namespace: options.namespace,
|
|
683
|
+
env: options.dev ? 'development' : 'production',
|
|
684
|
+
solver: Underpost.cluster.acmeSolverFactory({ options, presence: Underpost.cluster.ingressStackPresence() }),
|
|
685
|
+
});
|
|
682
686
|
},
|
|
683
687
|
|
|
684
688
|
/**
|
|
@@ -1093,6 +1097,57 @@ EOF
|
|
|
1093
1097
|
return true;
|
|
1094
1098
|
},
|
|
1095
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* @method acmeSolverFactory
|
|
1102
|
+
* @description Which ingress stack answers the HTTP-01 challenge.
|
|
1103
|
+
*
|
|
1104
|
+
* Read from the cluster, like {@link UnderpostCluster.ingressStackPresence}: the
|
|
1105
|
+
* `cluster` runner installs the issuer in a separate invocation that carries no stack
|
|
1106
|
+
* flag, so the flags alone would always name the Contour solver. `--contour` is the
|
|
1107
|
+
* only explicit override; a cluster with the Gateway API data plane installed routes
|
|
1108
|
+
* every hostname through it, so an Ingress solver there is unreachable.
|
|
1109
|
+
* @param {object} params
|
|
1110
|
+
* @param {object} [params.options] - Cluster init options.
|
|
1111
|
+
* @param {{contour: boolean, gateway: boolean}} params.presence - Installed stacks.
|
|
1112
|
+
* @returns {'gateway'|'ingress'} Solver kind.
|
|
1113
|
+
* @memberof UnderpostCluster
|
|
1114
|
+
*/
|
|
1115
|
+
acmeSolverFactory({ options = {}, presence }) {
|
|
1116
|
+
if (options.contour === true && options.gatewayApi !== true) return 'ingress';
|
|
1117
|
+
return options.gatewayApi === true || presence.gateway ? 'gateway' : 'ingress';
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* @method installCertManager
|
|
1122
|
+
* @description Installs or upgrades cert-manager and applies the Let's Encrypt
|
|
1123
|
+
* ClusterIssuer for the stack in use.
|
|
1124
|
+
*
|
|
1125
|
+
* `upgrade --install` rather than a one-time install: the Gateway API solver is
|
|
1126
|
+
* ignored by a controller that was not started with `enableGatewayAPI`, so a cluster
|
|
1127
|
+
* whose cert-manager predates the Gateway API migration must be re-configured, not
|
|
1128
|
+
* skipped because a pod is Running. The version is pinned, so on a cluster already at
|
|
1129
|
+
* it the upgrade is a no-op.
|
|
1130
|
+
* @param {object} params
|
|
1131
|
+
* @param {string} [params.namespace] - Namespace the deploys' Gateways live in.
|
|
1132
|
+
* @param {string} [params.env] - Environment the routed deploys publish under.
|
|
1133
|
+
* @param {'gateway'|'ingress'} params.solver - Which stack answers the challenge.
|
|
1134
|
+
* @returns {{solver: string, parentRefs: Array<{name: string, namespace: string}>}} What was applied.
|
|
1135
|
+
* @memberof UnderpostCluster
|
|
1136
|
+
*/
|
|
1137
|
+
installCertManager({ namespace = 'default', env = 'production', solver }) {
|
|
1138
|
+
const gatewayValues =
|
|
1139
|
+
solver === 'gateway'
|
|
1140
|
+
? ' --set config.apiVersion=controller.config.cert-manager.io/v1alpha1' +
|
|
1141
|
+
' --set config.kind=ControllerConfiguration --set config.enableGatewayAPI=true'
|
|
1142
|
+
: '';
|
|
1143
|
+
shellExec(`helm repo add jetstack https://charts.jetstack.io --force-update`);
|
|
1144
|
+
shellExec(
|
|
1145
|
+
`helm upgrade --install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace` +
|
|
1146
|
+
` --version ${CERT_MANAGER_VERSION} --set crds.enabled=true${gatewayValues} --wait --timeout 5m`,
|
|
1147
|
+
);
|
|
1148
|
+
return Underpost.deploy.applyLetsEncryptIssuer({ solver, namespace, env });
|
|
1149
|
+
},
|
|
1150
|
+
|
|
1096
1151
|
/**
|
|
1097
1152
|
* @method ingressStackPresence
|
|
1098
1153
|
* @description Which ingress data planes are installed right now.
|
|
@@ -1351,14 +1406,21 @@ EOF
|
|
|
1351
1406
|
}
|
|
1352
1407
|
|
|
1353
1408
|
try {
|
|
1354
|
-
|
|
1409
|
+
// The manifest carries the whole nginx.conf: echoing it as the command line
|
|
1410
|
+
// buries the lines that follow it — the fallback probes above all — under
|
|
1411
|
+
// a page of routing tables on every sync.
|
|
1412
|
+
logger.info('Applying the underpost ingress manifests', { namespace, node: ingressNode });
|
|
1413
|
+
shellExec(
|
|
1414
|
+
`kubectl apply -f - -n ${namespace} <<'EOF'
|
|
1355
1415
|
${underpostIngressManifestsFactory({
|
|
1356
1416
|
namespace,
|
|
1357
1417
|
conf,
|
|
1358
1418
|
nodeName: ingressNode,
|
|
1359
1419
|
})}
|
|
1360
1420
|
EOF
|
|
1361
|
-
|
|
1421
|
+
`,
|
|
1422
|
+
{ disableLog: true },
|
|
1423
|
+
);
|
|
1362
1424
|
} catch (error) {
|
|
1363
1425
|
if (shouldHotReload)
|
|
1364
1426
|
execIngress(`sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`, {
|