@underpostnet/cyberia 3.3.0 → 3.3.76
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/.dockerignore +1 -0
- package/.env.example +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +39 -14
- package/.github/workflows/cyberia-client.cd.yml +5 -0
- package/.github/workflows/cyberia-server.cd.yml +5 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
- package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
- package/.github/workflows/engine-cyberia.cd.yml +3 -0
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +35 -20
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +1 -0
- package/.prettierignore +1 -0
- package/AGENTS.md +96 -0
- package/CHANGELOG.md +653 -2513
- package/CLI-HELP.md +323 -157
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +5 -5
- package/README.md +48 -28
- package/bin/build.js +86 -41
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +1494 -1403
- package/bin/deploy.js +18 -104
- package/bin/index.js +1494 -1403
- package/compose.env +17 -5
- package/conf.js +98 -7
- package/deploy/cyberia-client/deploy.sh +46 -0
- package/deploy/cyberia-client/package.sh +17 -0
- package/deploy/cyberia-client/state.sh +15 -0
- package/deploy/cyberia-server/deploy.sh +46 -0
- package/deploy/cyberia-server/package.sh +17 -0
- package/deploy/cyberia-server/state.sh +15 -0
- package/deploy/dd-cyberia/deploy.sh +9 -29
- package/deploy/dd-cyberia/init.sh +38 -45
- package/deploy/dd-cyberia/package.sh +16 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +133 -147
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +610 -0
- package/deploy/lib/host.sh +194 -0
- package/deploy/lib/state.sh +65 -0
- package/deploy/pwa-microservices-template/deploy.sh +17 -42
- package/deploy/release/deploy.sh +9 -41
- package/deployment.yaml +7 -2
- package/docker-compose.yml +23 -17
- package/examples/static-page/README.md +19 -19
- package/examples/static-page/ssr-components/CustomPage.js +2 -2
- package/hardhat/hardhat.config.js +1 -6
- package/hardhat/package-lock.json +101 -40
- package/hardhat/package.json +2 -2
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- 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/manifests/grafana/deployment.yaml +26 -7
- package/manifests/grafana/kustomization.yaml +1 -0
- package/manifests/grafana/service.yaml +6 -1
- package/manifests/mariadb/pv.yaml +1 -0
- package/manifests/mongodb/statefulset.yaml +9 -2
- package/manifests/mysql/pv-pvc.yaml +14 -1
- package/manifests/postgresql/pv.yaml +12 -1
- package/manifests/postgresql/pvc.yaml +1 -0
- package/manifests/prometheus/deployment.yaml +18 -22
- package/package.json +87 -12
- package/scripts/event-monitor.sh +56 -0
- package/scripts/k3s-node-setup.sh +27 -14
- package/scripts/kubeadm-node-setup.sh +31 -11
- package/scripts/maas-setup.sh +8 -8
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +5 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +15 -10
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +7 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +90 -186
- package/src/api/core/core.controller.js +1 -1
- package/src/api/core/core.router.js +2 -2
- package/src/api/core/core.service.js +2 -2
- package/src/api/crypto/crypto.controller.js +1 -1
- package/src/api/crypto/crypto.service.js +1 -1
- package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
- package/src/api/cyberia-action/cyberia-action.router.js +3 -3
- package/src/api/cyberia-action/cyberia-action.service.js +5 -6
- 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 +2 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +13 -35
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +17 -34
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
- package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +3 -3
- 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 +17 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +468 -5
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +502 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +29 -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 +35 -142
- package/src/api/cyberia-instance/cyberia-instance.controller.js +2 -5
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +5 -18
- package/src/api/cyberia-instance/cyberia-instance.service.js +5 -45
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +76 -3
- package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
- package/src/api/cyberia-map/cyberia-map.router.js +1 -1
- package/src/api/cyberia-map/cyberia-map.service.js +8 -4
- 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.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +4 -4
- package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
- package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -214
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
- package/src/api/default/default.controller.js +1 -1
- package/src/api/default/default.router.js +1 -1
- package/src/api/default/default.service.js +2 -2
- package/src/api/document/document.controller.js +1 -1
- package/src/api/document/document.router.js +1 -1
- package/src/api/document/document.service.js +2 -2
- package/src/api/file/file.controller.js +1 -1
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/file/file.router.js +1 -1
- package/src/api/file/file.service.js +2 -2
- package/src/api/instance/instance.controller.js +1 -1
- package/src/api/instance/instance.router.js +1 -1
- package/src/api/instance/instance.service.js +2 -2
- package/src/api/ipfs/ipfs.controller.js +1 -1
- package/src/api/ipfs/ipfs.router.js +2 -2
- package/src/api/ipfs/ipfs.service.js +2 -2
- package/src/api/object-layer/object-layer.controller.js +1 -1
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +12 -5
- package/src/api/object-layer/object-layer.service.js +116 -123
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
- package/src/api/test/test.controller.js +1 -1
- package/src/api/test/test.service.js +1 -1
- package/src/api/user/guest.service.js +3 -3
- package/src/api/user/user.controller.js +1 -1
- package/src/api/user/user.router.js +2 -2
- package/src/api/user/user.service.js +4 -4
- package/src/api.js +5 -9
- package/src/cli/app.js +402 -0
- package/src/cli/baremetal.js +30 -35
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +191 -82
- package/src/cli/db.js +120 -68
- package/src/cli/deploy.js +157 -75
- package/src/cli/docker-compose.js +5 -5
- package/src/cli/domains.js +184 -0
- package/src/cli/dotenv-store.js +143 -0
- package/src/cli/event.js +2281 -0
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +159 -343
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -6
- package/src/cli/index.js +343 -151
- package/src/cli/ipfs.js +34 -30
- package/src/cli/kickstart.js +1 -1
- package/src/cli/kubectl.js +218 -41
- package/src/cli/lxd.js +3 -3
- package/src/cli/monitor.js +1359 -26
- package/src/cli/package.js +90 -0
- package/src/cli/release.js +16 -12
- package/src/cli/repository.js +586 -363
- package/src/cli/run.js +398 -843
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +478 -279
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +1 -1
- package/src/cli/test.js +203 -125
- package/src/cli/vultr.js +61 -21
- package/src/cli/wireguard.js +1112 -306
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +327 -303
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/Modal.js +56 -1
- package/src/client/components/core/PanelForm.js +4 -4
- package/src/client/components/core/Repository.js +56 -0
- package/src/client/components/core/Translate.js +7 -0
- package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
- package/src/client/components/core/Wallet.js +0 -11
- package/src/client/components/core/Worker.js +7 -1
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -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 +2 -35
- 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/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -1
- package/src/client/components/default/SettingsDefault.js +2 -2
- package/src/client/components/itemledger/SettingsItemledger.js +2 -2
- package/src/client/components/underpost/AppShellUnderpost.js +3 -2
- package/src/client/components/underpost/SettingsUnderpost.js +2 -2
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +313 -30
- 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 +48 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
- 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/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-384x384.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-512x512.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/default/assets/background/white.jpg +0 -0
- package/src/client/public/default/browserconfig.xml +1 -1
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon-48x48.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/manifest.webmanifest +5 -5
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +1 -1
- 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/RootDocument.js +1 -1
- package/src/client/ssr/body/404.js +1 -1
- package/src/client/ssr/body/500.js +1 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
- package/src/client/ssr/head/CryptokoynScripts.js +1 -1
- package/src/client/ssr/head/Css.js +1 -1
- package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +1 -1
- package/src/client/ssr/head/ItemledgerScripts.js +1 -1
- package/src/client/ssr/head/Microdata.js +1 -1
- package/src/client/ssr/head/Production.js +1 -1
- package/src/client/ssr/head/Pwa.js +1 -1
- package/src/client/ssr/head/PwaItemledger.js +1 -1
- package/src/client/ssr/head/Seo.js +1 -1
- package/src/client/ssr/head/UnderpostScripts.js +1 -1
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
- package/src/client/ssr/views/Cyberia404.js +7 -5
- package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/views/Maintenance.js +1 -1
- package/src/client/ssr/views/NoNetworkConnection.js +1 -1
- package/src/client/ssr/views/Test.js +1 -1
- package/src/client/sw/core.sw.js +20 -14
- package/src/client-builder/client-build-docs.js +92 -77
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +105 -74
- package/src/client-builder/client-bundle.js +347 -0
- package/src/client-builder/client-dev-server.js +3 -3
- package/src/client-builder/client-icons.js +2 -2
- package/src/client-builder/ssr.js +3 -3
- package/src/client.build.js +3 -6
- package/src/client.dev.js +3 -6
- package/src/db/DataBaseProvider.js +186 -3
- package/src/db/mariadb/MariaDB.js +3 -2
- package/src/db/mongo/MongoBootstrap.js +273 -101
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/db/mongo/MongooseDB.js +7 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +6 -8
- package/src/index.js +79 -23
- package/src/mailer/EmailRender.js +2 -2
- package/src/mailer/MailerInterceptor.js +87 -0
- package/src/mailer/MailerProvider.js +50 -6
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +144 -147
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +8 -17
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +54 -10
- package/src/projects/cyberia/gemini-client.js +4 -4
- package/src/projects/cyberia/generate-saga.js +5 -12
- package/src/projects/cyberia/hot-reload-trigger.js +18 -29
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +178 -258
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +33 -69
- package/src/projects/cyberia/object-layer.js +153 -320
- 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 +3 -19
- 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/projects/underpost/catalog-underpost.js +9 -1
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +17 -8
- package/src/runtime/cyberia-client/Dockerfile.dev +13 -4
- package/src/runtime/cyberia-server/Dockerfile +18 -6
- package/src/runtime/cyberia-server/Dockerfile.dev +16 -3
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +5 -5
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/runtime/express/Express.js +32 -27
- package/src/runtime/lampp/Lampp.js +22 -70
- package/src/runtime/nginx/Nginx.js +1 -1
- package/src/runtime/wp/Wp.js +16 -48
- package/src/server/{catalog.js → build/catalog.js} +15 -5
- package/src/server/build/coverage.js +188 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +508 -0
- package/src/server/build/testing.js +578 -0
- package/src/server/{dns.js → network/dns.js} +144 -11
- package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
- package/src/server/{middlewares.js → network/middlewares.js} +11 -5
- package/src/server/network/node-capability.js +98 -0
- package/src/server/{peer.js → network/peer.js} +3 -3
- package/src/server/{proxy.js → network/proxy.js} +7 -13
- package/src/server/network/router.js +356 -0
- package/src/server/{tls.js → network/tls.js} +2 -2
- package/src/server/{underpost-compression.js → network/underpost-compression.js} +1 -1
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/{cron.js → ops/cron.js} +349 -74
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +143 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/{systemd.js → ops/systemd.js} +55 -1
- package/src/server/{conf.js → runtime/conf.js} +516 -423
- package/src/server/runtime/config-scope.js +215 -0
- package/src/server/runtime/environment.js +149 -0
- package/src/server/{process.js → runtime/process.js} +153 -12
- package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
- package/src/server/{runtime.js → runtime/runtime.js} +12 -8
- package/src/server/{start.js → runtime/start.js} +128 -39
- package/src/server/{auth.js → security/auth.js} +7 -8
- package/src/server/security/container-storage.js +382 -0
- package/src/server/{crypto.js → security/crypto.js} +1 -1
- package/src/server/{selinux.js → security/selinux.js} +1 -1
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +17 -1
- package/src/server/storage/downloader.js +121 -0
- package/src/server/storage/repository.js +68 -0
- package/src/server/storage/zip.js +153 -0
- package/src/server.js +4 -7
- package/src/ws/IoInterface.js +1 -1
- package/src/ws/IoServer.js +1 -1
- package/src/ws/core/core.ws.connection.js +1 -1
- package/src/ws/core/core.ws.emit.js +1 -1
- package/src/ws/core/core.ws.server.js +1 -1
- package/src/ws/default/default.ws.connection.js +1 -1
- package/src/ws/default/default.ws.emit.js +1 -1
- package/src/ws/default/default.ws.server.js +1 -1
- package/test/e2e/event-e2e-public-ingress-down.js +45 -0
- package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
- package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
- package/test/{api.test.js → integration/app/api.test.js} +33 -14
- 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 +64 -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/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +17 -23
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +466 -0
- 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 +338 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- 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/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
- package/test/integration/infra/1-security/config-scope.test.js +305 -0
- package/test/integration/infra/1-security/container-storage.test.js +356 -0
- package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
- package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
- package/test/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
- package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
- package/test/integration/infra/1-security/systemd-service.test.js +70 -0
- package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
- package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
- package/test/integration/infra/2-network/node-capability.test.js +182 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +1187 -0
- package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +649 -55
- package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
- package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
- package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
- 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/deploy-routes.test.js +379 -0
- package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
- package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
- package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +6 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -4
- package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
- package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
- package/test/integration/infra/5-observability/event-notification.test.js +197 -0
- package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
- package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
- package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
- package/test/support/shell-harness.js +73 -0
- package/test/unit/build-template.test.js +97 -0
- package/test/unit/build-workflow-offline.test.js +132 -0
- package/test/unit/catalog.test.js +71 -0
- package/test/unit/client-build-docs.test.js +361 -0
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +996 -0
- package/test/unit/coverage-artifact.test.js +221 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/cyberia/instance-backup.test.js +183 -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-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +518 -0
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/logger-redaction.test.js +141 -0
- package/test/unit/package.test.js +457 -0
- package/test/unit/prepare-host.test.js +382 -0
- package/test/unit/process-environment.test.js +18 -0
- package/test/unit/propagation-message.test.js +78 -0
- package/test/unit/redeploy-plan.test.js +24 -0
- package/test/unit/release-bump.test.js +474 -0
- package/test/unit/ssh-output-redaction.test.js +28 -0
- package/test/unit/start-options.test.js +46 -0
- package/test/unit/test-tiers.test.js +220 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +66 -0
- package/.nycrc +0 -9
- package/deploy/lib/logging.sh +0 -96
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/coverall-test-cyberia.sh +0 -24
- package/scripts/coverall-test.sh +0 -24
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/api/user/user.build.js +0 -16
- package/src/cli/env.js +0 -177
- package/src/client/components/core/Blockchain.js +0 -41
- package/src/client/components/core/Blog.js +0 -9
- package/src/client/components/core/KeyboardAvoidance.js +0 -145
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/white0-min.jpg +0 -0
- package/src/client/public/default/assets/background/white0.jpg +0 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +0 -148
- package/src/server/downloader.js +0 -73
- package/src/server/environment.js +0 -98
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import UnderpostRepository from './repository.js';
|
|
4
|
+
|
|
5
|
+
const manifestFilterNames = ['key', 'fromKey', 'toKey', 'keyRegex'];
|
|
6
|
+
|
|
7
|
+
const normalizeStoragePath = (value) => {
|
|
8
|
+
if (value === undefined || value === '') return '';
|
|
9
|
+
const absolute = path.resolve(value);
|
|
10
|
+
const relative = path.relative(process.cwd(), absolute);
|
|
11
|
+
return (
|
|
12
|
+
path.isAbsolute(value) && (relative === '..' || relative.startsWith(`..${path.sep}`)) ? absolute : relative || '.'
|
|
13
|
+
)
|
|
14
|
+
.split(path.sep)
|
|
15
|
+
.join('/');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const isWithinScope = (key, scope) => {
|
|
19
|
+
if (scope === undefined || scope === '') return true;
|
|
20
|
+
const relative = path.relative(path.resolve(scope), path.resolve(key));
|
|
21
|
+
return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const validateSelectionOptions = (scope, options) => {
|
|
25
|
+
if (options.git && options.tracked) throw new Error('Use either --git or --tracked.');
|
|
26
|
+
if (!options.tracked && !scope) throw new Error('Provide a filesystem path, or use --tracked.');
|
|
27
|
+
if (options.rm && options.pull) throw new Error('Use either --rm or --pull.');
|
|
28
|
+
if (options.omitUnzip && !options.pull) throw new Error('--omit-unzip requires --pull.');
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const filterManifestEntries = (entries, options = {}, scope) => {
|
|
32
|
+
const keys = entries.map(([key]) => key);
|
|
33
|
+
const keyIndex = (name) => {
|
|
34
|
+
const index = keys.indexOf(options[name]);
|
|
35
|
+
if (index < 0) throw new Error(`Manifest key not found for ${name}: ${options[name]}`);
|
|
36
|
+
return index;
|
|
37
|
+
};
|
|
38
|
+
const start = options.fromKey === undefined ? 0 : keyIndex('fromKey');
|
|
39
|
+
const end = options.toKey === undefined ? keys.length - 1 : keyIndex('toKey');
|
|
40
|
+
if (start > end && (options.fromKey !== undefined || options.toKey !== undefined))
|
|
41
|
+
throw new Error('--from-key must precede or equal --to-key.');
|
|
42
|
+
|
|
43
|
+
let regex;
|
|
44
|
+
if (options.keyRegex !== undefined) {
|
|
45
|
+
try {
|
|
46
|
+
regex = new RegExp(options.keyRegex);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error(`Invalid --key-regex: ${options.keyRegex}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const matches = (key) => isWithinScope(key, scope) && (!regex || regex.test(key));
|
|
52
|
+
if (options.key !== undefined) {
|
|
53
|
+
const index = keyIndex('key');
|
|
54
|
+
if (index < start || index > end || !matches(options.key))
|
|
55
|
+
throw new Error('--key conflicts with the manifest range, path scope, or regex.');
|
|
56
|
+
}
|
|
57
|
+
return entries
|
|
58
|
+
.slice(start, end + 1)
|
|
59
|
+
.filter(([key]) => matches(key) && (options.key === undefined || key === options.key));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const resolveFilesystemSelection = (scope) => {
|
|
63
|
+
const files = [];
|
|
64
|
+
const visit = (target) => {
|
|
65
|
+
const stats = fs.statSync(target);
|
|
66
|
+
if (stats.isFile()) files.push(normalizeStoragePath(target));
|
|
67
|
+
else if (stats.isDirectory()) {
|
|
68
|
+
for (const entry of fs
|
|
69
|
+
.readdirSync(target, { withFileTypes: true })
|
|
70
|
+
.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
71
|
+
const child = path.join(target, entry.name);
|
|
72
|
+
// Do not follow directory links during traversal.
|
|
73
|
+
if (entry.isSymbolicLink()) {
|
|
74
|
+
if (fs.statSync(child, { throwIfNoEntry: false })?.isFile()) files.push(normalizeStoragePath(child));
|
|
75
|
+
} else visit(child);
|
|
76
|
+
}
|
|
77
|
+
} else throw new Error(`Storage requires a regular file or directory: ${target}`);
|
|
78
|
+
};
|
|
79
|
+
visit(scope);
|
|
80
|
+
return files;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const resolveGitSelection = (files, scope) => {
|
|
84
|
+
const context = fs.statSync(scope).isDirectory() ? scope : path.dirname(scope);
|
|
85
|
+
const tracked = new Set(UnderpostRepository.API.getTrackedFiles(context));
|
|
86
|
+
return files.filter((file) => tracked.has(path.resolve(file)));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const resolveTrackedSelection = (entries) => entries.map(([key]) => key);
|
|
90
|
+
|
|
91
|
+
const resolveSelection = (scope, manifest, options = {}) => {
|
|
92
|
+
validateSelectionOptions(scope, options);
|
|
93
|
+
const entries = Object.entries(manifest);
|
|
94
|
+
const filtered = filterManifestEntries(entries, options, scope);
|
|
95
|
+
if (options.tracked) return resolveTrackedSelection(filtered);
|
|
96
|
+
|
|
97
|
+
let files = resolveFilesystemSelection(scope);
|
|
98
|
+
if (options.git) files = resolveGitSelection(files, scope);
|
|
99
|
+
const manifestKeys = new Map(entries.map(([key]) => [normalizeStoragePath(key), key]));
|
|
100
|
+
files = files.map((file) => manifestKeys.get(file) ?? file);
|
|
101
|
+
if (manifestFilterNames.some((name) => options[name] !== undefined)) {
|
|
102
|
+
const selected = new Set(resolveTrackedSelection(filtered));
|
|
103
|
+
files = files.filter((file) => selected.has(file));
|
|
104
|
+
}
|
|
105
|
+
return files;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
normalizeStoragePath,
|
|
110
|
+
filterManifestEntries,
|
|
111
|
+
resolveFilesystemSelection,
|
|
112
|
+
resolveGitSelection,
|
|
113
|
+
resolveTrackedSelection,
|
|
114
|
+
resolveSelection,
|
|
115
|
+
};
|
package/src/cli/fs.js
CHANGED
|
@@ -1,378 +1,194 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File storage module for managing file operations using Cloudinary.
|
|
3
|
-
* @module src/cli/fs.js
|
|
4
|
-
* @namespace UnderpostFileStorage
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import { v2 as cloudinary } from 'cloudinary';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import * as dir from 'path';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import * as dir from 'node:path';
|
|
11
4
|
import fs from 'fs-extra';
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
5
|
+
import { loggerFactory } from '../server/ops/logger.js';
|
|
6
|
+
import { readZipEntry } from '../server/storage/zip.js';
|
|
7
|
+
import Downloader from '../server/storage/downloader.js';
|
|
8
|
+
import * as selection from './fs-selection.js';
|
|
15
9
|
|
|
16
10
|
const logger = loggerFactory(import.meta);
|
|
11
|
+
const CLOUDINARY_DELIVERY_TYPE = 'private';
|
|
12
|
+
const CLOUDINARY_ACCESS_CONTROL = [{ access_type: 'token' }];
|
|
13
|
+
const DELIVERY_TYPES = ['private', 'upload', 'authenticated'];
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* This class provides a set of static methods to upload, pull, and delete files
|
|
22
|
-
* from Cloudinary, as well as manage a local storage configuration file.
|
|
16
|
+
* What a pull reports when the local file is already there and no overwrite was asked for.
|
|
17
|
+
* Distinct from a delivery type, which is what a pull that actually transferred returns.
|
|
23
18
|
*/
|
|
19
|
+
const PULL_SKIPPED = 'skipped';
|
|
20
|
+
const manifestIdPattern = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
|
|
21
|
+
|
|
24
22
|
class UnderpostFileStorage {
|
|
25
23
|
static API = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* @description Configures the Cloudinary client with environment variables.
|
|
29
|
-
* @memberof UnderpostFileStorage
|
|
30
|
-
*/
|
|
24
|
+
...selection,
|
|
25
|
+
|
|
31
26
|
cloudinaryConfig() {
|
|
32
|
-
|
|
33
|
-
cloudinary.config({
|
|
27
|
+
const config = {
|
|
34
28
|
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
|
|
35
29
|
api_key: process.env.CLOUDINARY_API_KEY,
|
|
36
30
|
api_secret: process.env.CLOUDINARY_API_SECRET,
|
|
37
|
-
}
|
|
31
|
+
};
|
|
32
|
+
if (Object.values(config).some((value) => !value))
|
|
33
|
+
throw new Error('Set CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, and CLOUDINARY_API_SECRET in the environment.');
|
|
34
|
+
cloudinary.config(config);
|
|
38
35
|
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
|
|
37
|
+
resolveManifest(options = {}) {
|
|
38
|
+
if (options.storageFilePath !== undefined)
|
|
39
|
+
throw new Error('Use --storage-id with a sub-id, not a manifest path.');
|
|
40
|
+
if (typeof options.deployId !== 'string' || !manifestIdPattern.test(options.deployId))
|
|
41
|
+
throw new Error('Provide a valid --deploy-id using letters, digits, hyphens, or underscores.');
|
|
42
|
+
if (
|
|
43
|
+
options.storageId !== undefined &&
|
|
44
|
+
(typeof options.storageId !== 'string' || !manifestIdPattern.test(options.storageId))
|
|
45
|
+
)
|
|
46
|
+
throw new Error('Use a --storage-id sub-id with letters, digits, hyphens, or underscores.');
|
|
47
|
+
const name = options.storageId === undefined ? 'storage.json' : `storage.${options.storageId}.json`;
|
|
48
|
+
return `engine-private/conf/${options.deployId}/${name}`;
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
readManifest(options = {}) {
|
|
52
|
+
const storageConf = UnderpostFileStorage.API.resolveManifest(options);
|
|
53
|
+
const storage = fs.existsSync(storageConf) ? JSON.parse(fs.readFileSync(storageConf, 'utf8')) : {};
|
|
54
|
+
if (!storage || typeof storage !== 'object' || Array.isArray(storage))
|
|
55
|
+
throw new Error(`Manifest must contain an object: ${storageConf}`);
|
|
56
|
+
const paths = new Set();
|
|
57
|
+
for (const [key, entry] of Object.entries(storage)) {
|
|
58
|
+
const normalized = selection.normalizeStoragePath(key);
|
|
59
|
+
if (!key || !entry || typeof entry !== 'object' || Array.isArray(entry) || paths.has(normalized))
|
|
60
|
+
throw new Error(`Invalid or duplicate manifest entry: ${key}`);
|
|
61
|
+
if (entry.type !== undefined && !DELIVERY_TYPES.includes(entry.type))
|
|
62
|
+
throw new Error(`Invalid manifest delivery type: ${key}`);
|
|
63
|
+
if (entry.bytes !== undefined && (!Number.isSafeInteger(entry.bytes) || entry.bytes < 0))
|
|
64
|
+
throw new Error(`Invalid manifest byte count: ${key}`);
|
|
65
|
+
paths.add(normalized);
|
|
54
66
|
}
|
|
55
67
|
return { storage, storageConf };
|
|
56
68
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @param {string} storageConf - The path to the storage configuration file.
|
|
62
|
-
* @memberof UnderpostFileStorage
|
|
63
|
-
*/
|
|
64
|
-
writeStorageConf(storage, storageConf) {
|
|
65
|
-
if (storage) fs.writeFileSync(storageConf, JSON.stringify(storage, null, 4), 'utf8');
|
|
66
|
-
},
|
|
67
|
-
/**
|
|
68
|
-
* @method gitTrack
|
|
69
|
-
* @description Optional, non-fatal Git tracking layer. Any Git error is logged and swallowed
|
|
70
|
-
* so it can never interrupt or roll back the canonical `storage.*.json` workflow.
|
|
71
|
-
* @param {string} gitPath - The working directory to stage/commit.
|
|
72
|
-
* @param {object} [options] - Tracking options.
|
|
73
|
-
* @param {boolean} [options.init=false] - If true, initialize a local repo before staging.
|
|
74
|
-
* @param {string} [options.message=''] - Explicit commit message; when omitted, `underpost cmt` is used.
|
|
75
|
-
* @memberof UnderpostFileStorage
|
|
76
|
-
*/
|
|
77
|
-
gitTrack(gitPath, options = { init: false, message: '' }) {
|
|
69
|
+
|
|
70
|
+
writeManifest(storage, storageConf) {
|
|
71
|
+
fs.ensureDirSync(dir.dirname(storageConf));
|
|
72
|
+
const temporary = `${storageConf}.${randomUUID()}.tmp`;
|
|
78
73
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
silentOnError: true,
|
|
84
|
-
silent: true,
|
|
85
|
-
disableLog: true,
|
|
86
|
-
});
|
|
87
|
-
else shellExec(`underpost cmt ${gitPath} feat`, { silentOnError: true, silent: true, disableLog: true });
|
|
88
|
-
} catch (error) {
|
|
89
|
-
logger.warn('git tracking skipped (non-fatal)', { gitPath, error: error?.message });
|
|
74
|
+
fs.writeFileSync(temporary, JSON.stringify(storage, null, 4), { encoding: 'utf8', flag: 'wx' });
|
|
75
|
+
fs.renameSync(temporary, storageConf);
|
|
76
|
+
} finally {
|
|
77
|
+
fs.removeSync(temporary);
|
|
90
78
|
}
|
|
91
79
|
},
|
|
92
|
-
/**
|
|
93
|
-
* @method recursiveCallback
|
|
94
|
-
* @description Recursively processes files and directories based on the provided options.
|
|
95
|
-
* @param {string} path - The path to the directory to process.
|
|
96
|
-
* @param {object} [options] - An object containing options for the recursive callback.
|
|
97
|
-
* @param {boolean} [options.rm=false] - Flag to remove files and directories.
|
|
98
|
-
* @param {boolean} [options.recursive=false] - Flag to process directories recursively.
|
|
99
|
-
* @param {string} [options.deployId=''] - The identifier for the deployment.
|
|
100
|
-
* @param {boolean} [options.force=false] - Flag to force file operations.
|
|
101
|
-
* @param {boolean} [options.pull=false] - Flag to pull files from storage.
|
|
102
|
-
* @param {boolean} [options.git=false] - Flag to use Git for file operations.
|
|
103
|
-
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
104
|
-
* @param {string} [options.storageFilePath=''] - The path to the storage configuration file.
|
|
105
|
-
* @returns {Promise<void>} A promise that resolves when the recursive callback is complete.
|
|
106
|
-
* @memberof UnderpostFileStorage
|
|
107
|
-
*/
|
|
108
|
-
async recursiveCallback(
|
|
109
|
-
path,
|
|
110
|
-
options = {
|
|
111
|
-
rm: false,
|
|
112
|
-
recursive: false,
|
|
113
|
-
deployId: '',
|
|
114
|
-
force: false,
|
|
115
|
-
pull: false,
|
|
116
|
-
git: false,
|
|
117
|
-
omitUnzip: false,
|
|
118
|
-
storageFilePath: '',
|
|
119
|
-
},
|
|
120
|
-
) {
|
|
121
|
-
const { storage, storageConf } = Underpost.fs.getStorageConf(options);
|
|
122
|
-
|
|
123
|
-
// ── Single-file handling: when path is a file (not a directory), use parent dir
|
|
124
|
-
// as the git working directory and process just that one file. ──────────────
|
|
125
|
-
let isSingleFile = false;
|
|
126
|
-
let parentDir = path;
|
|
127
|
-
let singleFileName = '';
|
|
128
|
-
if (fs.existsSync(path) && !fs.statSync(path).isDirectory()) {
|
|
129
|
-
isSingleFile = true;
|
|
130
|
-
parentDir = dir.dirname(path);
|
|
131
|
-
singleFileName = path.split('/').pop();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// In recursive remove mode, delete every tracked storage key under the requested path,
|
|
135
|
-
// even when local files/directories are already missing.
|
|
136
|
-
if (options.rm === true) {
|
|
137
|
-
const normalizedPath = typeof path === 'string' ? path.trim() : '';
|
|
138
|
-
const basePath = normalizedPath.replace(/\/+$/, '');
|
|
139
|
-
const hasPathFilter = basePath.length > 0;
|
|
140
|
-
|
|
141
|
-
const associatedPaths = Object.keys(storage || {}).filter((storedPath) => {
|
|
142
|
-
if (!hasPathFilter) return true;
|
|
143
|
-
return storedPath === basePath || storedPath.startsWith(`${basePath}/`);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
for (const associatedPath of associatedPaths) {
|
|
147
|
-
await Underpost.fs.delete(associatedPath);
|
|
148
|
-
if (storage) delete storage[associatedPath];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (hasPathFilter && options.force === true && fs.existsSync(basePath)) fs.removeSync(basePath);
|
|
152
|
-
|
|
153
|
-
// Storage is canonical: persist the removal before any (optional) git tracking runs.
|
|
154
|
-
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
155
|
-
|
|
156
|
-
if (associatedPaths.length === 0)
|
|
157
|
-
logger.warn('No associated tracked storage paths found', { path: hasPathFilter ? basePath : '*' });
|
|
158
|
-
else
|
|
159
|
-
logger.info('Removed associated tracked storage paths', {
|
|
160
|
-
path: hasPathFilter ? basePath : '*',
|
|
161
|
-
removed: associatedPaths.length,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (options.git === true) {
|
|
165
|
-
const gitPath = !hasPathFilter ? '.' : isSingleFile ? parentDir : basePath;
|
|
166
|
-
Underpost.fs.gitTrack(gitPath);
|
|
167
|
-
}
|
|
168
80
|
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// Detecting locally-deleted files is a best-effort enhancement backed by git; if the path is
|
|
176
|
-
// not a repo (or git is unavailable) it must not block the canonical storage workflow.
|
|
177
|
-
let deleteFiles = [];
|
|
178
|
-
if (options.pull !== true) {
|
|
179
|
-
try {
|
|
180
|
-
deleteFiles = Underpost.repo.getDeleteFiles(gitContextPath);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
logger.warn('delete detection skipped (git unavailable)', { path: gitContextPath, error: error?.message });
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// When processing a single file, only consider it for deletion
|
|
187
|
-
for (const relativePath of deleteFiles) {
|
|
188
|
-
const _path = isSingleFile ? (relativePath === singleFileName ? path : null) : path + '/' + relativePath;
|
|
189
|
-
if (_path && _path in storage) {
|
|
190
|
-
await Underpost.fs.delete(_path);
|
|
191
|
-
delete storage[_path];
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (options.pull === true) {
|
|
195
|
-
let pullSkipCount = 0;
|
|
196
|
-
for (const _path of Object.keys(storage)) {
|
|
197
|
-
if (!fs.existsSync(_path) || options.force === true) {
|
|
198
|
-
if (options.force === true && fs.existsSync(_path)) fs.removeSync(_path);
|
|
199
|
-
await Underpost.fs.pull(_path, options);
|
|
200
|
-
} else pullSkipCount++;
|
|
201
|
-
}
|
|
202
|
-
if (pullSkipCount > 0) logger.warn(`Pull skipped ${pullSkipCount} files that already exist`);
|
|
203
|
-
// Only run git init/commit when the caller explicitly requests git tracking (--git flag).
|
|
204
|
-
// For bundle pulls into ./build the git step is unwanted and would error on a non-repo path.
|
|
205
|
-
if (options.git === true) Underpost.fs.gitTrack(gitContextPath, { init: true, message: 'Base pull state' });
|
|
206
|
-
} else {
|
|
207
|
-
let files;
|
|
208
|
-
if (isSingleFile) {
|
|
209
|
-
// Single file: treat the file itself as the sole item to process
|
|
210
|
-
files = [singleFileName];
|
|
211
|
-
} else {
|
|
212
|
-
files =
|
|
213
|
-
options.git === true
|
|
214
|
-
? Underpost.repo.getChangedFiles(gitContextPath)
|
|
215
|
-
: await fs.readdir(path, { recursive: true });
|
|
216
|
-
}
|
|
217
|
-
for (const relativePath of files) {
|
|
218
|
-
const _path = isSingleFile ? path : path + '/' + relativePath;
|
|
219
|
-
if (fs.existsSync(_path) && fs.statSync(_path).isDirectory()) {
|
|
220
|
-
if (options.pull === true && !fs.existsSync(_path)) fs.mkdirSync(_path, { recursive: true });
|
|
221
|
-
continue;
|
|
222
|
-
} else if (!(_path in storage) || options.force === true) {
|
|
223
|
-
await Underpost.fs.upload(_path, options);
|
|
224
|
-
if (storage) storage[_path] = {};
|
|
225
|
-
} else logger.warn('File already exists', _path);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// Storage is canonical and always persisted; git is an optional layer on top.
|
|
229
|
-
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
230
|
-
if (options.git === true) Underpost.fs.gitTrack(gitContextPath);
|
|
81
|
+
updateManifest(manifest, path, entry) {
|
|
82
|
+
const next = { ...manifest.storage };
|
|
83
|
+
if (entry === undefined) delete next[path];
|
|
84
|
+
else Object.defineProperty(next, path, { value: entry, enumerable: true, configurable: true, writable: true });
|
|
85
|
+
UnderpostFileStorage.API.writeManifest(next, manifest.storageConf);
|
|
86
|
+
manifest.storage = next;
|
|
231
87
|
},
|
|
232
|
-
/**
|
|
233
|
-
* @method callback
|
|
234
|
-
* @description Orchestrates file storage operations based on the provided options.
|
|
235
|
-
* This method handles file uploads, deletions, and recursive processing of directories.
|
|
236
|
-
* @param {string} path - The path to the file or directory to process.
|
|
237
|
-
* @param {object} [options] - An object containing options for the callback.
|
|
238
|
-
* @param {boolean} [options.rm=false] - Flag to remove files and directories.
|
|
239
|
-
* @param {boolean} [options.recursive=false] - Flag to process directories recursively.
|
|
240
|
-
* @param {string} [options.deployId=''] - The identifier for the deployment.
|
|
241
|
-
* @param {boolean} [options.force=false] - Flag to force file operations.
|
|
242
|
-
* @param {boolean} [options.pull=false] - Flag to pull files from storage.
|
|
243
|
-
* @param {boolean} [options.git=false] - Flag to use Git for file operations.
|
|
244
|
-
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
245
|
-
* @returns {Promise<void>} A promise that resolves when the callback is complete.
|
|
246
|
-
* @memberof UnderpostFileStorage
|
|
247
|
-
*/
|
|
248
|
-
async callback(
|
|
249
|
-
path,
|
|
250
|
-
options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false, omitUnzip: false },
|
|
251
|
-
) {
|
|
252
|
-
// rm always routes through recursiveCallback so storage.*.json is updated regardless of
|
|
253
|
-
// --recursive/--git. The bare `delete` primitive only removes the remote asset and would
|
|
254
|
-
// otherwise leave the tracked storage key orphaned.
|
|
255
|
-
if (options.recursive === true || options.git === true || options.rm === true)
|
|
256
|
-
return await Underpost.fs.recursiveCallback(path, options);
|
|
257
|
-
if (options.pull === true) return await Underpost.fs.pull(path, options);
|
|
258
|
-
return await Underpost.fs.upload(path, options);
|
|
259
|
-
},
|
|
260
|
-
/**
|
|
261
|
-
* @method upload
|
|
262
|
-
* @description Uploads a file to Cloudinary.
|
|
263
|
-
* @param {string} path - The path to the file to upload.
|
|
264
|
-
* @param {object} [options] - An object containing options for the upload.
|
|
265
|
-
* @param {string} [options.deployId=''] - The identifier for the deployment (used to locate the storage config file).
|
|
266
|
-
* @param {boolean} [options.force=false] - Flag to force file operations (overwrites existing remote asset).
|
|
267
|
-
* @param {string} [options.storageFilePath=''] - The path to the storage configuration file.
|
|
268
|
-
* @returns {Promise<object>} A promise that resolves to the upload result.
|
|
269
|
-
* @memberof UnderpostFileStorage
|
|
270
|
-
*/
|
|
271
88
|
|
|
272
|
-
|
|
273
|
-
path
|
|
274
|
-
|
|
275
|
-
) {
|
|
276
|
-
Underpost.fs.cloudinaryConfig();
|
|
277
|
-
const { storage, storageConf } = Underpost.fs.getStorageConf(options);
|
|
278
|
-
// path = Underpost.fs.file2Zip(path);
|
|
279
|
-
const uploadResult = await cloudinary.uploader
|
|
280
|
-
.upload(path, {
|
|
281
|
-
public_id: path,
|
|
282
|
-
resource_type: 'raw',
|
|
283
|
-
overwrite: options.force === true ? true : false,
|
|
284
|
-
})
|
|
285
|
-
.catch((error) => {
|
|
286
|
-
logger.error(error, { path, stack: error.stack });
|
|
287
|
-
});
|
|
288
|
-
logger.info('upload result', uploadResult);
|
|
289
|
-
if (storage) storage[path] = {};
|
|
290
|
-
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
291
|
-
return uploadResult;
|
|
89
|
+
deliveryTypeCandidates(storage, path) {
|
|
90
|
+
const recorded = storage[path]?.type ?? 'upload';
|
|
91
|
+
return [...new Set([recorded, CLOUDINARY_DELIVERY_TYPE, 'upload'])];
|
|
292
92
|
},
|
|
293
|
-
/**
|
|
294
|
-
* @method pull
|
|
295
|
-
* @description Pulls a file from Cloudinary.
|
|
296
|
-
* @param {string} path - The path to the file to pull.
|
|
297
|
-
* @param {object} [options] - Pull options.
|
|
298
|
-
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
299
|
-
* @param {boolean} [options.force=false] - If true, re-download even if the local zip already exists.
|
|
300
|
-
* @returns {Promise<void>} A promise that resolves when the file is pulled.
|
|
301
|
-
* @memberof UnderpostFileStorage
|
|
302
|
-
*/
|
|
303
|
-
async pull(path, options = { omitUnzip: false, force: false }) {
|
|
304
|
-
Underpost.fs.cloudinaryConfig();
|
|
305
|
-
const folder = dir.dirname(path);
|
|
306
|
-
if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
|
|
307
|
-
const zipPath = `${path}.zip`;
|
|
308
93
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
94
|
+
async callback(path, options = {}) {
|
|
95
|
+
const api = UnderpostFileStorage.API;
|
|
96
|
+
const manifest = api.readManifest(options);
|
|
97
|
+
const paths = api.resolveSelection(path, manifest.storage, options);
|
|
98
|
+
const operation = options.rm ? 'delete' : options.pull ? 'pull' : 'upload';
|
|
99
|
+
if (paths.length === 0) logger.warn('No storage paths selected.');
|
|
100
|
+
// A skip is the absence of work, and one line per file buries the transfers that did happen
|
|
101
|
+
// under hundreds that did not. The count carries the same information: which files were
|
|
102
|
+
// skipped is exactly which files were already there.
|
|
103
|
+
let skipped = 0;
|
|
104
|
+
for (const selected of paths) {
|
|
105
|
+
if ((await api[operation](selected, options, manifest)) === PULL_SKIPPED) skipped++;
|
|
312
106
|
}
|
|
107
|
+
if (skipped > 0) logger.info('Pull skipped files that already exist', { skipped, selected: paths.length });
|
|
108
|
+
},
|
|
313
109
|
|
|
314
|
-
|
|
315
|
-
|
|
110
|
+
async upload(path, options = {}, manifest = UnderpostFileStorage.API.readManifest(options)) {
|
|
111
|
+
const api = UnderpostFileStorage.API;
|
|
112
|
+
if (!fs.statSync(path).isFile()) throw new Error(`Upload requires a regular file: ${path}`);
|
|
113
|
+
api.cloudinaryConfig();
|
|
114
|
+
const result = await cloudinary.uploader.upload(path, {
|
|
115
|
+
public_id: path,
|
|
316
116
|
resource_type: 'raw',
|
|
117
|
+
type: CLOUDINARY_DELIVERY_TYPE,
|
|
118
|
+
access_control: CLOUDINARY_ACCESS_CONTROL,
|
|
119
|
+
overwrite: options.force === true,
|
|
317
120
|
});
|
|
318
|
-
|
|
121
|
+
const remote = result?.existing
|
|
122
|
+
? await cloudinary.api.resource(path, { resource_type: 'raw', type: CLOUDINARY_DELIVERY_TYPE })
|
|
123
|
+
: result;
|
|
124
|
+
if (
|
|
125
|
+
remote?.error ||
|
|
126
|
+
remote?.public_id !== path ||
|
|
127
|
+
remote?.type !== CLOUDINARY_DELIVERY_TYPE ||
|
|
128
|
+
!Number.isSafeInteger(remote?.bytes) ||
|
|
129
|
+
remote.bytes < 0
|
|
130
|
+
)
|
|
131
|
+
throw new Error(`Invalid Cloudinary upload response: ${path}`);
|
|
132
|
+
api.updateManifest(manifest, path, { ...manifest.storage[path], type: remote.type, bytes: remote.bytes });
|
|
133
|
+
logger.info(result.existing ? 'Remote asset already exists' : 'Uploaded asset', { path });
|
|
134
|
+
return result;
|
|
135
|
+
},
|
|
319
136
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
137
|
+
async pull(path, options = {}, manifest = UnderpostFileStorage.API.readManifest(options)) {
|
|
138
|
+
const api = UnderpostFileStorage.API;
|
|
139
|
+
if (!Object.hasOwn(manifest.storage, path)) throw new Error(`Asset is not in the selected manifest: ${path}`);
|
|
140
|
+
const target = options.omitUnzip ? `${path}.zip` : path;
|
|
141
|
+
if (fs.existsSync(target) && !fs.statSync(target).isFile())
|
|
142
|
+
throw new Error(`Pull target must be a regular file: ${target}`);
|
|
143
|
+
if (fs.existsSync(target) && !options.force) return PULL_SKIPPED;
|
|
144
|
+
api.cloudinaryConfig();
|
|
145
|
+
fs.ensureDirSync(dir.dirname(target));
|
|
146
|
+
const temporary = fs.mkdtempSync(dir.join(dir.dirname(target), '.underpost-fs-'));
|
|
147
|
+
const archive = dir.join(temporary, 'download.zip');
|
|
148
|
+
try {
|
|
149
|
+
let resolvedType;
|
|
150
|
+
let downloadError;
|
|
151
|
+
for (const type of api.deliveryTypeCandidates(manifest.storage, path)) {
|
|
152
|
+
try {
|
|
153
|
+
const url = cloudinary.utils.download_archive_url({ public_ids: [path], resource_type: 'raw', type });
|
|
154
|
+
await Downloader.downloadFile(url, archive);
|
|
155
|
+
resolvedType = type;
|
|
156
|
+
break;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
downloadError = error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (resolvedType === undefined) throw downloadError;
|
|
162
|
+
const content = await readZipEntry(archive, dir.basename(path));
|
|
163
|
+
if (!content) throw new Error(`Downloaded archive has no asset: ${path}`);
|
|
164
|
+
let downloaded = archive;
|
|
165
|
+
if (!options.omitUnzip) {
|
|
166
|
+
downloaded = dir.join(temporary, 'asset');
|
|
167
|
+
fs.writeFileSync(downloaded, content);
|
|
168
|
+
}
|
|
169
|
+
fs.renameSync(downloaded, target);
|
|
170
|
+
api.updateManifest(manifest, path, { ...manifest.storage[path], type: resolvedType, bytes: content.length });
|
|
171
|
+
logger.info('Pulled asset', { path: target });
|
|
172
|
+
return resolvedType;
|
|
173
|
+
} finally {
|
|
174
|
+
fs.removeSync(temporary);
|
|
323
175
|
}
|
|
324
|
-
|
|
325
|
-
path = Underpost.fs.zip2File(zipPath);
|
|
326
|
-
fs.removeSync(`${path}.zip`);
|
|
327
|
-
},
|
|
328
|
-
/**
|
|
329
|
-
* @method delete
|
|
330
|
-
* @description Deletes a file from Cloudinary by its public ID.
|
|
331
|
-
* @param {string} path - The path (public ID) of the file to delete.
|
|
332
|
-
* @returns {Promise<object>} A promise that resolves to the Cloudinary delete result.
|
|
333
|
-
* @memberof UnderpostFileStorage
|
|
334
|
-
*/
|
|
335
|
-
async delete(path) {
|
|
336
|
-
Underpost.fs.cloudinaryConfig();
|
|
337
|
-
const deleteResult = await cloudinary.api
|
|
338
|
-
.delete_resources([path], { type: 'upload', resource_type: 'raw' })
|
|
339
|
-
.catch((error) => {
|
|
340
|
-
logger.error(error, { path, stack: error.stack });
|
|
341
|
-
});
|
|
342
|
-
logger.info('delete result', deleteResult);
|
|
343
|
-
return deleteResult;
|
|
344
|
-
},
|
|
345
|
-
/**
|
|
346
|
-
* @method file2Zip
|
|
347
|
-
* @description Converts a file to a zip file.
|
|
348
|
-
* @param {string} path - The path to the file to convert.
|
|
349
|
-
* @returns {string} The path to the zip file.
|
|
350
|
-
* @memberof UnderpostFileStorage
|
|
351
|
-
*/
|
|
352
|
-
file2Zip(path) {
|
|
353
|
-
const zip = new AdmZip();
|
|
354
|
-
zip.addLocalFile(path, '/');
|
|
355
|
-
path = path + '.zip';
|
|
356
|
-
zip.writeZip(path);
|
|
357
|
-
return path;
|
|
358
176
|
},
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
);
|
|
375
|
-
return path;
|
|
177
|
+
|
|
178
|
+
async delete(path, options = {}, manifest = UnderpostFileStorage.API.readManifest(options)) {
|
|
179
|
+
const api = UnderpostFileStorage.API;
|
|
180
|
+
api.cloudinaryConfig();
|
|
181
|
+
const results = {};
|
|
182
|
+
for (const type of api.deliveryTypeCandidates(manifest.storage, path)) {
|
|
183
|
+
const result = await cloudinary.api.delete_resources([path], { type, resource_type: 'raw' });
|
|
184
|
+
const status = result?.deleted?.[path];
|
|
185
|
+
if (result?.error || !['deleted', 'not_found'].includes(status))
|
|
186
|
+
throw new Error(`Cloudinary delete failed for ${path} (${type}): ${status ?? 'missing status'}`);
|
|
187
|
+
results[type] = result;
|
|
188
|
+
}
|
|
189
|
+
api.updateManifest(manifest, path, undefined);
|
|
190
|
+
logger.info('Deleted remote asset', { path });
|
|
191
|
+
return results;
|
|
376
192
|
},
|
|
377
193
|
};
|
|
378
194
|
}
|