@underpostnet/cyberia 3.2.80
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 +14 -0
- package/.env.example +144 -0
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
- package/.github/workflows/engine-cyberia.cd.yml +157 -0
- package/.github/workflows/engine-cyberia.ci.yml +125 -0
- package/.github/workflows/ghpkg.ci.yml +160 -0
- package/.github/workflows/gitlab.ci.yml +20 -0
- package/.github/workflows/hardhat.ci.yml +82 -0
- package/.github/workflows/npmpkg.ci.yml +126 -0
- package/.github/workflows/publish.ci.yml +87 -0
- package/.github/workflows/publish.cyberia.ci.yml +79 -0
- package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
- package/.github/workflows/release.cd.yml +46 -0
- package/.nycrc +9 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +26 -0
- package/CHANGELOG.md +2516 -0
- package/CLI-HELP.md +1099 -0
- package/Dockerfile +161 -0
- package/Dockerfile.dev +161 -0
- package/Dockerfile.test +165 -0
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/baremetal/commission-workflows.json +211 -0
- package/baremetal/packer-workflows.json +24 -0
- package/bin/build.js +261 -0
- package/bin/build.template.js +33 -0
- package/bin/cyberia.js +5627 -0
- package/bin/deploy.js +1547 -0
- package/bin/index.js +5627 -0
- package/bump.config.js +27 -0
- package/compose.env +144 -0
- package/conf.js +952 -0
- package/deployment.yaml +418 -0
- package/docker-compose.yml +434 -0
- package/examples/static-page/README.md +150 -0
- package/examples/static-page/ssr-components/CustomPage.js +554 -0
- package/examples/static-page/static-config-example.json +57 -0
- package/hardhat/.env.example +31 -0
- package/hardhat/contracts/ObjectLayerToken.sol +391 -0
- package/hardhat/deployments/.gitkeep +0 -0
- package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
- package/hardhat/hardhat.config.js +136 -0
- package/hardhat/ignition/modules/.gitkeep +0 -0
- package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
- package/hardhat/networks/besu-object-layer.network.json +138 -0
- package/hardhat/package-lock.json +2871 -0
- package/hardhat/package.json +43 -0
- package/hardhat/scripts/deployObjectLayerToken.js +98 -0
- package/hardhat/test/ObjectLayerToken.js +696 -0
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/ipfs/configure-ipfs.sh +13 -0
- package/jsconfig.json +9 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
- package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/fastapi/initial_data.sh +8 -0
- package/manifests/deployment/kafka/deployment.yaml +67 -0
- package/manifests/deployment/mongo-express/deployment.yaml +62 -0
- package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
- package/manifests/deployment/playwright/deployment.yaml +52 -0
- package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
- package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
- package/manifests/envoy-service-nodeport.yaml +22 -0
- package/manifests/grafana/deployment.yaml +64 -0
- package/manifests/grafana/kustomization.yaml +7 -0
- package/manifests/grafana/pvc.yaml +12 -0
- package/manifests/grafana/service.yaml +14 -0
- package/manifests/grafana/storage-class.yaml +9 -0
- package/manifests/ipfs/configmap.yaml +64 -0
- package/manifests/ipfs/headless-service.yaml +35 -0
- package/manifests/ipfs/kustomization.yaml +8 -0
- package/manifests/ipfs/statefulset.yaml +149 -0
- package/manifests/ipfs/storage-class.yaml +9 -0
- package/manifests/kind-config-dev.yaml +20 -0
- package/manifests/kind-config.yaml +12 -0
- package/manifests/kubeadm-calico-config.yaml +14 -0
- package/manifests/letsencrypt-prod.yaml +14 -0
- package/manifests/lxd/lxd-admin-profile.yaml +26 -0
- package/manifests/lxd/lxd-preseed.yaml +30 -0
- package/manifests/mariadb/kustomization.yaml +9 -0
- package/manifests/mariadb/pv.yaml +12 -0
- package/manifests/mariadb/pvc.yaml +10 -0
- package/manifests/mariadb/service.yaml +10 -0
- package/manifests/mariadb/statefulset.yaml +56 -0
- package/manifests/mariadb/storage-class.yaml +10 -0
- package/manifests/mongodb/headless-service.yaml +10 -0
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +59 -0
- package/manifests/mongodb/statefulset.yaml +113 -0
- package/manifests/mongodb/storage-class.yaml +9 -0
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +9 -0
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/mysql/kustomization.yaml +7 -0
- package/manifests/mysql/pv-pvc.yaml +27 -0
- package/manifests/mysql/statefulset.yaml +55 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/manifests/pv-pvc-dd.yaml +34 -0
- package/manifests/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +10 -0
- package/manifests/valkey/statefulset.yaml +37 -0
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +128 -0
- package/nodemon.json +6 -0
- package/package.json +101 -0
- package/packer/images/Rocky9Amd64/Makefile +62 -0
- package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
- package/packer/images/Rocky9Amd64/README.md +122 -0
- package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
- package/packer/images/Rocky9Arm64/Makefile +69 -0
- package/packer/images/Rocky9Arm64/README.md +122 -0
- package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
- package/packer/scripts/fuse-nbd +64 -0
- package/packer/scripts/fuse-tar-root +63 -0
- package/proxy.yaml +189 -0
- package/pv-pvc.yaml +164 -0
- package/scripts/device-scan.sh +65 -0
- package/scripts/disk-clean.sh +182 -0
- package/scripts/disk-devices.sh +13 -0
- package/scripts/gen-fqdns.sh +14 -0
- package/scripts/gpu-diag.sh +19 -0
- package/scripts/ip-info.sh +118 -0
- package/scripts/ipxe-setup.sh +200 -0
- package/scripts/k3s-node-setup.sh +124 -0
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/lxd-vm-setup.sh +208 -0
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/scripts/maas-setup.sh +135 -0
- package/scripts/maas-upload-boot-resource.sh +183 -0
- package/scripts/nat-iptables.sh +111 -0
- package/scripts/nvim.sh +91 -0
- package/scripts/packer-init-vars-file.sh +40 -0
- package/scripts/packer-setup.sh +289 -0
- package/scripts/ports-ls.sh +33 -0
- package/scripts/quick-tftp.sh +19 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/scripts/rocky-kickstart.sh +986 -0
- package/scripts/rocky-pwa.sh +200 -0
- package/scripts/rocky-setup.sh +106 -0
- package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
- package/scripts/ssl.sh +170 -0
- package/scripts/test-monitor.sh +248 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
- package/src/api/core/core.controller.js +8 -0
- package/src/api/core/core.model.js +11 -0
- package/src/api/core/core.router.js +23 -0
- package/src/api/core/core.service.js +30 -0
- package/src/api/crypto/crypto.controller.js +10 -0
- package/src/api/crypto/crypto.model.js +23 -0
- package/src/api/crypto/crypto.router.js +23 -0
- package/src/api/crypto/crypto.service.js +64 -0
- package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
- package/src/api/cyberia-action/cyberia-action.model.js +79 -0
- package/src/api/cyberia-action/cyberia-action.router.js +21 -0
- package/src/api/cyberia-action/cyberia-action.service.js +58 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
- package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
- package/src/api/cyberia-map/cyberia-map.model.js +33 -0
- package/src/api/cyberia-map/cyberia-map.router.js +19 -0
- package/src/api/cyberia-map/cyberia-map.service.js +76 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
- package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
- package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +21 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
- package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
- package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/default/default.controller.js +6 -0
- package/src/api/default/default.model.js +20 -0
- package/src/api/default/default.router.js +21 -0
- package/src/api/default/default.service.js +42 -0
- package/src/api/document/document.controller.js +9 -0
- package/src/api/document/document.model.js +95 -0
- package/src/api/document/document.router.js +32 -0
- package/src/api/document/document.service.js +633 -0
- package/src/api/file/file.controller.js +15 -0
- package/src/api/file/file.model.js +127 -0
- package/src/api/file/file.ref.json +39 -0
- package/src/api/file/file.router.js +36 -0
- package/src/api/file/file.service.js +499 -0
- package/src/api/instance/instance.controller.js +8 -0
- package/src/api/instance/instance.model.js +34 -0
- package/src/api/instance/instance.router.js +27 -0
- package/src/api/instance/instance.service.js +75 -0
- package/src/api/ipfs/ipfs.controller.js +8 -0
- package/src/api/ipfs/ipfs.model.js +77 -0
- package/src/api/ipfs/ipfs.router.js +26 -0
- package/src/api/ipfs/ipfs.service.js +145 -0
- package/src/api/object-layer/object-layer.controller.js +18 -0
- package/src/api/object-layer/object-layer.model.js +200 -0
- package/src/api/object-layer/object-layer.router.js +729 -0
- package/src/api/object-layer/object-layer.service.js +894 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
- package/src/api/test/test.controller.js +14 -0
- package/src/api/test/test.model.js +14 -0
- package/src/api/test/test.router.js +23 -0
- package/src/api/test/test.service.js +35 -0
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +100 -0
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.build.js +16 -0
- package/src/api/user/user.controller.js +35 -0
- package/src/api/user/user.model.js +142 -0
- package/src/api/user/user.router.js +367 -0
- package/src/api/user/user.service.js +582 -0
- package/src/api.js +23 -0
- package/src/cli/baremetal.js +3947 -0
- package/src/cli/cloud-init.js +673 -0
- package/src/cli/cluster.js +1395 -0
- package/src/cli/db.js +1533 -0
- package/src/cli/deploy.js +1542 -0
- package/src/cli/docker-compose.js +802 -0
- package/src/cli/env.js +177 -0
- package/src/cli/fs.js +380 -0
- package/src/cli/image.js +389 -0
- package/src/cli/index.js +1059 -0
- package/src/cli/ipfs.js +182 -0
- package/src/cli/kickstart.js +271 -0
- package/src/cli/kubectl.js +214 -0
- package/src/cli/lxd.js +1232 -0
- package/src/cli/monitor.js +659 -0
- package/src/cli/release.js +595 -0
- package/src/cli/repository.js +1969 -0
- package/src/cli/run.js +3326 -0
- package/src/cli/secrets.js +151 -0
- package/src/cli/ssh.js +975 -0
- package/src/cli/static.js +653 -0
- package/src/cli/system.js +332 -0
- package/src/cli/test.js +176 -0
- package/src/client/Cryptokoyn.index.js +30 -0
- package/src/client/CyberiaPortal.index.js +31 -0
- package/src/client/Default.index.js +77 -0
- package/src/client/Itemledger.index.js +35 -0
- package/src/client/Underpost.index.js +31 -0
- package/src/client/components/core/404.js +20 -0
- package/src/client/components/core/500.js +20 -0
- package/src/client/components/core/Account.js +630 -0
- package/src/client/components/core/AgGrid.js +238 -0
- package/src/client/components/core/Alert.js +76 -0
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/Auth.js +353 -0
- package/src/client/components/core/Badge.js +26 -0
- package/src/client/components/core/Blockchain.js +41 -0
- package/src/client/components/core/Blog.js +9 -0
- package/src/client/components/core/BtnIcon.js +109 -0
- package/src/client/components/core/CalendarCore.js +437 -0
- package/src/client/components/core/Chat.js +62 -0
- package/src/client/components/core/ClientEvents.js +163 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/CommonJs.js +1014 -0
- package/src/client/components/core/Content.js +349 -0
- package/src/client/components/core/Css.js +1134 -0
- package/src/client/components/core/CssCore.js +881 -0
- package/src/client/components/core/D3Chart.js +44 -0
- package/src/client/components/core/Docs.js +478 -0
- package/src/client/components/core/DropDown.js +321 -0
- package/src/client/components/core/EventBus.js +96 -0
- package/src/client/components/core/EventsUI.js +130 -0
- package/src/client/components/core/FileExplorer.js +1406 -0
- package/src/client/components/core/FullScreen.js +201 -0
- package/src/client/components/core/Input.js +537 -0
- package/src/client/components/core/Keyboard.js +80 -0
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +152 -0
- package/src/client/components/core/LogIn.js +212 -0
- package/src/client/components/core/LogOut.js +72 -0
- package/src/client/components/core/Logger.js +22 -0
- package/src/client/components/core/Modal.js +2956 -0
- package/src/client/components/core/NotificationManager.js +80 -0
- package/src/client/components/core/Pagination.js +252 -0
- package/src/client/components/core/Panel.js +1308 -0
- package/src/client/components/core/PanelForm.js +915 -0
- package/src/client/components/core/Polyhedron.js +947 -0
- package/src/client/components/core/PublicProfile.js +895 -0
- package/src/client/components/core/Recover.js +211 -0
- package/src/client/components/core/Responsive.js +146 -0
- package/src/client/components/core/RichText.js +33 -0
- package/src/client/components/core/Router.js +523 -0
- package/src/client/components/core/Scroll.js +76 -0
- package/src/client/components/core/SearchBox.js +1007 -0
- package/src/client/components/core/SignUp.js +153 -0
- package/src/client/components/core/SocketIo.js +163 -0
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +161 -0
- package/src/client/components/core/ToggleSwitch.js +89 -0
- package/src/client/components/core/ToolTip.js +78 -0
- package/src/client/components/core/Translate.js +694 -0
- package/src/client/components/core/Validator.js +140 -0
- package/src/client/components/core/VanillaJs.js +485 -0
- package/src/client/components/core/Wallet.js +95 -0
- package/src/client/components/core/WebComponent.js +44 -0
- package/src/client/components/core/Webhook.js +58 -0
- package/src/client/components/core/Worker.js +313 -0
- package/src/client/components/core/windowGetDimensions.js +269 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
- package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
- package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
- package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
- package/src/client/components/default/AppShellDefault.js +765 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/CommonDefault.js +29 -0
- package/src/client/components/default/CssDefault.js +27 -0
- package/src/client/components/default/LogInDefault.js +16 -0
- package/src/client/components/default/LogOutDefault.js +12 -0
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +16 -0
- package/src/client/components/default/SignUpDefault.js +11 -0
- package/src/client/components/default/SocketIoDefault.js +6 -0
- package/src/client/components/default/TranslateDefault.js +7 -0
- package/src/client/components/itemledger/AppShellItemledger.js +353 -0
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/CssItemledger.js +212 -0
- package/src/client/components/itemledger/LogInItemledger.js +17 -0
- package/src/client/components/itemledger/LogOutItemledger.js +12 -0
- package/src/client/components/itemledger/RouterItemledger.js +38 -0
- package/src/client/components/itemledger/SettingsItemledger.js +16 -0
- package/src/client/components/itemledger/SignUpItemledger.js +11 -0
- package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
- package/src/client/components/itemledger/TranslateItemledger.js +7 -0
- package/src/client/components/underpost/AppShellUnderpost.js +691 -0
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/CssUnderpost.js +281 -0
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
- package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
- package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
- package/src/client/components/underpost/LogInUnderpost.js +25 -0
- package/src/client/components/underpost/LogOutUnderpost.js +17 -0
- package/src/client/components/underpost/RouterUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +16 -0
- package/src/client/components/underpost/SignUpUnderpost.js +11 -0
- package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
- package/src/client/components/underpost/TranslateUnderpost.js +10 -0
- package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
- package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
- package/src/client/public/cryptokoyn/favicon.ico +0 -0
- package/src/client/public/cryptokoyn/microdata.json +85 -0
- package/src/client/public/cryptokoyn/site.webmanifest +57 -0
- package/src/client/public/cryptokoyn/sitemap +148 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
- package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
- 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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152-precomposed.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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76-precomposed.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/dark.jpg +0 -0
- package/src/client/public/default/assets/background/dark.svg +557 -0
- package/src/client/public/default/assets/background/white.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/default/assets/logo/base-icon.png +0 -0
- package/src/client/public/default/assets/logo/underpost.gif +0 -0
- package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
- package/src/client/public/default/browserconfig.xml +12 -0
- 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 +69 -0
- 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/safari-pinned-tab.svg +24 -0
- package/src/client/public/default/site.webmanifest +69 -0
- package/src/client/public/default/sitemap +148 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +9 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +148 -0
- package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
- package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
- package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
- package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
- package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
- package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
- package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
- package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
- package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
- package/src/client/public/itemledger/browserconfig.xml +12 -0
- package/src/client/public/itemledger/favicon-16x16.png +0 -0
- package/src/client/public/itemledger/favicon-32x32.png +0 -0
- package/src/client/public/itemledger/favicon-48x48.png +0 -0
- package/src/client/public/itemledger/favicon.ico +0 -0
- package/src/client/public/itemledger/manifest.webmanifest +69 -0
- package/src/client/public/itemledger/microdata.json +71 -0
- package/src/client/public/itemledger/mstile-144x144.png +0 -0
- package/src/client/public/itemledger/mstile-150x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x310.png +0 -0
- package/src/client/public/itemledger/mstile-70x70.png +0 -0
- package/src/client/public/itemledger/sitemap +148 -0
- package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
- package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
- package/src/client/public/test/favicon.ico +0 -0
- package/src/client/public/test/sitemap +148 -0
- package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
- package/src/client/services/core/core.service.js +309 -0
- package/src/client/services/crypto/crypto.service.js +68 -0
- package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
- package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +137 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
- package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
- package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/default/default.management.js +1015 -0
- package/src/client/services/default/default.service.js +99 -0
- package/src/client/services/document/document.service.js +134 -0
- package/src/client/services/file/file.service.js +94 -0
- package/src/client/services/instance/instance.management.js +78 -0
- package/src/client/services/instance/instance.service.js +107 -0
- package/src/client/services/ipfs/ipfs.service.js +118 -0
- package/src/client/services/object-layer/object-layer.management.js +414 -0
- package/src/client/services/object-layer/object-layer.service.js +208 -0
- package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
- package/src/client/services/test/test.service.js +68 -0
- package/src/client/services/user/guest.service.js +86 -0
- package/src/client/services/user/user.management.js +51 -0
- package/src/client/services/user/user.service.js +106 -0
- package/src/client/ssr/RootDocument.js +237 -0
- package/src/client/ssr/body/404.js +77 -0
- package/src/client/ssr/body/500.js +76 -0
- package/src/client/ssr/body/CacheControl.js +117 -0
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
- package/src/client/ssr/head/CryptokoynScripts.js +4 -0
- package/src/client/ssr/head/Css.js +241 -0
- package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
- package/src/client/ssr/head/DefaultScripts.js +6 -0
- package/src/client/ssr/head/ItemledgerScripts.js +4 -0
- package/src/client/ssr/head/Microdata.js +11 -0
- package/src/client/ssr/head/Production.js +1 -0
- package/src/client/ssr/head/Pwa.js +146 -0
- package/src/client/ssr/head/PwaItemledger.js +197 -0
- package/src/client/ssr/head/Seo.js +15 -0
- package/src/client/ssr/head/UnderpostScripts.js +6 -0
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
- package/src/client/ssr/views/Cyberia404.js +328 -0
- package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
- package/src/client/ssr/views/Maintenance.js +64 -0
- package/src/client/ssr/views/NoNetworkConnection.js +68 -0
- package/src/client/ssr/views/Test.js +199 -0
- package/src/client/sw/core.sw.js +274 -0
- package/src/client-builder/client-build-docs.js +493 -0
- package/src/client-builder/client-build-live.js +100 -0
- package/src/client-builder/client-build.js +1092 -0
- package/src/client-builder/client-dev-server.js +88 -0
- package/src/client-builder/client-formatted.js +170 -0
- package/src/client-builder/client-icons.js +108 -0
- package/src/client-builder/ssr.js +124 -0
- package/src/client.build.js +17 -0
- package/src/client.dev.js +21 -0
- package/src/db/DataBaseProvider.js +213 -0
- package/src/db/mariadb/MariaDB.js +72 -0
- package/src/db/mongo/MongoBootstrap.js +657 -0
- package/src/db/mongo/MongooseDB.js +199 -0
- package/src/grpc/cyberia/grpc-server.js +162 -0
- package/src/index.js +357 -0
- package/src/mailer/EmailRender.js +116 -0
- package/src/mailer/MailerProvider.js +213 -0
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
- package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
- package/src/projects/cyberia/catalog-cyberia.js +90 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2137 -0
- package/src/projects/cyberia/hot-reload-trigger.js +163 -0
- package/src/projects/cyberia/instance-data.js +719 -0
- package/src/projects/cyberia/ipfs-client.js +599 -0
- package/src/projects/cyberia/map-preview-generator.js +304 -0
- package/src/projects/cyberia/object-layer.js +978 -0
- package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
- package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
- package/src/projects/cyberia/semantic-layer-generator.js +753 -0
- package/src/projects/cyberia/shape-generator.js +1060 -0
- package/src/projects/underpost/catalog-underpost.js +60 -0
- package/src/proxy.js +19 -0
- package/src/runtime/cyberia-client/Dockerfile +49 -0
- package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
- package/src/runtime/cyberia-server/Dockerfile +43 -0
- package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
- package/src/runtime/engine-cyberia/Dockerfile +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +144 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +128 -0
- package/src/runtime/express/Dockerfile +45 -0
- package/src/runtime/express/Express.js +285 -0
- package/src/runtime/lampp/Dockerfile +62 -0
- package/src/runtime/lampp/Lampp.js +359 -0
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/runtime/wp/Dockerfile +62 -0
- package/src/runtime/wp/Wp.js +642 -0
- package/src/server/auth.js +713 -0
- package/src/server/backup.js +102 -0
- package/src/server/catalog.js +72 -0
- package/src/server/conf.js +2316 -0
- package/src/server/cron.js +476 -0
- package/src/server/crypto.js +210 -0
- package/src/server/data-query.js +461 -0
- package/src/server/dns.js +553 -0
- package/src/server/downloader.js +73 -0
- package/src/server/logger.js +229 -0
- package/src/server/middlewares.js +152 -0
- package/src/server/peer.js +75 -0
- package/src/server/process.js +299 -0
- package/src/server/proxy.js +139 -0
- package/src/server/runtime-status.js +252 -0
- package/src/server/runtime.js +183 -0
- package/src/server/start.js +272 -0
- package/src/server/tls.js +254 -0
- package/src/server/valkey.js +201 -0
- package/src/server.js +25 -0
- package/src/ws/IoInterface.js +129 -0
- package/src/ws/IoServer.js +68 -0
- package/src/ws/core/channels/core.ws.chat.js +68 -0
- package/src/ws/core/channels/core.ws.mailer.js +116 -0
- package/src/ws/core/channels/core.ws.stream.js +90 -0
- package/src/ws/core/core.ws.connection.js +41 -0
- package/src/ws/core/core.ws.emit.js +37 -0
- package/src/ws/core/core.ws.server.js +44 -0
- package/src/ws/default/channels/default.ws.main.js +57 -0
- package/src/ws/default/default.ws.connection.js +35 -0
- package/src/ws/default/default.ws.emit.js +14 -0
- package/src/ws/default/default.ws.server.js +39 -0
- package/test/api.test.js +53 -0
- package/test/crypto.test.js +117 -0
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +267 -0
- package/test/shape-generator.test.js +463 -0
- package/tsconfig.docs.json +15 -0
- package/typedoc.dd-cyberia.json +31 -0
- package/typedoc.json +31 -0
package/src/cli/db.js
ADDED
|
@@ -0,0 +1,1533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UnderpostDB CLI module
|
|
3
|
+
* @module src/cli/db.js
|
|
4
|
+
* @namespace UnderpostDB
|
|
5
|
+
* @description Manages database operations, backups, and cluster metadata for Kubernetes deployments.
|
|
6
|
+
* Supports MariaDB and MongoDB with import/export capabilities, Git integration, and multi-pod operations.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { mergeFile, splitFileFactory, loadConfServerJson, resolveConfSecrets, loadConfInstances } from '../server/conf.js';
|
|
10
|
+
import { loggerFactory } from '../server/logger.js';
|
|
11
|
+
import { shellExec } from '../server/process.js';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import { DataBaseProviderService } from '../db/DataBaseProvider.js';
|
|
14
|
+
import { loadReplicas, pathPortAssignmentFactory, loadCronDeployEnv } from '../server/conf.js';
|
|
15
|
+
import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
|
|
16
|
+
import Underpost from '../index.js';
|
|
17
|
+
import { timer } from '../client/components/core/CommonJs.js';
|
|
18
|
+
const logger = loggerFactory(import.meta);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Constants for database operations
|
|
22
|
+
* @constant {number} MAX_BACKUP_RETENTION - Maximum number of backups to retain
|
|
23
|
+
* @memberof UnderpostDB
|
|
24
|
+
*/
|
|
25
|
+
const MAX_BACKUP_RETENTION = 5;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} DatabaseOptions
|
|
29
|
+
* @memberof UnderpostDB
|
|
30
|
+
* @property {boolean} [import=false] - Flag to import data from a backup
|
|
31
|
+
* @property {boolean} [export=false] - Flag to export data to a backup
|
|
32
|
+
* @property {string} [podName=''] - Comma-separated list of pod names or patterns
|
|
33
|
+
* @property {string} [ns='default'] - Kubernetes namespace
|
|
34
|
+
* @property {string} [collections=''] - Comma-separated list of collections to include
|
|
35
|
+
* @property {string} [outPath=''] - Output path for backup files
|
|
36
|
+
* @property {boolean} [drop=false] - Flag to drop the database before importing
|
|
37
|
+
* @property {boolean} [preserveUUID=false] - Flag to preserve UUIDs during import
|
|
38
|
+
* @property {boolean} [git=false] - Flag to enable Git integration
|
|
39
|
+
* @property {string} [hosts=''] - Comma-separated list of hosts to include
|
|
40
|
+
* @property {string} [paths=''] - Comma-separated list of paths to include
|
|
41
|
+
* @property {boolean} [allPods=false] - Flag to target all matching pods
|
|
42
|
+
* @property {boolean} [primaryPod=false] - Flag to automatically detect and use MongoDB primary pod
|
|
43
|
+
* @property {boolean} [stats=false] - Flag to display collection/table statistics
|
|
44
|
+
* @property {boolean} [forceClone=false] - Flag to force remove and re-clone cron backup repository
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {Object} PodInfo
|
|
49
|
+
* @memberof UnderpostDB
|
|
50
|
+
* @property {string} NAME - Pod name
|
|
51
|
+
* @property {string} NAMESPACE - Pod namespace
|
|
52
|
+
* @property {string} NODE - Node where pod is running
|
|
53
|
+
* @property {string} STATUS - Pod status
|
|
54
|
+
* @property {string} [IP] - Pod IP address
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {Object} DatabaseConfig
|
|
59
|
+
* @memberof UnderpostDB
|
|
60
|
+
* @property {string} provider - Database provider (mariadb, mongoose)
|
|
61
|
+
* @property {string} name - Database name
|
|
62
|
+
* @property {string} user - Database user
|
|
63
|
+
* @property {string} password - Database password
|
|
64
|
+
* @property {string} hostFolder - Host folder path
|
|
65
|
+
* @property {string} host - Host identifier
|
|
66
|
+
* @property {string} path - Path identifier
|
|
67
|
+
* @property {number} [currentBackupTimestamp] - Timestamp of current backup
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @class UnderpostDB
|
|
72
|
+
* @description Manages database operations and backups for Kubernetes-based deployments.
|
|
73
|
+
* Provides comprehensive database management including import/export, multi-pod targeting,
|
|
74
|
+
* Git integration, and cluster metadata management.
|
|
75
|
+
*/
|
|
76
|
+
/**
|
|
77
|
+
* UnderpostDB class for managing database operations and backups.
|
|
78
|
+
* @class UnderpostDB
|
|
79
|
+
* @memberof UnderpostDB
|
|
80
|
+
*/
|
|
81
|
+
class UnderpostDB {
|
|
82
|
+
/**
|
|
83
|
+
* Static API object containing all database operation methods.
|
|
84
|
+
* @static
|
|
85
|
+
* @memberof UnderpostDB
|
|
86
|
+
*/
|
|
87
|
+
static API = {
|
|
88
|
+
/**
|
|
89
|
+
* Helper: Resolves the latest backup timestamp from an existing backup directory.
|
|
90
|
+
* Scans the directory for numeric (epoch) sub-folders and returns the most recent one.
|
|
91
|
+
* @method _getLatestBackupTimestamp
|
|
92
|
+
* @memberof UnderpostDB
|
|
93
|
+
* @param {string} backupDir - Path to the host-folder backup directory.
|
|
94
|
+
* @return {string|null} The latest timestamp string, or null if none found.
|
|
95
|
+
*/
|
|
96
|
+
_getLatestBackupTimestamp(backupDir) {
|
|
97
|
+
if (!fs.existsSync(backupDir)) return null;
|
|
98
|
+
const entries = fs.readdirSync(backupDir).filter((e) => /^\d+$/.test(e));
|
|
99
|
+
if (entries.length === 0) return null;
|
|
100
|
+
return entries.sort((a, b) => parseInt(b) - parseInt(a))[0];
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Helper: Performs MariaDB import operation.
|
|
105
|
+
* @method _importMariaDB
|
|
106
|
+
* @memberof UnderpostDB
|
|
107
|
+
* @param {Object} params - Import parameters.
|
|
108
|
+
* @param {PodInfo} params.pod - Target pod.
|
|
109
|
+
* @param {string} params.namespace - Namespace.
|
|
110
|
+
* @param {string} params.dbName - Database name.
|
|
111
|
+
* @param {string} params.user - Database user.
|
|
112
|
+
* @param {string} params.password - Database password.
|
|
113
|
+
* @param {string} params.sqlPath - SQL file path.
|
|
114
|
+
* @return {boolean} Success status.
|
|
115
|
+
*/
|
|
116
|
+
_importMariaDB({ pod, namespace, dbName, user, password, sqlPath }) {
|
|
117
|
+
try {
|
|
118
|
+
const podName = pod.NAME;
|
|
119
|
+
const containerSqlPath = `/${dbName}.sql`;
|
|
120
|
+
|
|
121
|
+
logger.info('Importing MariaDB database', { podName, dbName });
|
|
122
|
+
|
|
123
|
+
// Always ensure the database exists first — required for WP even when no backup is available
|
|
124
|
+
Underpost.kubectl.run(
|
|
125
|
+
`kubectl exec -n ${namespace} -i ${podName} -- mariadb -p${password} -e 'CREATE DATABASE IF NOT EXISTS ${dbName};'`,
|
|
126
|
+
{ context: `create database ${dbName}` },
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// If no SQL file is available, the empty database is enough — return early
|
|
130
|
+
if (!sqlPath || !fs.existsSync(sqlPath)) {
|
|
131
|
+
logger.warn('No SQL backup file found — empty database ensured', { podName, dbName, sqlPath });
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Remove existing SQL file in container
|
|
136
|
+
Underpost.kubectl.exec({
|
|
137
|
+
podName,
|
|
138
|
+
namespace,
|
|
139
|
+
command: `rm -rf ${containerSqlPath}`,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Copy SQL file to pod
|
|
143
|
+
if (
|
|
144
|
+
!Underpost.kubectl.cpTo({
|
|
145
|
+
sourcePath: sqlPath,
|
|
146
|
+
podName,
|
|
147
|
+
namespace,
|
|
148
|
+
destPath: containerSqlPath,
|
|
149
|
+
})
|
|
150
|
+
) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Import SQL file
|
|
155
|
+
const importCmd = `mariadb -u ${user} -p${password} ${dbName} < ${containerSqlPath}`;
|
|
156
|
+
Underpost.kubectl.exec({ podName, namespace, command: importCmd });
|
|
157
|
+
|
|
158
|
+
logger.info('Successfully imported MariaDB database', { podName, dbName });
|
|
159
|
+
return true;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
logger.error('MariaDB import failed', { podName: pod.NAME, dbName, error: error.message });
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Helper: Performs MariaDB export operation.
|
|
168
|
+
* @method _exportMariaDB
|
|
169
|
+
* @memberof UnderpostDB
|
|
170
|
+
* @param {Object} params - Export parameters.
|
|
171
|
+
* @param {PodInfo} params.pod - Source pod.
|
|
172
|
+
* @param {string} params.namespace - Namespace.
|
|
173
|
+
* @param {string} params.dbName - Database name.
|
|
174
|
+
* @param {string} params.user - Database user.
|
|
175
|
+
* @param {string} params.password - Database password.
|
|
176
|
+
* @param {string} params.outputPath - Output file path.
|
|
177
|
+
* @return {Promise<boolean>} A promise that resolves with success status.
|
|
178
|
+
*/
|
|
179
|
+
async _exportMariaDB({ pod, namespace, dbName, user, password, outputPath }) {
|
|
180
|
+
try {
|
|
181
|
+
const podName = pod.NAME;
|
|
182
|
+
const containerSqlPath = `/home/${dbName}.sql`;
|
|
183
|
+
|
|
184
|
+
logger.info('Exporting MariaDB database', { podName, dbName });
|
|
185
|
+
|
|
186
|
+
// Remove existing SQL file in container
|
|
187
|
+
Underpost.kubectl.exec({
|
|
188
|
+
podName,
|
|
189
|
+
namespace,
|
|
190
|
+
command: `rm -rf ${containerSqlPath}`,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Dump database
|
|
194
|
+
const dumpCmd = `mariadb-dump --user=${user} --password=${password} --lock-tables ${dbName} > ${containerSqlPath}`;
|
|
195
|
+
Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
|
|
196
|
+
|
|
197
|
+
// Copy SQL file from pod
|
|
198
|
+
if (
|
|
199
|
+
!Underpost.kubectl.cpFrom({
|
|
200
|
+
podName,
|
|
201
|
+
namespace,
|
|
202
|
+
sourcePath: containerSqlPath,
|
|
203
|
+
destPath: outputPath,
|
|
204
|
+
})
|
|
205
|
+
) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Split file if it exists
|
|
210
|
+
if (fs.existsSync(outputPath)) {
|
|
211
|
+
await splitFileFactory(dbName, outputPath);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
logger.info('Successfully exported MariaDB database', { podName, dbName, outputPath });
|
|
215
|
+
return true;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logger.error('MariaDB export failed', { podName: pod.NAME, dbName, error: error.message });
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Helper: Performs MongoDB import operation.
|
|
224
|
+
* @method _importMongoDB
|
|
225
|
+
* @memberof UnderpostDB
|
|
226
|
+
* @param {Object} params - Import parameters.
|
|
227
|
+
* @param {PodInfo} params.pod - Target pod.
|
|
228
|
+
* @param {string} params.namespace - Namespace.
|
|
229
|
+
* @param {string} params.dbName - Database name.
|
|
230
|
+
* @param {string} params.bsonPath - BSON directory path.
|
|
231
|
+
* @param {boolean} params.drop - Whether to drop existing database.
|
|
232
|
+
* @param {boolean} params.preserveUUID - Whether to preserve UUIDs.
|
|
233
|
+
* @param {string} [params.user=''] - MongoDB username for authenticated restore.
|
|
234
|
+
* @param {string} [params.password=''] - MongoDB password for authenticated restore.
|
|
235
|
+
* @param {string} [params.authDatabase='admin'] - Auth database for restore command.
|
|
236
|
+
* @return {boolean} Success status.
|
|
237
|
+
*/
|
|
238
|
+
_importMongoDB({ pod, namespace, dbName, bsonPath, drop, preserveUUID, user = '', password = '', authDatabase = 'admin' }) {
|
|
239
|
+
try {
|
|
240
|
+
const podName = pod.NAME;
|
|
241
|
+
const containerBsonPath = `/${dbName}`;
|
|
242
|
+
|
|
243
|
+
logger.info('Importing MongoDB database', { podName, dbName });
|
|
244
|
+
|
|
245
|
+
// If no BSON directory is available, skip — MongoDB creates the DB on first write
|
|
246
|
+
if (!bsonPath || !fs.existsSync(bsonPath)) {
|
|
247
|
+
logger.warn('No BSON backup directory found — database will be created on first write', {
|
|
248
|
+
podName,
|
|
249
|
+
dbName,
|
|
250
|
+
bsonPath,
|
|
251
|
+
});
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Remove existing BSON directory in container
|
|
256
|
+
Underpost.kubectl.exec({
|
|
257
|
+
podName,
|
|
258
|
+
namespace,
|
|
259
|
+
command: `rm -rf ${containerBsonPath}`,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Copy BSON directory to pod
|
|
263
|
+
if (
|
|
264
|
+
!Underpost.kubectl.cpTo({
|
|
265
|
+
sourcePath: bsonPath,
|
|
266
|
+
podName,
|
|
267
|
+
namespace,
|
|
268
|
+
destPath: containerBsonPath,
|
|
269
|
+
})
|
|
270
|
+
) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Restore database
|
|
275
|
+
const authFlags = user && password
|
|
276
|
+
? ` --username ${JSON.stringify(user)} --password ${JSON.stringify(password)} --authenticationDatabase ${JSON.stringify(authDatabase)}`
|
|
277
|
+
: '';
|
|
278
|
+
const restoreCmd = `mongorestore -d ${dbName} ${containerBsonPath}${drop ? ' --drop' : ''}${preserveUUID ? ' --preserveUUID' : ''}${authFlags}`;
|
|
279
|
+
Underpost.kubectl.exec({ podName, namespace, command: restoreCmd });
|
|
280
|
+
|
|
281
|
+
logger.info('Successfully imported MongoDB database', { podName, dbName });
|
|
282
|
+
return true;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
logger.error('MongoDB import failed', { podName: pod.NAME, dbName, error: error.message });
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Helper: Performs MongoDB export operation.
|
|
291
|
+
* @method _exportMongoDB
|
|
292
|
+
* @memberof UnderpostDB
|
|
293
|
+
* @param {Object} params - Export parameters.
|
|
294
|
+
* @param {PodInfo} params.pod - Source pod.
|
|
295
|
+
* @param {string} params.namespace - Namespace.
|
|
296
|
+
* @param {string} params.dbName - Database name.
|
|
297
|
+
* @param {string} params.outputPath - Output directory path.
|
|
298
|
+
* @param {string} [params.collections=''] - Comma-separated collection list.
|
|
299
|
+
* @param {string} [params.user=''] - MongoDB username for authenticated dump.
|
|
300
|
+
* @param {string} [params.password=''] - MongoDB password for authenticated dump.
|
|
301
|
+
* @param {string} [params.authDatabase='admin'] - Auth database for dump command.
|
|
302
|
+
* @return {boolean} Success status.
|
|
303
|
+
*/
|
|
304
|
+
_exportMongoDB({ pod, namespace, dbName, outputPath, collections = '', user = '', password = '', authDatabase = 'admin' }) {
|
|
305
|
+
try {
|
|
306
|
+
const podName = pod.NAME;
|
|
307
|
+
const containerBsonPath = `/${dbName}`;
|
|
308
|
+
|
|
309
|
+
logger.info('Exporting MongoDB database', { podName, dbName, collections });
|
|
310
|
+
|
|
311
|
+
// Remove existing BSON directory in container
|
|
312
|
+
Underpost.kubectl.exec({
|
|
313
|
+
podName,
|
|
314
|
+
namespace,
|
|
315
|
+
command: `rm -rf ${containerBsonPath}`,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Dump database or specific collections
|
|
319
|
+
const authFlags = user && password
|
|
320
|
+
? ` --username ${JSON.stringify(user)} --password ${JSON.stringify(password)} --authenticationDatabase ${JSON.stringify(authDatabase)}`
|
|
321
|
+
: '';
|
|
322
|
+
|
|
323
|
+
if (collections) {
|
|
324
|
+
const collectionList = collections.split(',').map((c) => c.trim());
|
|
325
|
+
for (const collection of collectionList) {
|
|
326
|
+
const dumpCmd = `mongodump -d ${dbName} --collection ${collection} -o /${authFlags}`;
|
|
327
|
+
Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
const dumpCmd = `mongodump -d ${dbName} -o /${authFlags}`;
|
|
331
|
+
Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Copy BSON directory from pod
|
|
335
|
+
if (
|
|
336
|
+
!Underpost.kubectl.cpFrom({
|
|
337
|
+
podName,
|
|
338
|
+
namespace,
|
|
339
|
+
sourcePath: containerBsonPath,
|
|
340
|
+
destPath: outputPath,
|
|
341
|
+
})
|
|
342
|
+
) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
logger.info('Successfully exported MongoDB database', { podName, dbName, outputPath });
|
|
347
|
+
return true;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
logger.error('MongoDB export failed', { podName: pod.NAME, dbName, error: error.message });
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Helper: Gets MongoDB collection statistics.
|
|
356
|
+
* @method _getMongoStats
|
|
357
|
+
* @memberof UnderpostDB
|
|
358
|
+
* @param {Object} params - Parameters.
|
|
359
|
+
* @param {string} params.podName - Pod name.
|
|
360
|
+
* @param {string} params.namespace - Namespace.
|
|
361
|
+
* @param {string} params.dbName - Database name.
|
|
362
|
+
* @param {string} [params.user=''] - MongoDB username for authenticated stats query.
|
|
363
|
+
* @param {string} [params.password=''] - MongoDB password for authenticated stats query.
|
|
364
|
+
* @param {string} [params.authDatabase='admin'] - Auth database for stats query.
|
|
365
|
+
* @return {Object|null} Collection statistics or null on error.
|
|
366
|
+
*/
|
|
367
|
+
_getMongoStats({ podName, namespace, dbName, user = '', password = '', authDatabase = 'admin' }) {
|
|
368
|
+
try {
|
|
369
|
+
logger.info('Getting MongoDB collection statistics', { podName, dbName });
|
|
370
|
+
|
|
371
|
+
// Use db.getSiblingDB() instead of 'use' command
|
|
372
|
+
const script = `db.getSiblingDB('${dbName}').getCollectionNames().map(function(c) { return { collection: c, count: db.getSiblingDB('${dbName}')[c].countDocuments() }; })`;
|
|
373
|
+
|
|
374
|
+
// Execute the script
|
|
375
|
+
const authFlags = user && password
|
|
376
|
+
? ` --authenticationDatabase ${JSON.stringify(authDatabase)} -u ${JSON.stringify(user)} -p ${JSON.stringify(password)}`
|
|
377
|
+
: '';
|
|
378
|
+
const command = `sudo kubectl exec -n ${namespace} -i ${podName} -- mongosh --quiet${authFlags} --eval "${script}"`;
|
|
379
|
+
const output = shellExec(command, { stdout: true, silent: true, silentOnError: true });
|
|
380
|
+
|
|
381
|
+
if (!output || output.trim() === '') {
|
|
382
|
+
logger.warn('No collections found or empty output');
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Clean the output: remove newlines, handle EJSON format, replace single quotes with double quotes
|
|
387
|
+
let cleanedOutput = output
|
|
388
|
+
.trim()
|
|
389
|
+
.replace(/\n/g, '')
|
|
390
|
+
.replace(/\s+/g, ' ')
|
|
391
|
+
.replace(/NumberLong\("(\d+)"\)/g, '$1')
|
|
392
|
+
.replace(/NumberLong\((\d+)\)/g, '$1')
|
|
393
|
+
.replace(/NumberInt\("(\d+)"\)/g, '$1')
|
|
394
|
+
.replace(/NumberInt\((\d+)\)/g, '$1')
|
|
395
|
+
.replace(/ISODate\("([^"]+)"\)/g, '"$1"')
|
|
396
|
+
.replace(/'/g, '"')
|
|
397
|
+
.replace(/(\w+):/g, '"$1":');
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
const stats = JSON.parse(cleanedOutput);
|
|
401
|
+
logger.info('MongoDB statistics retrieved', { dbName, collections: stats.length });
|
|
402
|
+
return stats;
|
|
403
|
+
} catch (parseError) {
|
|
404
|
+
logger.error('Failed to parse MongoDB output', {
|
|
405
|
+
podName,
|
|
406
|
+
dbName,
|
|
407
|
+
error: parseError.message,
|
|
408
|
+
rawOutput: output.substring(0, 200),
|
|
409
|
+
cleanedOutput: cleanedOutput.substring(0, 200),
|
|
410
|
+
});
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
} catch (error) {
|
|
414
|
+
logger.error('Failed to get MongoDB statistics', { podName, dbName, error: error.message });
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Helper: Gets MariaDB table statistics.
|
|
421
|
+
* @method _getMariaDBStats
|
|
422
|
+
* @memberof UnderpostDB
|
|
423
|
+
* @param {Object} params - Parameters.
|
|
424
|
+
* @param {string} params.podName - Pod name.
|
|
425
|
+
* @param {string} params.namespace - Namespace.
|
|
426
|
+
* @param {string} params.dbName - Database name.
|
|
427
|
+
* @param {string} params.user - Database user.
|
|
428
|
+
* @param {string} params.password - Database password.
|
|
429
|
+
* @return {Object|null} Table statistics or null on error.
|
|
430
|
+
*/
|
|
431
|
+
_getMariaDBStats({ podName, namespace, dbName, user, password }) {
|
|
432
|
+
try {
|
|
433
|
+
logger.info('Getting MariaDB table statistics', { podName, dbName });
|
|
434
|
+
|
|
435
|
+
const command = `sudo kubectl exec -n ${namespace} -i ${podName} -- mariadb -u ${user} -p${password} ${dbName} -e "SELECT TABLE_NAME as 'table', TABLE_ROWS as 'count' FROM information_schema.TABLES WHERE TABLE_SCHEMA = '${dbName}' ORDER BY TABLE_NAME;" --skip-column-names --batch`;
|
|
436
|
+
const output = shellExec(command, { stdout: true, silent: true, disableLog: true, silentOnError: true });
|
|
437
|
+
|
|
438
|
+
if (!output || output.trim() === '') {
|
|
439
|
+
logger.warn('No tables found or empty output');
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Parse the output (tab-separated values)
|
|
444
|
+
const lines = output.trim().split('\n');
|
|
445
|
+
const stats = lines.map((line) => {
|
|
446
|
+
const [table, count] = line.split('\t');
|
|
447
|
+
return { table, count: parseInt(count) || 0 };
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
logger.info('MariaDB statistics retrieved', { dbName, tables: stats.length });
|
|
451
|
+
return stats;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
logger.error('Failed to get MariaDB statistics', { podName, dbName, error: error.message });
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Helper: Displays database statistics in table format.
|
|
460
|
+
* @method _displayStats
|
|
461
|
+
* @memberof UnderpostDB
|
|
462
|
+
* @param {Object} params - Parameters.
|
|
463
|
+
* @param {string} params.provider - Database provider.
|
|
464
|
+
* @param {string} params.dbName - Database name.
|
|
465
|
+
* @param {Array<Object>} params.stats - Statistics array.
|
|
466
|
+
* @return {void}
|
|
467
|
+
*/
|
|
468
|
+
_displayStats({ provider, dbName, stats }) {
|
|
469
|
+
if (!stats || stats.length === 0) {
|
|
470
|
+
logger.warn('No statistics to display', { provider, dbName });
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const title = provider === 'mongoose' ? 'Collections' : 'Tables';
|
|
475
|
+
const itemKey = provider === 'mongoose' ? 'collection' : 'table';
|
|
476
|
+
|
|
477
|
+
console.log('\n' + '='.repeat(70));
|
|
478
|
+
console.log(`DATABASE: ${dbName} (${provider.toUpperCase()})`);
|
|
479
|
+
console.log('='.repeat(70));
|
|
480
|
+
console.log(`${title.padEnd(50)} ${'Documents/Rows'.padStart(18)}`);
|
|
481
|
+
console.log('-'.repeat(70));
|
|
482
|
+
|
|
483
|
+
let totalCount = 0;
|
|
484
|
+
stats.forEach((item) => {
|
|
485
|
+
const name = item[itemKey] || 'Unknown';
|
|
486
|
+
const count = item.count || 0;
|
|
487
|
+
totalCount += count;
|
|
488
|
+
console.log(`${name.padEnd(50)} ${count.toString().padStart(18)}`);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
console.log('-'.repeat(70));
|
|
492
|
+
console.log(`${'TOTAL'.padEnd(50)} ${totalCount.toString().padStart(18)}`);
|
|
493
|
+
console.log('='.repeat(70) + '\n');
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Main callback: Initiates database backup workflow.
|
|
500
|
+
* Orchestrates the backup process for multiple deployments, handling
|
|
501
|
+
* database connections, backup storage, and optional Git integration for version control.
|
|
502
|
+
* Supports targeting multiple specific pods, nodes, and namespaces with advanced filtering.
|
|
503
|
+
* @method callback
|
|
504
|
+
* @memberof UnderpostDB
|
|
505
|
+
* @param {string} [deployList='default'] - Comma-separated list of deployment IDs.
|
|
506
|
+
* @param {Object} [options={}] - Backup options.
|
|
507
|
+
* @param {boolean} [options.import=false] - Whether to perform import operation.
|
|
508
|
+
* @param {boolean} [options.export=false] - Whether to perform export operation.
|
|
509
|
+
* @param {string} [options.podName=''] - Comma-separated pod name patterns to target.
|
|
510
|
+
* @param {string} [options.ns='default'] - Kubernetes namespace.
|
|
511
|
+
* @param {string} [options.collections=''] - Comma-separated MongoDB collections for export.
|
|
512
|
+
* @param {string} [options.outPath=''] - Output path for backups.
|
|
513
|
+
* @param {boolean} [options.drop=false] - Whether to drop existing database on import.
|
|
514
|
+
* @param {boolean} [options.preserveUUID=false] - Whether to preserve UUIDs on MongoDB import.
|
|
515
|
+
* @param {boolean} [options.git=false] - Whether to use Git for backup versioning.
|
|
516
|
+
* @param {string} [options.hosts=''] - Comma-separated list of hosts to filter databases.
|
|
517
|
+
* @param {string} [options.paths=''] - Comma-separated list of paths to filter databases.
|
|
518
|
+
* @param {boolean} [options.allPods=false] - Whether to target all pods in deployment.
|
|
519
|
+
* @param {boolean} [options.primaryPod=false] - Whether to target MongoDB primary pod only.
|
|
520
|
+
* @param {string} [options.primaryPodEnsure=''] - Pod name to ensure MongoDB primary pod is running.
|
|
521
|
+
* @param {boolean} [options.stats=false] - Whether to display database statistics.
|
|
522
|
+
* @param {number} [options.macroRollbackExport=1] - Number of commits to rollback in macro export.
|
|
523
|
+
* @param {boolean} [options.forceClone=false] - Whether to force re-clone Git repository.
|
|
524
|
+
* @param {boolean} [options.cleanFsCollection=false] - Clean orphaned File documents flag.
|
|
525
|
+
* @param {boolean} [options.cleanFsDryRun=false] - Dry run mode flag (use with cleanFsCollection).
|
|
526
|
+
* @param {boolean} [options.dev=false] - Development mode flag.
|
|
527
|
+
* @param {boolean} [options.k3s=false] - k3s cluster flag.
|
|
528
|
+
* @param {boolean} [options.kubeadm=false] - kubeadm cluster flag.
|
|
529
|
+
* @param {boolean} [options.kind=false] - kind cluster flag.
|
|
530
|
+
* @param {boolean} [options.repoBackup=false] - Backs up repositories (git commit+push) inside deployment pods via kubectl exec.
|
|
531
|
+
* @return {Promise<void>} Resolves when operation is complete.
|
|
532
|
+
*/
|
|
533
|
+
async callback(
|
|
534
|
+
deployList = 'default',
|
|
535
|
+
options = {
|
|
536
|
+
import: false,
|
|
537
|
+
export: false,
|
|
538
|
+
podName: '',
|
|
539
|
+
ns: 'default',
|
|
540
|
+
collections: '',
|
|
541
|
+
outPath: '',
|
|
542
|
+
drop: false,
|
|
543
|
+
preserveUUID: false,
|
|
544
|
+
git: false,
|
|
545
|
+
hosts: '',
|
|
546
|
+
paths: '',
|
|
547
|
+
allPods: false,
|
|
548
|
+
primaryPod: false,
|
|
549
|
+
primaryPodEnsure: '',
|
|
550
|
+
stats: false,
|
|
551
|
+
macroRollbackExport: 1,
|
|
552
|
+
forceClone: false,
|
|
553
|
+
cleanFsCollection: false,
|
|
554
|
+
cleanFsDryRun: false,
|
|
555
|
+
dev: false,
|
|
556
|
+
k3s: false,
|
|
557
|
+
kubeadm: false,
|
|
558
|
+
kind: false,
|
|
559
|
+
repoBackup: false,
|
|
560
|
+
},
|
|
561
|
+
) {
|
|
562
|
+
// Ensure engine-private is available (clone if inside a deployment
|
|
563
|
+
// container where globalSecretClean has already removed it).
|
|
564
|
+
const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
|
|
565
|
+
try {
|
|
566
|
+
loadCronDeployEnv();
|
|
567
|
+
const newBackupTimestamp = new Date().getTime();
|
|
568
|
+
const namespace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
|
|
569
|
+
|
|
570
|
+
if (deployList === 'dd') deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
571
|
+
|
|
572
|
+
// Handle repository backup (git commit+push inside deployment pod)
|
|
573
|
+
if (options.repoBackup) {
|
|
574
|
+
const namespace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
|
|
575
|
+
for (const _deployId of deployList.split(',')) {
|
|
576
|
+
const deployId = _deployId.trim();
|
|
577
|
+
if (!deployId) continue;
|
|
578
|
+
logger.info('Starting pod repository backup', { deployId, namespace });
|
|
579
|
+
Underpost.repo.backupPodRepositories({
|
|
580
|
+
deployId,
|
|
581
|
+
namespace,
|
|
582
|
+
env: options.dev ? 'development' : 'production',
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Handle clean-fs-collection operation
|
|
589
|
+
if (options.cleanFsCollection || options.cleanFsDryRun) {
|
|
590
|
+
logger.info('Starting File collection cleanup operation', { deployList });
|
|
591
|
+
await Underpost.db.cleanFsCollection(deployList, {
|
|
592
|
+
hosts: options.hosts,
|
|
593
|
+
paths: options.paths,
|
|
594
|
+
dryRun: options.cleanFsDryRun,
|
|
595
|
+
});
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
logger.info('Starting database operation', {
|
|
600
|
+
deployList,
|
|
601
|
+
namespace,
|
|
602
|
+
import: options.import,
|
|
603
|
+
export: options.export,
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
if (options.primaryPodEnsure) {
|
|
607
|
+
const primaryPodName = MongoBootstrap.getPrimaryPodName({
|
|
608
|
+
namespace,
|
|
609
|
+
podName: options.primaryPodEnsure,
|
|
610
|
+
username: process.env.MONGODB_USERNAME || process.env.DB_USER || '',
|
|
611
|
+
password: process.env.MONGODB_PASSWORD || process.env.DB_PASSWORD || '',
|
|
612
|
+
authDatabase: process.env.MONGODB_AUTH_DB || 'admin',
|
|
613
|
+
});
|
|
614
|
+
if (!primaryPodName) {
|
|
615
|
+
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
616
|
+
const baseClusterCommand = options.dev ? ' --dev' : '';
|
|
617
|
+
let clusterFlag = options.k3s ? ' --k3s' : options.kubeadm ? ' --kubeadm' : '';
|
|
618
|
+
shellExec(`${baseCommand} cluster${baseClusterCommand}${clusterFlag} --mongodb`);
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Track processed repositories to avoid duplicate Git operations
|
|
624
|
+
const processedRepos = new Set();
|
|
625
|
+
// Track processed host+path combinations to avoid duplicates
|
|
626
|
+
const processedHostPaths = new Set();
|
|
627
|
+
|
|
628
|
+
for (const _deployId of deployList.split(',')) {
|
|
629
|
+
const deployId = _deployId.trim();
|
|
630
|
+
if (!deployId) continue;
|
|
631
|
+
|
|
632
|
+
logger.info('Processing deployment', { deployId });
|
|
633
|
+
|
|
634
|
+
/** @type {Object.<string, Object.<string, DatabaseConfig>>} */
|
|
635
|
+
const dbs = {};
|
|
636
|
+
const repoName = `engine-${deployId.includes('dd-') ? deployId.split('dd-')[1] : deployId}-cron-backups`;
|
|
637
|
+
|
|
638
|
+
// Load server configuration
|
|
639
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
640
|
+
if (!fs.existsSync(confServerPath)) {
|
|
641
|
+
logger.error('Configuration file not found', { path: confServerPath });
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
646
|
+
|
|
647
|
+
// Build database configuration map
|
|
648
|
+
for (const host of Object.keys(confServer)) {
|
|
649
|
+
for (const path of Object.keys(confServer[host])) {
|
|
650
|
+
const { db } = confServer[host][path];
|
|
651
|
+
if (db) {
|
|
652
|
+
const { provider, name, user, password } = db;
|
|
653
|
+
if (!dbs[provider]) dbs[provider] = {};
|
|
654
|
+
|
|
655
|
+
if (!(name in dbs[provider])) {
|
|
656
|
+
dbs[provider][name] = {
|
|
657
|
+
user,
|
|
658
|
+
password,
|
|
659
|
+
hostFolder: host + path.replaceAll('/', '-'),
|
|
660
|
+
host,
|
|
661
|
+
path,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Handle Git operations - execute only once per repository
|
|
669
|
+
if (!processedRepos.has(repoName)) {
|
|
670
|
+
logger.info('Processing Git operations for repository', { repoName, deployId });
|
|
671
|
+
if (options.git === true) {
|
|
672
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'clone', forceClone: options.forceClone });
|
|
673
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'pull' });
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (options.macroRollbackExport) {
|
|
677
|
+
// Only clone if not already done by git option above
|
|
678
|
+
if (options.git !== true) {
|
|
679
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'clone', forceClone: options.forceClone });
|
|
680
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'pull' });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const nCommits = parseInt(options.macroRollbackExport);
|
|
684
|
+
const repoPath = `../${repoName}`;
|
|
685
|
+
const username = process.env.GITHUB_USERNAME;
|
|
686
|
+
|
|
687
|
+
if (fs.existsSync(repoPath) && username) {
|
|
688
|
+
logger.info('Executing macro rollback export', { repoName, nCommits });
|
|
689
|
+
shellExec(`cd ${repoPath} && underpost cmt . reset ${nCommits}`);
|
|
690
|
+
shellExec(`cd ${repoPath} && git reset`);
|
|
691
|
+
shellExec(`cd ${repoPath} && git checkout .`);
|
|
692
|
+
shellExec(`cd ${repoPath} && git clean -f -d`);
|
|
693
|
+
shellExec(`cd ${repoPath} && underpost push . ${username}/${repoName} -f`);
|
|
694
|
+
} else {
|
|
695
|
+
if (!username) logger.error('GITHUB_USERNAME environment variable not set');
|
|
696
|
+
logger.warn('Repository not found for macro rollback', { repoPath });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
processedRepos.add(repoName);
|
|
701
|
+
logger.info('Repository marked as processed', { repoName });
|
|
702
|
+
} else {
|
|
703
|
+
logger.info('Skipping Git operations for already processed repository', { repoName, deployId });
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Process each database provider
|
|
707
|
+
for (const provider of Object.keys(dbs)) {
|
|
708
|
+
for (const dbName of Object.keys(dbs[provider])) {
|
|
709
|
+
const { hostFolder, user, password, host, path } = dbs[provider][dbName];
|
|
710
|
+
|
|
711
|
+
// Create unique identifier for host+path combination
|
|
712
|
+
const hostPathKey = `${deployId}:${host}:${path}`;
|
|
713
|
+
|
|
714
|
+
// Skip if this host+path combination was already processed
|
|
715
|
+
if (processedHostPaths.has(hostPathKey)) {
|
|
716
|
+
logger.info('Skipping already processed host/path', { dbName, host, path, deployId });
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Filter by hosts and paths if specified
|
|
721
|
+
if (
|
|
722
|
+
(options.hosts &&
|
|
723
|
+
!options.hosts
|
|
724
|
+
.split(',')
|
|
725
|
+
.map((h) => h.trim())
|
|
726
|
+
.includes(host)) ||
|
|
727
|
+
(options.paths &&
|
|
728
|
+
!options.paths
|
|
729
|
+
.split(',')
|
|
730
|
+
.map((p) => p.trim())
|
|
731
|
+
.includes(path))
|
|
732
|
+
) {
|
|
733
|
+
logger.info('Skipping database due to host/path filter', { dbName, host, path });
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (!hostFolder) {
|
|
738
|
+
logger.warn('No hostFolder defined for database', { dbName, provider });
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
logger.info('Processing database', { hostFolder, provider, dbName, deployId });
|
|
743
|
+
|
|
744
|
+
const latestBackupTimestamp = Underpost.db._getLatestBackupTimestamp(`../${repoName}/${hostFolder}`);
|
|
745
|
+
|
|
746
|
+
dbs[provider][dbName].currentBackupTimestamp = latestBackupTimestamp;
|
|
747
|
+
|
|
748
|
+
const currentTimestamp = latestBackupTimestamp || newBackupTimestamp;
|
|
749
|
+
const sqlContainerPath = `/home/${dbName}.sql`;
|
|
750
|
+
const fromPartsPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}-parths.json`;
|
|
751
|
+
const toSqlPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}.sql`;
|
|
752
|
+
const toNewSqlPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}.sql`;
|
|
753
|
+
const toBsonPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}`;
|
|
754
|
+
const toNewBsonPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}`;
|
|
755
|
+
|
|
756
|
+
// Merge split SQL files if needed for import
|
|
757
|
+
if (options.import === true && fs.existsSync(fromPartsPath) && !fs.existsSync(toSqlPath)) {
|
|
758
|
+
const names = JSON.parse(fs.readFileSync(fromPartsPath, 'utf8')).map((_path) => {
|
|
759
|
+
return `../${repoName}/${hostFolder}/${currentTimestamp}/${_path.split('/').pop()}`;
|
|
760
|
+
});
|
|
761
|
+
logger.info('Merging backup parts', { fromPartsPath, toSqlPath, parts: names.length });
|
|
762
|
+
await mergeFile(names, toSqlPath);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Get target pods based on provider and options
|
|
766
|
+
let targetPods = [];
|
|
767
|
+
const podCriteria = {
|
|
768
|
+
podNames: options.podName,
|
|
769
|
+
namespace,
|
|
770
|
+
deployId: provider === 'mariadb' ? 'mariadb' : 'mongo',
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
targetPods = Underpost.kubectl.getFilteredPods(podCriteria);
|
|
774
|
+
|
|
775
|
+
// Fallback to default if no custom pods specified
|
|
776
|
+
if (targetPods.length === 0 && !options.podName) {
|
|
777
|
+
const defaultPods = Underpost.kubectl.get(
|
|
778
|
+
provider === 'mariadb' ? 'mariadb' : 'mongo',
|
|
779
|
+
'pods',
|
|
780
|
+
namespace,
|
|
781
|
+
);
|
|
782
|
+
console.log('defaultPods', defaultPods);
|
|
783
|
+
targetPods = defaultPods;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (targetPods.length === 0) {
|
|
787
|
+
logger.warn('No pods found matching criteria', { provider, criteria: podCriteria });
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Handle primary pod detection for MongoDB
|
|
792
|
+
let podsToProcess = [];
|
|
793
|
+
if (provider === 'mongoose' && !options.allPods) {
|
|
794
|
+
// For MongoDB, always use primary pod unless allPods is true
|
|
795
|
+
if (!targetPods || targetPods.length === 0) {
|
|
796
|
+
logger.warn('No MongoDB pods available to check for primary');
|
|
797
|
+
podsToProcess = [];
|
|
798
|
+
} else {
|
|
799
|
+
const firstPod = targetPods[0].NAME;
|
|
800
|
+
const primaryPodName = MongoBootstrap.getPrimaryPodName({
|
|
801
|
+
namespace,
|
|
802
|
+
podName: firstPod,
|
|
803
|
+
username: user,
|
|
804
|
+
password,
|
|
805
|
+
authDatabase: 'admin',
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
if (primaryPodName) {
|
|
809
|
+
const primaryPod = targetPods.find((p) => p.NAME === primaryPodName);
|
|
810
|
+
if (primaryPod) {
|
|
811
|
+
podsToProcess = [primaryPod];
|
|
812
|
+
logger.info('Using MongoDB primary pod', { primaryPod: primaryPodName });
|
|
813
|
+
} else {
|
|
814
|
+
logger.warn('Primary pod not in filtered list, using first pod', { primaryPodName });
|
|
815
|
+
podsToProcess = [targetPods[0]];
|
|
816
|
+
}
|
|
817
|
+
} else {
|
|
818
|
+
logger.warn('Could not detect primary pod, using first pod');
|
|
819
|
+
podsToProcess = [targetPods[0]];
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
} else {
|
|
823
|
+
// For MariaDB or when allPods is true, limit to first pod unless allPods is true
|
|
824
|
+
podsToProcess = options.allPods === true ? targetPods : [targetPods[0]];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
logger.info(`Processing ${podsToProcess.length} pod(s) for ${provider}`, {
|
|
828
|
+
dbName,
|
|
829
|
+
pods: podsToProcess.map((p) => p.NAME),
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
let exportSucceeded = false;
|
|
833
|
+
|
|
834
|
+
// Process each pod
|
|
835
|
+
for (const pod of podsToProcess) {
|
|
836
|
+
logger.info('Processing pod', { podName: pod.NAME, node: pod.NODE, status: pod.STATUS });
|
|
837
|
+
|
|
838
|
+
switch (provider) {
|
|
839
|
+
case 'mariadb': {
|
|
840
|
+
if (options.stats === true) {
|
|
841
|
+
const stats = Underpost.db._getMariaDBStats({
|
|
842
|
+
podName: pod.NAME,
|
|
843
|
+
namespace,
|
|
844
|
+
dbName,
|
|
845
|
+
user,
|
|
846
|
+
password,
|
|
847
|
+
});
|
|
848
|
+
if (stats) {
|
|
849
|
+
Underpost.db._displayStats({ provider, dbName, stats });
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (options.import === true) {
|
|
854
|
+
Underpost.db._importMariaDB({
|
|
855
|
+
pod,
|
|
856
|
+
namespace,
|
|
857
|
+
dbName,
|
|
858
|
+
user,
|
|
859
|
+
password,
|
|
860
|
+
sqlPath: toSqlPath,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (options.export === true) {
|
|
865
|
+
const outputPath = options.outPath || toNewSqlPath;
|
|
866
|
+
const success = await Underpost.db._exportMariaDB({
|
|
867
|
+
pod,
|
|
868
|
+
namespace,
|
|
869
|
+
dbName,
|
|
870
|
+
user,
|
|
871
|
+
password,
|
|
872
|
+
outputPath,
|
|
873
|
+
});
|
|
874
|
+
exportSucceeded = exportSucceeded || success;
|
|
875
|
+
}
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
case 'mongoose': {
|
|
880
|
+
if (options.stats === true) {
|
|
881
|
+
const stats = Underpost.db._getMongoStats({
|
|
882
|
+
podName: pod.NAME,
|
|
883
|
+
namespace,
|
|
884
|
+
dbName,
|
|
885
|
+
user,
|
|
886
|
+
password,
|
|
887
|
+
authDatabase: 'admin',
|
|
888
|
+
});
|
|
889
|
+
if (stats) {
|
|
890
|
+
Underpost.db._displayStats({ provider, dbName, stats });
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (options.import === true) {
|
|
895
|
+
const bsonPath = options.outPath || toBsonPath;
|
|
896
|
+
Underpost.db._importMongoDB({
|
|
897
|
+
pod,
|
|
898
|
+
namespace,
|
|
899
|
+
dbName,
|
|
900
|
+
bsonPath,
|
|
901
|
+
drop: options.drop,
|
|
902
|
+
preserveUUID: options.preserveUUID,
|
|
903
|
+
user,
|
|
904
|
+
password,
|
|
905
|
+
authDatabase: 'admin',
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (options.export === true) {
|
|
910
|
+
const outputPath = options.outPath || toNewBsonPath;
|
|
911
|
+
const success = Underpost.db._exportMongoDB({
|
|
912
|
+
pod,
|
|
913
|
+
namespace,
|
|
914
|
+
dbName,
|
|
915
|
+
outputPath,
|
|
916
|
+
collections: options.collections,
|
|
917
|
+
user,
|
|
918
|
+
password,
|
|
919
|
+
authDatabase: 'admin',
|
|
920
|
+
});
|
|
921
|
+
exportSucceeded = exportSucceeded || success;
|
|
922
|
+
}
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
default:
|
|
927
|
+
logger.warn('Unsupported database provider', { provider });
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (options.export === true && exportSucceeded === true) {
|
|
933
|
+
Underpost.db._enforceBackupRetention(`../${repoName}/${hostFolder}`);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Mark this host+path combination as processed
|
|
937
|
+
processedHostPaths.add(hostPathKey);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Commit and push to Git if enabled - execute only once per repository
|
|
942
|
+
if (options.export === true && options.git === true && !processedRepos.has(`${repoName}-committed`)) {
|
|
943
|
+
const commitMessage = `${new Date(newBackupTimestamp).toLocaleDateString()} ${new Date(
|
|
944
|
+
newBackupTimestamp,
|
|
945
|
+
).toLocaleTimeString()}`;
|
|
946
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'commit', message: commitMessage });
|
|
947
|
+
Underpost.repo.manageBackupRepo({ repoName, operation: 'push' });
|
|
948
|
+
processedRepos.add(`${repoName}-committed`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
logger.info('Database operation completed successfully');
|
|
953
|
+
} catch (error) {
|
|
954
|
+
logger.error('Database operation failed', { error: error.message });
|
|
955
|
+
throw error;
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
/**
|
|
959
|
+
* Helper: Removes old timestamp backup folders and keeps only the newest ones.
|
|
960
|
+
* @method _enforceBackupRetention
|
|
961
|
+
* @memberof UnderpostDB
|
|
962
|
+
* @param {string} backupDir - Path to host-folder backup directory.
|
|
963
|
+
* @param {number} [maxRetention=MAX_BACKUP_RETENTION] - Maximum folders to keep.
|
|
964
|
+
* @return {number} Number of removed backup folders.
|
|
965
|
+
*/
|
|
966
|
+
_enforceBackupRetention(backupDir, maxRetention = MAX_BACKUP_RETENTION) {
|
|
967
|
+
try {
|
|
968
|
+
if (!fs.existsSync(backupDir)) return 0;
|
|
969
|
+
|
|
970
|
+
const timestamps = fs
|
|
971
|
+
.readdirSync(backupDir)
|
|
972
|
+
.filter((entry) => /^\d+$/.test(entry))
|
|
973
|
+
.sort((a, b) => parseInt(b, 10) - parseInt(a, 10));
|
|
974
|
+
|
|
975
|
+
if (timestamps.length <= maxRetention) return 0;
|
|
976
|
+
|
|
977
|
+
const staleTimestamps = timestamps.slice(maxRetention);
|
|
978
|
+
staleTimestamps.forEach((timestamp) => {
|
|
979
|
+
fs.removeSync(`${backupDir}/${timestamp}`);
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
logger.info('Pruned old backup timestamp folders', {
|
|
983
|
+
backupDir,
|
|
984
|
+
kept: maxRetention,
|
|
985
|
+
removed: staleTimestamps.length,
|
|
986
|
+
removedTimestamps: staleTimestamps,
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
return staleTimestamps.length;
|
|
990
|
+
} catch (error) {
|
|
991
|
+
logger.error('Failed to enforce backup retention', { backupDir, maxRetention, error: error.message });
|
|
992
|
+
return 0;
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Creates cluster metadata for the specified deployment.
|
|
998
|
+
* Loads database configuration and initializes cluster metadata including
|
|
999
|
+
* instances and cron jobs. This method populates the database with deployment information.
|
|
1000
|
+
* @method clusterMetadataFactory
|
|
1001
|
+
* @memberof UnderpostDB
|
|
1002
|
+
* @param {string} [deployId=process.env.DEFAULT_DEPLOY_ID] - The deployment ID.
|
|
1003
|
+
* @param {string} [host=process.env.DEFAULT_DEPLOY_HOST] - The host identifier.
|
|
1004
|
+
* @param {string} [path=process.env.DEFAULT_DEPLOY_PATH] - The path identifier.
|
|
1005
|
+
* @param {object} [options] - Options.
|
|
1006
|
+
* @param {boolean} [options.dev=false] - Development mode flag.
|
|
1007
|
+
* @return {Promise<void>} Resolves when metadata creation is complete.
|
|
1008
|
+
* @throws {Error} If database configuration is invalid or connection fails.
|
|
1009
|
+
*/
|
|
1010
|
+
async clusterMetadataFactory(
|
|
1011
|
+
deployId = process.env.DEFAULT_DEPLOY_ID,
|
|
1012
|
+
host = process.env.DEFAULT_DEPLOY_HOST,
|
|
1013
|
+
path = process.env.DEFAULT_DEPLOY_PATH,
|
|
1014
|
+
options = { dev: false },
|
|
1015
|
+
) {
|
|
1016
|
+
try {
|
|
1017
|
+
loadCronDeployEnv();
|
|
1018
|
+
deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
|
|
1019
|
+
host = host ? host : process.env.DEFAULT_DEPLOY_HOST;
|
|
1020
|
+
path = path ? path : process.env.DEFAULT_DEPLOY_PATH;
|
|
1021
|
+
|
|
1022
|
+
logger.info('Creating cluster metadata', { deployId, host, path });
|
|
1023
|
+
|
|
1024
|
+
const env = 'production';
|
|
1025
|
+
const deployListPath = './engine-private/deploy/dd.router';
|
|
1026
|
+
|
|
1027
|
+
if (!fs.existsSync(deployListPath)) {
|
|
1028
|
+
logger.error('Deploy router file not found', { path: deployListPath });
|
|
1029
|
+
throw new Error(`Deploy router file not found: ${deployListPath}`);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const deployList = fs.readFileSync(deployListPath, 'utf8').split(',');
|
|
1033
|
+
|
|
1034
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
1035
|
+
if (!fs.existsSync(confServerPath)) {
|
|
1036
|
+
logger.error('Server configuration not found', { path: confServerPath });
|
|
1037
|
+
throw new Error(`Server configuration not found: ${confServerPath}`);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
const { db } = loadConfServerJson(confServerPath, { resolve: true })[host][path];
|
|
1041
|
+
|
|
1042
|
+
const maxRetries = 5;
|
|
1043
|
+
const retryDelay = 3000;
|
|
1044
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1045
|
+
try {
|
|
1046
|
+
await DataBaseProviderService.load({ apis: ['instance', 'cron'], host, path, db });
|
|
1047
|
+
break;
|
|
1048
|
+
} catch (err) {
|
|
1049
|
+
if (attempt === maxRetries) {
|
|
1050
|
+
logger.error('Failed to connect to database after retries', { attempts: maxRetries, error: err.message });
|
|
1051
|
+
throw err;
|
|
1052
|
+
}
|
|
1053
|
+
logger.warn('Database connection failed, retrying...', { attempt, maxRetries, error: err.message });
|
|
1054
|
+
await timer(retryDelay);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
try {
|
|
1059
|
+
/** @type {import('../api/instance/instance.model.js').InstanceModel} */
|
|
1060
|
+
const Instance = DataBaseProviderService.getModel('instance', { host, path });
|
|
1061
|
+
|
|
1062
|
+
await Instance.deleteMany();
|
|
1063
|
+
logger.info('Cleared existing instance metadata');
|
|
1064
|
+
|
|
1065
|
+
for (const _deployId of deployList) {
|
|
1066
|
+
const deployId = _deployId.trim();
|
|
1067
|
+
if (!deployId) continue;
|
|
1068
|
+
|
|
1069
|
+
logger.info('Processing deployment for metadata', { deployId });
|
|
1070
|
+
|
|
1071
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
1072
|
+
if (!fs.existsSync(confServerPath)) {
|
|
1073
|
+
logger.warn('Configuration not found for deployment', { deployId, path: confServerPath });
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const confServer = loadReplicas(deployId, loadConfServerJson(confServerPath, { resolve: true }));
|
|
1078
|
+
const router = await Underpost.deploy.routerFactory(deployId, env);
|
|
1079
|
+
const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
|
|
1080
|
+
|
|
1081
|
+
for (const host of Object.keys(confServer)) {
|
|
1082
|
+
for (const { path, port } of pathPortAssignmentData[host]) {
|
|
1083
|
+
if (!confServer[host][path]) continue;
|
|
1084
|
+
|
|
1085
|
+
const { client, runtime, apis, peer } = confServer[host][path];
|
|
1086
|
+
|
|
1087
|
+
// Save main instance
|
|
1088
|
+
{
|
|
1089
|
+
const body = {
|
|
1090
|
+
deployId,
|
|
1091
|
+
host,
|
|
1092
|
+
path,
|
|
1093
|
+
port,
|
|
1094
|
+
client,
|
|
1095
|
+
runtime,
|
|
1096
|
+
apis,
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
logger.info('Saving instance metadata', body);
|
|
1100
|
+
await new Instance(body).save();
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// Save peer instance if exists
|
|
1104
|
+
if (peer) {
|
|
1105
|
+
const body = {
|
|
1106
|
+
deployId,
|
|
1107
|
+
host,
|
|
1108
|
+
path: path === '/' ? '/peer' : `${path}/peer`,
|
|
1109
|
+
port: port + 1,
|
|
1110
|
+
runtime: 'nodejs',
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
logger.info('Saving peer instance metadata', body);
|
|
1114
|
+
await new Instance(body).save();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Process additional instances
|
|
1120
|
+
const confInstancesPath = `./engine-private/conf/${deployId}/conf.instances.json`;
|
|
1121
|
+
if (fs.existsSync(confInstancesPath)) {
|
|
1122
|
+
// Expands multiInstance variants so each deployed instance is
|
|
1123
|
+
// registered (handles both the bare-array and { instances } shapes).
|
|
1124
|
+
const confInstances = loadConfInstances(deployId);
|
|
1125
|
+
for (const instance of confInstances) {
|
|
1126
|
+
const { id, host, path, fromPort, metadata } = instance;
|
|
1127
|
+
const { runtime } = metadata;
|
|
1128
|
+
const body = {
|
|
1129
|
+
deployId,
|
|
1130
|
+
host,
|
|
1131
|
+
path,
|
|
1132
|
+
port: fromPort,
|
|
1133
|
+
client: id,
|
|
1134
|
+
runtime,
|
|
1135
|
+
};
|
|
1136
|
+
logger.info('Saving additional instance metadata', body);
|
|
1137
|
+
await new Instance(body).save();
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
logger.error('Failed to create instance metadata', { error: error.message });
|
|
1143
|
+
throw error;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
try {
|
|
1147
|
+
const cronDeployPath = './engine-private/deploy/dd.cron';
|
|
1148
|
+
if (!fs.existsSync(cronDeployPath)) {
|
|
1149
|
+
logger.warn('Cron deploy file not found', { path: cronDeployPath });
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
const cronDeployId = fs.readFileSync(cronDeployPath, 'utf8').trim();
|
|
1154
|
+
const confCronPath = `./engine-private/conf/${cronDeployId}/conf.cron.json`;
|
|
1155
|
+
|
|
1156
|
+
if (!fs.existsSync(confCronPath)) {
|
|
1157
|
+
logger.warn('Cron configuration not found', { path: confCronPath });
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
const confCron = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
1162
|
+
|
|
1163
|
+
await DataBaseProviderService.load({ apis: ['cron'], host, path, db });
|
|
1164
|
+
|
|
1165
|
+
/** @type {import('../api/cron/cron.model.js').CronModel} */
|
|
1166
|
+
const Cron = DataBaseProviderService.getModel('cron', { host, path });
|
|
1167
|
+
|
|
1168
|
+
await Cron.deleteMany();
|
|
1169
|
+
logger.info('Cleared existing cron metadata');
|
|
1170
|
+
|
|
1171
|
+
for (const jobId of Object.keys(confCron.jobs)) {
|
|
1172
|
+
const body = {
|
|
1173
|
+
jobId,
|
|
1174
|
+
deployId: Underpost.cron.getRelatedDeployIdList(jobId),
|
|
1175
|
+
expression: confCron.jobs[jobId].expression,
|
|
1176
|
+
enabled: confCron.jobs[jobId].enabled,
|
|
1177
|
+
};
|
|
1178
|
+
logger.info('Saving cron metadata', body);
|
|
1179
|
+
await new Cron(body).save();
|
|
1180
|
+
}
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
logger.error('Failed to create cron metadata', { error: error.message });
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
1186
|
+
logger.info('Cluster metadata creation completed');
|
|
1187
|
+
} catch (error) {
|
|
1188
|
+
logger.error('Cluster metadata creation failed', { error: error.message });
|
|
1189
|
+
throw error;
|
|
1190
|
+
}
|
|
1191
|
+
},
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Cleans orphaned File references from database collections.
|
|
1195
|
+
* Iterates over all deploy-ids and checks if File documents are actually referenced
|
|
1196
|
+
* by other collections. Removes File documents that are not referenced anywhere.
|
|
1197
|
+
* @method cleanFsCollection
|
|
1198
|
+
* @memberof UnderpostDB
|
|
1199
|
+
* @param {string} [deployList='dd'] - Comma-separated list of deployment IDs.
|
|
1200
|
+
* @param {Object} [options={}] - Clean operation options.
|
|
1201
|
+
* @param {string} [options.hosts=''] - Comma-separated list of hosts to filter.
|
|
1202
|
+
* @param {string} [options.paths=''] - Comma-separated list of paths to filter.
|
|
1203
|
+
* @param {boolean} [options.dryRun=false] - If true, only reports what would be deleted.
|
|
1204
|
+
* @param {boolean} [options.dev=false] - Development mode flag.
|
|
1205
|
+
* @return {Promise<void>} Resolves when clean operation is complete.
|
|
1206
|
+
*/
|
|
1207
|
+
async cleanFsCollection(
|
|
1208
|
+
deployList = 'dd',
|
|
1209
|
+
options = {
|
|
1210
|
+
hosts: '',
|
|
1211
|
+
paths: '',
|
|
1212
|
+
dryRun: false,
|
|
1213
|
+
dev: false,
|
|
1214
|
+
},
|
|
1215
|
+
) {
|
|
1216
|
+
const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
|
|
1217
|
+
try {
|
|
1218
|
+
loadCronDeployEnv();
|
|
1219
|
+
if (deployList === 'dd') deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
1220
|
+
|
|
1221
|
+
logger.info('Starting File collection cleanup', { deployList, options });
|
|
1222
|
+
|
|
1223
|
+
// Load file.ref.json to know which models reference File
|
|
1224
|
+
const fileRefPath = './src/api/file/file.ref.json';
|
|
1225
|
+
if (!fs.existsSync(fileRefPath)) {
|
|
1226
|
+
logger.error('file.ref.json not found', { path: fileRefPath });
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const fileRefData = JSON.parse(fs.readFileSync(fileRefPath, 'utf8'));
|
|
1231
|
+
logger.info('Loaded file reference configuration', { apis: fileRefData.length });
|
|
1232
|
+
|
|
1233
|
+
// Filter hosts and paths if specified
|
|
1234
|
+
const filterHosts = options.hosts ? options.hosts.split(',').map((h) => h.trim()) : [];
|
|
1235
|
+
const filterPaths = options.paths ? options.paths.split(',').map((p) => p.trim()) : [];
|
|
1236
|
+
|
|
1237
|
+
// Track all connections to close them at the end
|
|
1238
|
+
const connectionsToClose = [];
|
|
1239
|
+
|
|
1240
|
+
for (const _deployId of deployList.split(',')) {
|
|
1241
|
+
const deployId = _deployId.trim();
|
|
1242
|
+
if (!deployId) continue;
|
|
1243
|
+
|
|
1244
|
+
logger.info('Processing deployment for File cleanup', { deployId });
|
|
1245
|
+
|
|
1246
|
+
// Load server configuration
|
|
1247
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
1248
|
+
if (!fs.existsSync(confServerPath)) {
|
|
1249
|
+
logger.error('Configuration file not found', { path: confServerPath });
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
1254
|
+
|
|
1255
|
+
// Process each host+path combination
|
|
1256
|
+
for (const host of Object.keys(confServer)) {
|
|
1257
|
+
if (filterHosts.length > 0 && !filterHosts.includes(host)) continue;
|
|
1258
|
+
|
|
1259
|
+
for (const path of Object.keys(confServer[host])) {
|
|
1260
|
+
if (filterPaths.length > 0 && !filterPaths.includes(path)) continue;
|
|
1261
|
+
|
|
1262
|
+
const { db, apis } = confServer[host][path];
|
|
1263
|
+
if (!db || !apis) continue;
|
|
1264
|
+
|
|
1265
|
+
// Check if 'file' api is in the apis list
|
|
1266
|
+
if (!apis.includes('file')) {
|
|
1267
|
+
logger.info('Skipping - no file api in configuration', { host, path });
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// logger.info('Processing host+path with file api', { host, path, db: db.name });
|
|
1272
|
+
|
|
1273
|
+
try {
|
|
1274
|
+
// Connect to database with retry
|
|
1275
|
+
let dbProvider;
|
|
1276
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
1277
|
+
try {
|
|
1278
|
+
dbProvider = await DataBaseProviderService.load({ apis, host, path, db });
|
|
1279
|
+
break;
|
|
1280
|
+
} catch (err) {
|
|
1281
|
+
if (attempt === 3) throw err;
|
|
1282
|
+
logger.warn('Database connection failed, retrying...', { attempt, host, path, error: err.message });
|
|
1283
|
+
await timer(3000);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (!dbProvider || !dbProvider.models) {
|
|
1287
|
+
logger.error('Failed to load database provider', { host, path });
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const { models } = dbProvider;
|
|
1292
|
+
|
|
1293
|
+
// Track this connection for cleanup
|
|
1294
|
+
connectionsToClose.push({ host, path, dbProvider });
|
|
1295
|
+
|
|
1296
|
+
// Check if File model exists
|
|
1297
|
+
if (!models.File) {
|
|
1298
|
+
logger.warn('File model not loaded', { host, path });
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// Get all File documents
|
|
1303
|
+
const allFiles = await models.File.find({}, '_id').lean();
|
|
1304
|
+
logger.info('Found File documents', { count: allFiles.length, host, path });
|
|
1305
|
+
|
|
1306
|
+
if (allFiles.length === 0) continue;
|
|
1307
|
+
|
|
1308
|
+
// Track which File IDs are referenced
|
|
1309
|
+
const referencedFileIds = new Set();
|
|
1310
|
+
|
|
1311
|
+
// Check each API from file.ref.json
|
|
1312
|
+
for (const refConfig of fileRefData) {
|
|
1313
|
+
const { api, model: modelFields } = refConfig;
|
|
1314
|
+
|
|
1315
|
+
// Check if this API is loaded in current context
|
|
1316
|
+
const modelName = api
|
|
1317
|
+
.split('-')
|
|
1318
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
1319
|
+
.join('');
|
|
1320
|
+
const Model = models[modelName];
|
|
1321
|
+
|
|
1322
|
+
if (!Model) {
|
|
1323
|
+
logger.debug('Model not loaded in current context', { api, modelName, host, path });
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
logger.info('Checking references in model', { api, modelName });
|
|
1328
|
+
|
|
1329
|
+
// Helper function to recursively check field references
|
|
1330
|
+
const checkFieldReferences = async (fieldPath, fieldConfig) => {
|
|
1331
|
+
for (const [fieldName, fieldValue] of Object.entries(fieldConfig)) {
|
|
1332
|
+
const currentPath = fieldPath ? `${fieldPath}.${fieldName}` : fieldName;
|
|
1333
|
+
|
|
1334
|
+
if (fieldValue === true) {
|
|
1335
|
+
// This is a File reference field
|
|
1336
|
+
const query = {};
|
|
1337
|
+
query[currentPath] = { $exists: true, $ne: null };
|
|
1338
|
+
|
|
1339
|
+
const docs = await Model.find(query, currentPath).lean();
|
|
1340
|
+
|
|
1341
|
+
for (const doc of docs) {
|
|
1342
|
+
// Navigate to the nested field
|
|
1343
|
+
const parts = currentPath.split('.');
|
|
1344
|
+
let value = doc;
|
|
1345
|
+
for (const part of parts) {
|
|
1346
|
+
value = value?.[part];
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if (value) {
|
|
1350
|
+
if (Array.isArray(value)) {
|
|
1351
|
+
value.forEach((id) => id && referencedFileIds.add(id.toString()));
|
|
1352
|
+
} else {
|
|
1353
|
+
referencedFileIds.add(value.toString());
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
logger.info('Found references', {
|
|
1359
|
+
model: modelName,
|
|
1360
|
+
field: currentPath,
|
|
1361
|
+
count: docs.length,
|
|
1362
|
+
});
|
|
1363
|
+
} else if (typeof fieldValue === 'object') {
|
|
1364
|
+
// Nested object, recurse
|
|
1365
|
+
await checkFieldReferences(currentPath, fieldValue);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
await checkFieldReferences('', modelFields);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
logger.info('Total referenced File IDs', { count: referencedFileIds.size, host, path });
|
|
1374
|
+
|
|
1375
|
+
// Find orphaned files
|
|
1376
|
+
const orphanedFiles = allFiles.filter((file) => !referencedFileIds.has(file._id.toString()));
|
|
1377
|
+
|
|
1378
|
+
if (orphanedFiles.length === 0) {
|
|
1379
|
+
logger.info('No orphaned files found', { host, path });
|
|
1380
|
+
} else {
|
|
1381
|
+
logger.info('Found orphaned files', { count: orphanedFiles.length, host, path });
|
|
1382
|
+
|
|
1383
|
+
if (options.dryRun) {
|
|
1384
|
+
logger.info('Dry run - would delete files', {
|
|
1385
|
+
count: orphanedFiles.length,
|
|
1386
|
+
ids: orphanedFiles.map((f) => f._id.toString()),
|
|
1387
|
+
});
|
|
1388
|
+
} else {
|
|
1389
|
+
const orphanedIds = orphanedFiles.map((f) => f._id);
|
|
1390
|
+
const deleteResult = await models.File.deleteMany({ _id: { $in: orphanedIds } });
|
|
1391
|
+
logger.info('Deleted orphaned files', {
|
|
1392
|
+
deletedCount: deleteResult.deletedCount,
|
|
1393
|
+
host,
|
|
1394
|
+
path,
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
logger.error('Error processing host+path', {
|
|
1400
|
+
host,
|
|
1401
|
+
path,
|
|
1402
|
+
error: error.message,
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// Close all connections
|
|
1410
|
+
logger.info('Closing all database connections', { count: connectionsToClose.length });
|
|
1411
|
+
for (const { host, path, dbProvider } of connectionsToClose) {
|
|
1412
|
+
try {
|
|
1413
|
+
if (dbProvider && dbProvider.close) {
|
|
1414
|
+
await dbProvider.close();
|
|
1415
|
+
logger.info('Connection closed', { host, path });
|
|
1416
|
+
}
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
logger.error('Error closing connection', { host, path, error: error.message });
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
logger.info('File collection cleanup completed');
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
logger.error('File collection cleanup failed', { error: error.message });
|
|
1425
|
+
throw error;
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* Handles backup of cluster metadata.
|
|
1431
|
+
* Orchestrates backup and restore operations for cluster metadata including
|
|
1432
|
+
* instances and cron jobs. Supports import/export and metadata generation.
|
|
1433
|
+
* @method clusterMetadataBackupCallback
|
|
1434
|
+
* @memberof UnderpostDB
|
|
1435
|
+
* @param {string} [deployId=process.env.DEFAULT_DEPLOY_ID] - The deployment ID.
|
|
1436
|
+
* @param {string} [host=process.env.DEFAULT_DEPLOY_HOST] - The host identifier.
|
|
1437
|
+
* @param {string} [path=process.env.DEFAULT_DEPLOY_PATH] - The path identifier.
|
|
1438
|
+
* @param {Object} [options={}] - Backup operation options.
|
|
1439
|
+
* @param {boolean} [options.generate=false] - Generate cluster metadata.
|
|
1440
|
+
* @param {boolean} [options.itc=false] - Execute in container context.
|
|
1441
|
+
* @param {boolean} [options.import=false] - Import metadata from backup.
|
|
1442
|
+
* @param {boolean} [options.export=false] - Export metadata to backup.
|
|
1443
|
+
* @param {boolean} [options.instances=false] - Process instances collection.
|
|
1444
|
+
* @param {boolean} [options.crons=false] - Process crons collection.
|
|
1445
|
+
* @param {boolean} [options.dev=false] - Development mode flag.
|
|
1446
|
+
* @return {Promise<void>} Resolves when backup operation is complete.
|
|
1447
|
+
*/
|
|
1448
|
+
async clusterMetadataBackupCallback(
|
|
1449
|
+
deployId = process.env.DEFAULT_DEPLOY_ID,
|
|
1450
|
+
host = process.env.DEFAULT_DEPLOY_HOST,
|
|
1451
|
+
path = process.env.DEFAULT_DEPLOY_PATH,
|
|
1452
|
+
options = {
|
|
1453
|
+
generate: false,
|
|
1454
|
+
itc: false,
|
|
1455
|
+
import: false,
|
|
1456
|
+
export: false,
|
|
1457
|
+
instances: false,
|
|
1458
|
+
crons: false,
|
|
1459
|
+
dev: false,
|
|
1460
|
+
},
|
|
1461
|
+
) {
|
|
1462
|
+
try {
|
|
1463
|
+
loadCronDeployEnv();
|
|
1464
|
+
deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
|
|
1465
|
+
host = host ? host : process.env.DEFAULT_DEPLOY_HOST;
|
|
1466
|
+
path = path ? path : process.env.DEFAULT_DEPLOY_PATH;
|
|
1467
|
+
|
|
1468
|
+
logger.info('Starting cluster metadata backup operation', {
|
|
1469
|
+
deployId,
|
|
1470
|
+
host,
|
|
1471
|
+
path,
|
|
1472
|
+
options,
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
if (options.generate === true) {
|
|
1476
|
+
logger.info('Generating cluster metadata');
|
|
1477
|
+
await Underpost.db.clusterMetadataFactory(deployId, host, path);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
if (options.instances === true) {
|
|
1481
|
+
const outputPath = './engine-private/instances';
|
|
1482
|
+
if (!fs.existsSync(outputPath)) {
|
|
1483
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
1484
|
+
}
|
|
1485
|
+
const collection = 'instances';
|
|
1486
|
+
|
|
1487
|
+
if (options.export === true) {
|
|
1488
|
+
logger.info('Exporting instances collection', { outputPath });
|
|
1489
|
+
shellExec(
|
|
1490
|
+
`node bin db --export --primary-pod --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
if (options.import === true) {
|
|
1495
|
+
logger.info('Importing instances collection', { outputPath });
|
|
1496
|
+
shellExec(
|
|
1497
|
+
`node bin db --import --primary-pod --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
if (options.crons === true) {
|
|
1503
|
+
const outputPath = './engine-private/crons';
|
|
1504
|
+
if (!fs.existsSync(outputPath)) {
|
|
1505
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
1506
|
+
}
|
|
1507
|
+
const collection = 'crons';
|
|
1508
|
+
|
|
1509
|
+
if (options.export === true) {
|
|
1510
|
+
logger.info('Exporting crons collection', { outputPath });
|
|
1511
|
+
shellExec(
|
|
1512
|
+
`node bin db --export --primary-pod --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
if (options.import === true) {
|
|
1517
|
+
logger.info('Importing crons collection', { outputPath });
|
|
1518
|
+
shellExec(
|
|
1519
|
+
`node bin db --import --primary-pod --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
logger.info('Cluster metadata backup operation completed');
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
logger.error('Cluster metadata backup operation failed', { error: error.message });
|
|
1527
|
+
throw error;
|
|
1528
|
+
}
|
|
1529
|
+
},
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
export default UnderpostDB;
|