@underpostnet/cyberia 3.2.90 → 3.3.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.env.example +16 -0
- package/.github/workflows/coverall.cyberia.ci.yml +107 -0
- package/.github/workflows/cyberia-client.cd.yml +6 -18
- package/.github/workflows/cyberia-server.cd.yml +6 -18
- package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
- package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
- package/.github/workflows/engine-cyberia.cd.yml +6 -78
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +42 -21
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +2 -9
- package/.prettierignore +1 -0
- package/AGENTS.md +47 -0
- package/CHANGELOG.md +581 -2417
- package/CLI-HELP.md +416 -127
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +4 -4
- package/README.md +21 -1
- package/bin/build.js +70 -43
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +372 -135
- package/bin/deploy.js +18 -104
- package/bin/index.js +372 -135
- package/compose.env +16 -0
- package/conf.js +93 -4
- package/deploy/cyberia-client/deploy.sh +47 -0
- package/deploy/cyberia-client/package.sh +18 -0
- package/deploy/cyberia-client/state.sh +14 -0
- package/deploy/cyberia-server/deploy.sh +47 -0
- package/deploy/cyberia-server/package.sh +18 -0
- package/deploy/cyberia-server/state.sh +14 -0
- package/deploy/dd-cyberia/deploy.sh +22 -0
- package/deploy/dd-cyberia/init.sh +55 -0
- package/deploy/dd-cyberia/package.sh +17 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +116 -0
- package/deploy/lib/github-actions-logging.sh +594 -0
- package/deploy/lib/host.sh +185 -0
- package/deploy/lib/state.sh +62 -0
- package/deploy/pwa-microservices-template/deploy.sh +45 -0
- package/deploy/release/deploy.sh +32 -0
- package/deployment.yaml +7 -2
- package/docker-compose.yml +31 -37
- package/examples/static-page/README.md +19 -19
- package/examples/static-page/ssr-components/CustomPage.js +2 -2
- package/hardhat/hardhat.config.js +1 -6
- package/hardhat/package-lock.json +104 -43
- package/hardhat/package.json +3 -3
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/grafana/deployment.yaml +26 -7
- package/manifests/grafana/kustomization.yaml +1 -0
- package/manifests/grafana/service.yaml +6 -1
- package/manifests/mariadb/pv.yaml +1 -0
- package/manifests/mongodb/statefulset.yaml +9 -2
- package/manifests/mysql/pv-pvc.yaml +14 -1
- package/manifests/postgresql/pv.yaml +12 -1
- package/manifests/postgresql/pvc.yaml +1 -0
- package/manifests/prometheus/deployment.yaml +18 -22
- package/package.json +83 -10
- package/scripts/audit-selinux.sh +64 -0
- package/scripts/event-monitor.sh +56 -0
- package/scripts/gpu-diag.sh +0 -0
- package/scripts/ip-info.sh +0 -0
- package/scripts/k3s-node-setup.sh +30 -14
- package/scripts/kubeadm-node-setup.sh +40 -31
- package/scripts/lxd-vm-setup.sh +0 -0
- package/scripts/maas-nat-firewalld.sh +0 -0
- package/scripts/maas-setup.sh +8 -8
- package/scripts/nat-iptables.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +0 -0
- package/scripts/rocky-kickstart.sh +25 -9
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
- package/src/api/core/core.controller.js +1 -1
- package/src/api/core/core.router.js +2 -2
- package/src/api/core/core.service.js +2 -2
- package/src/api/crypto/crypto.controller.js +1 -1
- package/src/api/crypto/crypto.service.js +1 -1
- package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
- package/src/api/cyberia-action/cyberia-action.router.js +1 -1
- package/src/api/cyberia-action/cyberia-action.service.js +2 -2
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
- package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
- package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
- package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
- package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
- package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
- package/src/api/cyberia-map/cyberia-map.router.js +1 -1
- package/src/api/cyberia-map/cyberia-map.service.js +2 -2
- package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
- package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
- package/src/api/default/default.controller.js +1 -1
- package/src/api/default/default.router.js +1 -1
- package/src/api/default/default.service.js +2 -2
- package/src/api/document/document.controller.js +1 -1
- package/src/api/document/document.router.js +1 -1
- package/src/api/document/document.service.js +2 -2
- package/src/api/file/file.controller.js +1 -1
- package/src/api/file/file.router.js +1 -1
- package/src/api/file/file.service.js +2 -2
- package/src/api/instance/instance.controller.js +1 -1
- package/src/api/instance/instance.router.js +1 -1
- package/src/api/instance/instance.service.js +2 -2
- package/src/api/ipfs/ipfs.controller.js +1 -1
- package/src/api/ipfs/ipfs.router.js +2 -2
- package/src/api/ipfs/ipfs.service.js +2 -2
- package/src/api/object-layer/object-layer.controller.js +1 -1
- package/src/api/object-layer/object-layer.model.js +204 -3
- package/src/api/object-layer/object-layer.router.js +2 -2
- package/src/api/object-layer/object-layer.service.js +4 -17
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
- package/src/api/test/test.controller.js +1 -1
- package/src/api/test/test.service.js +1 -1
- package/src/api/user/guest.service.js +3 -3
- package/src/api/user/user.controller.js +1 -1
- package/src/api/user/user.router.js +2 -2
- package/src/api/user/user.service.js +4 -4
- package/src/api.js +5 -9
- package/src/cli/app.js +391 -0
- package/src/cli/baremetal.js +30 -36
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +242 -135
- package/src/cli/db.js +126 -67
- package/src/cli/deploy.js +158 -60
- package/src/cli/docker-compose.js +5 -5
- package/src/cli/domains.js +184 -0
- package/src/cli/dotenv-store.js +143 -0
- package/src/cli/event.js +2281 -0
- package/src/cli/fs.js +3 -3
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -7
- package/src/cli/index.js +426 -134
- package/src/cli/ipfs.js +34 -30
- package/src/cli/kickstart.js +1 -1
- package/src/cli/kubectl.js +218 -41
- package/src/cli/lxd.js +3 -3
- package/src/cli/monitor.js +1359 -26
- package/src/cli/package.js +90 -0
- package/src/cli/release.js +73 -34
- package/src/cli/repository.js +564 -367
- package/src/cli/run.js +362 -654
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +651 -331
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +26 -13
- package/src/cli/test.js +199 -125
- package/src/cli/vultr.js +623 -0
- package/src/cli/wireguard.js +2930 -0
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +258 -211
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/PanelForm.js +4 -4
- package/src/client/components/core/Repository.js +56 -0
- package/src/client/components/core/Translate.js +7 -0
- package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
- package/src/client/components/core/Wallet.js +0 -11
- package/src/client/components/core/Worker.js +7 -1
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppShellDefault.js +1 -1
- package/src/client/components/default/SettingsDefault.js +2 -2
- package/src/client/components/itemledger/SettingsItemledger.js +2 -2
- package/src/client/components/underpost/AppShellUnderpost.js +3 -2
- package/src/client/components/underpost/SettingsUnderpost.js +2 -2
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-384x384.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-512x512.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/default/assets/background/white.jpg +0 -0
- package/src/client/public/default/browserconfig.xml +1 -1
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon-48x48.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/manifest.webmanifest +5 -5
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +1 -1
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
- package/src/client/services/object-layer/object-layer.management.js +4 -4
- package/src/client/ssr/RootDocument.js +1 -1
- package/src/client/ssr/body/404.js +1 -1
- package/src/client/ssr/body/500.js +1 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
- package/src/client/ssr/head/CryptokoynScripts.js +1 -1
- package/src/client/ssr/head/Css.js +1 -1
- package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +1 -1
- package/src/client/ssr/head/ItemledgerScripts.js +1 -1
- package/src/client/ssr/head/Microdata.js +1 -1
- package/src/client/ssr/head/Production.js +1 -1
- package/src/client/ssr/head/Pwa.js +1 -1
- package/src/client/ssr/head/PwaItemledger.js +1 -1
- package/src/client/ssr/head/Seo.js +1 -1
- package/src/client/ssr/head/UnderpostScripts.js +1 -1
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
- package/src/client/ssr/views/Cyberia404.js +7 -5
- package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/views/Maintenance.js +1 -1
- package/src/client/ssr/views/NoNetworkConnection.js +1 -1
- package/src/client/ssr/views/Test.js +1 -1
- package/src/client/sw/core.sw.js +20 -14
- package/src/client-builder/client-build-docs.js +61 -46
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +58 -44
- package/src/client-builder/client-dev-server.js +3 -3
- package/src/client-builder/client-icons.js +2 -2
- package/src/client-builder/ssr.js +3 -3
- package/src/client.build.js +3 -6
- package/src/client.dev.js +3 -6
- package/src/db/DataBaseProvider.js +186 -3
- package/src/db/mariadb/MariaDB.js +3 -2
- package/src/db/mongo/MongoBootstrap.js +273 -101
- package/src/db/mongo/MongooseDB.js +11 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +1 -1
- package/src/index.js +103 -23
- package/src/mailer/EmailRender.js +2 -2
- package/src/mailer/MailerInterceptor.js +87 -0
- package/src/mailer/MailerProvider.js +50 -6
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
- package/src/projects/cyberia/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +54 -6
- package/src/projects/cyberia/gemini-client.js +1 -1
- package/src/projects/cyberia/generate-saga.js +15 -24
- package/src/projects/cyberia/hot-reload-trigger.js +1 -1
- package/src/projects/cyberia/instance-data.js +22 -3
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +1 -1
- package/src/projects/cyberia/object-layer.js +12 -22
- package/src/projects/cyberia/semantic-layer-generator.js +1 -1
- package/src/projects/underpost/catalog-underpost.js +13 -2
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +9 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -4
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
- package/src/runtime/engine-cyberia/compose.env +16 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
- package/src/runtime/express/Express.js +32 -27
- package/src/runtime/lampp/Lampp.js +22 -70
- package/src/runtime/nginx/Nginx.js +1 -1
- package/src/runtime/wp/Wp.js +16 -48
- package/src/server/{catalog.js → build/catalog.js} +15 -5
- package/src/server/build/coverage.js +117 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +465 -0
- package/src/server/build/testing.js +528 -0
- package/src/server/{dns.js → network/dns.js} +241 -14
- package/src/server/network/forward-proxy.js +541 -0
- package/src/server/{middlewares.js → network/middlewares.js} +59 -4
- package/src/server/network/node-capability.js +98 -0
- package/src/server/{peer.js → network/peer.js} +3 -3
- package/src/server/{proxy.js → network/proxy.js} +7 -13
- package/src/server/network/router.js +356 -0
- package/src/server/{tls.js → network/tls.js} +2 -2
- package/src/server/network/underpost-compression.js +186 -0
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/ops/cron.js +949 -0
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +108 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/ops/systemd.js +259 -0
- package/src/server/{conf.js → runtime/conf.js} +521 -520
- package/src/server/runtime/config-scope.js +215 -0
- package/src/server/runtime/environment.js +149 -0
- package/src/server/{process.js → runtime/process.js} +153 -13
- package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
- package/src/server/{runtime.js → runtime/runtime.js} +12 -8
- package/src/server/{start.js → runtime/start.js} +128 -39
- package/src/server/{auth.js → security/auth.js} +7 -8
- package/src/server/security/container-storage.js +382 -0
- package/src/server/{crypto.js → security/crypto.js} +1 -1
- package/src/server/security/selinux.js +185 -0
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +1 -1
- package/src/server/{downloader.js → storage/downloader.js} +2 -2
- package/src/server/storage/repository.js +68 -0
- package/src/server.js +4 -7
- package/src/ws/IoInterface.js +1 -1
- package/src/ws/IoServer.js +1 -1
- package/src/ws/core/core.ws.connection.js +1 -1
- package/src/ws/core/core.ws.emit.js +1 -1
- package/src/ws/core/core.ws.server.js +1 -1
- package/src/ws/default/default.ws.connection.js +1 -1
- package/src/ws/default/default.ws.emit.js +1 -1
- package/src/ws/default/default.ws.server.js +1 -1
- package/test/e2e/event-e2e-public-ingress-down.js +45 -0
- package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
- package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
- package/test/{api.test.js → integration/app/api.test.js} +33 -14
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
- package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
- package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
- package/test/integration/infra/1-security/config-scope.test.js +305 -0
- package/test/integration/infra/1-security/container-storage.test.js +356 -0
- package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
- package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
- package/test/integration/infra/1-security/selinux.test.js +69 -0
- package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
- package/test/integration/infra/1-security/systemd-service.test.js +70 -0
- package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
- package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
- package/test/integration/infra/2-network/node-capability.test.js +182 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
- package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
- package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
- package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
- package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
- package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
- package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
- package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
- package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
- package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
- package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
- package/test/integration/infra/5-observability/event-notification.test.js +197 -0
- package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
- package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
- package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
- package/test/support/shell-harness.js +73 -0
- package/test/unit/build-template.test.js +97 -0
- package/test/unit/build-workflow-offline.test.js +132 -0
- package/test/unit/catalog.test.js +71 -0
- package/test/unit/client-build-docs.test.js +334 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +979 -0
- package/test/unit/coverage-artifact.test.js +130 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/deploy-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +447 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/logger-redaction.test.js +112 -0
- package/test/unit/package.test.js +403 -0
- package/test/unit/prepare-host.test.js +299 -0
- package/test/unit/process-environment.test.js +18 -0
- package/test/unit/propagation-message.test.js +78 -0
- package/test/unit/redeploy-plan.test.js +24 -0
- package/test/unit/release-bump.test.js +474 -0
- package/test/unit/ssh-output-redaction.test.js +28 -0
- package/test/unit/start-options.test.js +46 -0
- package/test/unit/test-tiers.test.js +193 -0
- package/vitest.config.js +58 -0
- package/.nycrc +0 -9
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/user/user.build.js +0 -16
- package/src/cli/env.js +0 -177
- package/src/client/components/core/Blockchain.js +0 -41
- package/src/client/components/core/Blog.js +0 -9
- package/src/client/components/core/KeyboardAvoidance.js +0 -145
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/white0-min.jpg +0 -0
- package/src/client/public/default/assets/background/white0.jpg +0 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +0 -148
- package/src/server/cron.js +0 -476
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host filesystem preparation for Kubernetes storage that unprivileged containers mount.
|
|
3
|
+
*
|
|
4
|
+
* Every node-local path a pod reaches through a `hostPath` PersistentVolume — or through a
|
|
5
|
+
* dynamic provisioner that is itself backed by a node directory — must carry a type the
|
|
6
|
+
* `container_t` domain can use. The base RHEL/Rocky policy has no mapping for the trees this
|
|
7
|
+
* platform writes into, so a directory created there falls back to the policy default
|
|
8
|
+
* (`default_t` under `/data`, `usr_t` under `/opt`, `user_home_t` under the operator's home) and
|
|
9
|
+
* every read and write from a pod becomes an AVC denial.
|
|
10
|
+
*
|
|
11
|
+
* The mapping is registered with `semanage fcontext` rather than applied with `chcon` so it
|
|
12
|
+
* survives a relabel, a policy update and `/.autorelabel`; the paths are then restored. Both
|
|
13
|
+
* operations are idempotent, which is what lets deploy, redeploy and recovery flows all call
|
|
14
|
+
* this without special-casing.
|
|
15
|
+
*
|
|
16
|
+
* @module src/server/security/container-storage.js
|
|
17
|
+
* @namespace ContainerStorageService
|
|
18
|
+
*/
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
import SELinuxService, { runSELinuxCommands, shellArgumentFactory } from './selinux.js';
|
|
22
|
+
import { HOST_VOLUME_ROOT } from '../runtime/environment.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Host storage preparation and SELinux labeling for container-mounted paths.
|
|
26
|
+
* @class ContainerStorageService
|
|
27
|
+
* @memberof ContainerStorageService
|
|
28
|
+
*/
|
|
29
|
+
class ContainerStorageService {
|
|
30
|
+
/** Label every unprivileged container domain can read and write, at the empty category set. */
|
|
31
|
+
static SHARED_CONTAINER_TYPE = SELinuxService.SHARED_CONTAINER_TYPE;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Mode used when this module is the one creating the directory. Existing directories keep the
|
|
35
|
+
* mode, ownership and ACLs they already have — the workload's own init container owns that,
|
|
36
|
+
* and resetting it would rewrite live data trees.
|
|
37
|
+
*/
|
|
38
|
+
static DEFAULT_MODE = '0755';
|
|
39
|
+
|
|
40
|
+
/** Upstream rancher local-path-provisioner defaults, used only when discovery finds nothing. */
|
|
41
|
+
static LOCAL_PATH_DEFAULT_ROOT = '/opt/local-path-provisioner';
|
|
42
|
+
static LOCAL_PATH_CONFIG_MAP = 'local-path-config';
|
|
43
|
+
static LOCAL_PATH_NAMESPACES = ['local-path-storage', 'kube-system'];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Node directories this platform owns and hands to containers. `/data` covers the MongoDB
|
|
47
|
+
* replica volumes, MariaDB, MySQL and PostgreSQL trees; `HOST_VOLUME_ROOT` covers every
|
|
48
|
+
* `hostPath` PersistentVolume the deploy flow materializes, including the gateway's static tree.
|
|
49
|
+
*/
|
|
50
|
+
static PLATFORM_STORAGE_ROOTS = ['/data', HOST_VOLUME_ROOT];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Kubernetes control-plane state bind-mounted into containers by kubeadm.
|
|
54
|
+
*/
|
|
55
|
+
static CONTROL_PLANE_MOUNT_PATHS = ['/etc/kubernetes', '/var/lib/etcd', '/var/lib/calico'];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Directories that are a system location in their own right. A storage tree may live *under*
|
|
59
|
+
* one of these (`/opt/local-path-provisioner`, `/var/lib/etcd`), but the directory itself never
|
|
60
|
+
* becomes container storage.
|
|
61
|
+
*/
|
|
62
|
+
static PROTECTED_EXACT_PATHS = [
|
|
63
|
+
'/',
|
|
64
|
+
'/boot',
|
|
65
|
+
'/dev',
|
|
66
|
+
'/etc',
|
|
67
|
+
'/home',
|
|
68
|
+
'/mnt',
|
|
69
|
+
'/opt',
|
|
70
|
+
'/proc',
|
|
71
|
+
'/root',
|
|
72
|
+
'/run',
|
|
73
|
+
'/srv',
|
|
74
|
+
'/sys',
|
|
75
|
+
'/tmp',
|
|
76
|
+
'/usr',
|
|
77
|
+
'/var',
|
|
78
|
+
'/var/lib',
|
|
79
|
+
'/var/log',
|
|
80
|
+
'/var/run',
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Trees no container storage may live in at all.
|
|
85
|
+
*
|
|
86
|
+
* A host directory being mounted into a pod is not evidence that it holds workload data: the
|
|
87
|
+
* CNI plugin directory, the runtime socket and `/proc` are all mounted into pods, and each has
|
|
88
|
+
* a policy type that exists for a reason. Relabeling `/opt/cni/bin` would strip `bin_t` from
|
|
89
|
+
* the calico executables the kubelet runs, which is a worse outcome than the denial it set out
|
|
90
|
+
* to fix.
|
|
91
|
+
*/
|
|
92
|
+
static PROTECTED_PATH_PREFIXES = [
|
|
93
|
+
'/bin',
|
|
94
|
+
'/boot',
|
|
95
|
+
'/dev',
|
|
96
|
+
'/etc',
|
|
97
|
+
'/lib',
|
|
98
|
+
'/lib64',
|
|
99
|
+
'/opt/cni',
|
|
100
|
+
'/proc',
|
|
101
|
+
'/run',
|
|
102
|
+
'/sbin',
|
|
103
|
+
'/sys',
|
|
104
|
+
'/usr',
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The one set of paths allowed to break {@link PROTECTED_PATH_PREFIXES}: kubeadm bind-mounts
|
|
109
|
+
* these into its control-plane containers, so they genuinely are container-visible state. They
|
|
110
|
+
* are a hardcoded constant, never something discovery or an operator flag can widen.
|
|
111
|
+
*/
|
|
112
|
+
static PROTECTED_PATH_EXCEPTIONS = ContainerStorageService.CONTROL_PLANE_MOUNT_PATHS;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Reports whether a path may be given the shared container label.
|
|
116
|
+
* @param {string} path - Absolute path.
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
static isSafeStoragePath(path) {
|
|
120
|
+
if (typeof path !== 'string' || !path.startsWith('/')) return false;
|
|
121
|
+
const normalized = path.replace(/\/+$/, '') || '/';
|
|
122
|
+
if (ContainerStorageService.PROTECTED_PATH_EXCEPTIONS.includes(normalized)) return true;
|
|
123
|
+
if (ContainerStorageService.PROTECTED_EXACT_PATHS.includes(normalized)) return false;
|
|
124
|
+
return !ContainerStorageService.PROTECTED_PATH_PREFIXES.some(
|
|
125
|
+
(prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Throws unless every path may be labeled. Applied at the command-building boundary so no
|
|
131
|
+
* caller — a discovery result, a CLI `extraPaths`, a future one — can route around it.
|
|
132
|
+
* @param {string[]} paths - Absolute paths.
|
|
133
|
+
* @returns {string[]} The same paths.
|
|
134
|
+
*/
|
|
135
|
+
static assertSafeStoragePaths(paths = []) {
|
|
136
|
+
const refused = paths.filter((path) => !ContainerStorageService.isSafeStoragePath(path));
|
|
137
|
+
if (refused.length > 0)
|
|
138
|
+
throw new RangeError(
|
|
139
|
+
`refusing to apply the container storage label to a system path: ${refused.join(', ')}. ` +
|
|
140
|
+
`A host directory mounted into a pod is not necessarily workload data; give the volume ` +
|
|
141
|
+
`its own directory under a declared storage root instead.`,
|
|
142
|
+
);
|
|
143
|
+
return paths;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Deduplicates and sorts a path list, keeping only absolute entries.
|
|
148
|
+
* @param {string[]} [paths] - Candidate paths.
|
|
149
|
+
* @returns {string[]} Absolute paths, parents before their descendants.
|
|
150
|
+
*/
|
|
151
|
+
static absoluteStoragePaths(paths = []) {
|
|
152
|
+
return [
|
|
153
|
+
...new Set(
|
|
154
|
+
paths
|
|
155
|
+
.filter((path) => typeof path === 'string')
|
|
156
|
+
.map((path) => path.trim().replace(/\/+$/, ''))
|
|
157
|
+
.filter((path) => path.startsWith('/') && path !== '/'),
|
|
158
|
+
),
|
|
159
|
+
].sort();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Reduces a path list to the smallest set of roots that still covers every entry.
|
|
164
|
+
*
|
|
165
|
+
* A `semanage fcontext` mapping is the regular expression `<root>(/.*)?`, so a root already
|
|
166
|
+
* covers all of its descendants; keeping both `/data` and `/data/mongodb` would add a redundant
|
|
167
|
+
* rule and a second full `restorecon` pass over the same tree on every deploy.
|
|
168
|
+
* @param {string[]} [paths] - Candidate absolute paths.
|
|
169
|
+
* @returns {string[]} Non-overlapping, sorted roots.
|
|
170
|
+
*/
|
|
171
|
+
static normalizeStorageRoots(paths = []) {
|
|
172
|
+
const absolute = ContainerStorageService.absoluteStoragePaths(paths);
|
|
173
|
+
return absolute.filter(
|
|
174
|
+
(path) => !absolute.some((candidate) => candidate !== path && path.startsWith(`${candidate}/`)),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Extracts the node directories a local-path provisioner writes into, from its `config.json`.
|
|
180
|
+
*
|
|
181
|
+
* Read rather than assumed: the provisioner's root is operator-configurable, and every
|
|
182
|
+
* dynamically provisioned claim in this platform (Grafana, Prometheus, Alertmanager, IPFS,
|
|
183
|
+
* MariaDB) becomes a subdirectory of it. Labeling the wrong root leaves those claims with the
|
|
184
|
+
* `/opt` policy default (`usr_t`), which is exactly the shape of the Grafana denials.
|
|
185
|
+
* @param {string} [configJson] - Contents of the provisioner ConfigMap's `config.json` key.
|
|
186
|
+
* @returns {string[]} Configured node paths; empty when the config is absent or unparsable.
|
|
187
|
+
*/
|
|
188
|
+
static localPathRootsFactory(configJson = '') {
|
|
189
|
+
if (!configJson || !`${configJson}`.trim()) return [];
|
|
190
|
+
let config;
|
|
191
|
+
try {
|
|
192
|
+
config = JSON.parse(configJson);
|
|
193
|
+
} catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
const paths = [];
|
|
197
|
+
if (Array.isArray(config?.nodePathMap))
|
|
198
|
+
for (const entry of config.nodePathMap) if (Array.isArray(entry?.paths)) paths.push(...entry.paths);
|
|
199
|
+
if (typeof config?.sharedFileSystemPath === 'string') paths.push(config.sharedFileSystemPath);
|
|
200
|
+
return ContainerStorageService.normalizeStorageRoots(paths);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Builds the command that creates a storage directory only when it is missing.
|
|
205
|
+
*
|
|
206
|
+
* Guarded rather than unconditional: `install -d -m` on an existing tree would rewrite the
|
|
207
|
+
* mode a workload's init container set (MongoDB's `chown 999:999`, MariaDB's data dir), and
|
|
208
|
+
* this runs on every redeploy.
|
|
209
|
+
* @param {string} path - Absolute directory path.
|
|
210
|
+
* @param {{mode?: string, owner?: string, sudo?: boolean}} [options]
|
|
211
|
+
* @returns {string} Shell command.
|
|
212
|
+
*/
|
|
213
|
+
static storageDirectoryCommandFactory(
|
|
214
|
+
path,
|
|
215
|
+
{ mode = ContainerStorageService.DEFAULT_MODE, owner = '', sudo = true } = {},
|
|
216
|
+
) {
|
|
217
|
+
if (!path) throw new TypeError('storageDirectoryCommandFactory requires a path');
|
|
218
|
+
const prefix = sudo ? 'sudo ' : '';
|
|
219
|
+
const quoted = shellArgumentFactory(path);
|
|
220
|
+
const ownerFlag = owner
|
|
221
|
+
? ` -o ${shellArgumentFactory(`${owner}`.split(':')[0])}${
|
|
222
|
+
`${owner}`.includes(':') ? ` -g ${shellArgumentFactory(`${owner}`.split(':')[1])}` : ''
|
|
223
|
+
}`
|
|
224
|
+
: '';
|
|
225
|
+
return `[ -d ${quoted} ] || ${prefix}install -d -m ${mode}${ownerFlag} ${quoted}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Builds the full idempotent preparation for one or more container-mounted storage roots:
|
|
230
|
+
* create when missing, register the persistent `container_file_t` mapping, restore the tree.
|
|
231
|
+
* @param {string|string[]} paths - Absolute directories.
|
|
232
|
+
* @param {{mode?: string, owner?: string, sudo?: boolean}} [options]
|
|
233
|
+
* @returns {string[]} Commands, in execution order.
|
|
234
|
+
*/
|
|
235
|
+
static containerStorageCommandsFactory(paths, options = {}) {
|
|
236
|
+
const requested = ContainerStorageService.absoluteStoragePaths(Array.isArray(paths) ? paths : [paths]);
|
|
237
|
+
if (requested.length === 0)
|
|
238
|
+
throw new TypeError('containerStorageCommandsFactory requires at least one absolute path');
|
|
239
|
+
ContainerStorageService.assertSafeStoragePaths(requested);
|
|
240
|
+
// Every requested path is created — a nested one (`/data/mysql`) is a real directory a PV
|
|
241
|
+
// mounts. Mapping and restoring collapse to the covering roots: an fcontext expression is
|
|
242
|
+
// `<root>(/.*)?`, so a nested entry would add a redundant rule to the local policy store and
|
|
243
|
+
// a second full relabel pass over the same tree on every deploy.
|
|
244
|
+
return [
|
|
245
|
+
...requested.map((path) => ContainerStorageService.storageDirectoryCommandFactory(path, options)),
|
|
246
|
+
...ContainerStorageService.containerStorageSubtreeCommandsFactory(requested, {
|
|
247
|
+
sudo: options.sudo !== false,
|
|
248
|
+
}),
|
|
249
|
+
];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Resolves which declared storage root covers a path.
|
|
254
|
+
* @param {string} path - Absolute path.
|
|
255
|
+
* @param {string[]} [roots] - Candidate roots.
|
|
256
|
+
* @returns {string} The covering root, or the path itself when none covers it.
|
|
257
|
+
*/
|
|
258
|
+
static coveringRootFactory(path, roots = ContainerStorageService.PLATFORM_STORAGE_ROOTS) {
|
|
259
|
+
const covering = ContainerStorageService.absoluteStoragePaths(roots)
|
|
260
|
+
.filter((root) => path === root || path.startsWith(`${root}/`))
|
|
261
|
+
.sort((a, b) => b.length - a.length)[0];
|
|
262
|
+
return covering || path;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Builds the preparation for one subtree of an already-declared storage root: the persistent
|
|
267
|
+
* mapping is registered on the covering root, and only the subtree is restored.
|
|
268
|
+
*
|
|
269
|
+
* A per-volume mapping would grow the local policy store by one `semanage fcontext` rule for
|
|
270
|
+
* every PersistentVolume the deploy flow has ever materialized, and every one of those rules is
|
|
271
|
+
* then evaluated on each relabel. One rule on the root already covers all of them, and it is
|
|
272
|
+
* what makes a volume created later — by kubelet's `DirectoryOrCreate`, or by the local-path
|
|
273
|
+
* helper pod — inherit the right label without another pass.
|
|
274
|
+
* @param {string|string[]} paths - Absolute subtrees to restore.
|
|
275
|
+
* @param {{roots?: string[], mode?: string, owner?: string, sudo?: boolean}} [options]
|
|
276
|
+
* @returns {string[]} Commands, in execution order.
|
|
277
|
+
*/
|
|
278
|
+
static containerStorageSubtreeCommandsFactory(paths, { roots, ...options } = {}) {
|
|
279
|
+
const subtrees = ContainerStorageService.absoluteStoragePaths(Array.isArray(paths) ? paths : [paths]);
|
|
280
|
+
if (subtrees.length === 0)
|
|
281
|
+
throw new TypeError('containerStorageSubtreeCommandsFactory requires at least one absolute path');
|
|
282
|
+
const sudo = options.sudo !== false;
|
|
283
|
+
ContainerStorageService.assertSafeStoragePaths(subtrees);
|
|
284
|
+
const mapped = ContainerStorageService.normalizeStorageRoots(
|
|
285
|
+
subtrees.map((path) => ContainerStorageService.coveringRootFactory(path, roots)),
|
|
286
|
+
);
|
|
287
|
+
// The mapped root is what the fcontext rule covers, so it is checked too: a subtree that is
|
|
288
|
+
// itself safe must never resolve up into a root that is not.
|
|
289
|
+
ContainerStorageService.assertSafeStoragePaths(mapped);
|
|
290
|
+
return [
|
|
291
|
+
...mapped.map((root) =>
|
|
292
|
+
SELinuxService.selinuxFileContextCommandFactory(root, {
|
|
293
|
+
type: ContainerStorageService.SHARED_CONTAINER_TYPE,
|
|
294
|
+
sudo,
|
|
295
|
+
}),
|
|
296
|
+
),
|
|
297
|
+
SELinuxService.selinuxRestoreconCommandFactory(ContainerStorageService.normalizeStorageRoots(subtrees), {
|
|
298
|
+
sudo,
|
|
299
|
+
}),
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Executes {@link containerStorageSubtreeCommandsFactory}.
|
|
305
|
+
* @param {string|string[]} paths - Absolute subtrees.
|
|
306
|
+
* @param {{execute: Function, roots?: string[], sudo?: boolean}} options
|
|
307
|
+
* @returns {*[]} Executor results.
|
|
308
|
+
*/
|
|
309
|
+
static ensureContainerStorageSubtree(paths, { execute, ...options } = {}) {
|
|
310
|
+
return runSELinuxCommands(ContainerStorageService.containerStorageSubtreeCommandsFactory(paths, options), {
|
|
311
|
+
execute,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Executes {@link containerStorageCommandsFactory}.
|
|
317
|
+
* @param {string|string[]} paths - Absolute directories.
|
|
318
|
+
* @param {{execute: Function, mode?: string, owner?: string, sudo?: boolean}} options
|
|
319
|
+
* @returns {*[]} Executor results.
|
|
320
|
+
*/
|
|
321
|
+
static ensureContainerStorage(paths, { execute, ...options } = {}) {
|
|
322
|
+
return runSELinuxCommands(ContainerStorageService.containerStorageCommandsFactory(paths, options), { execute });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Command that prints the local-path provisioner `config.json`, or nothing when absent.
|
|
327
|
+
* @param {{namespace: string, configMap?: string}} options
|
|
328
|
+
* @returns {string} Shell command.
|
|
329
|
+
*/
|
|
330
|
+
static localPathConfigCommandFactory({ namespace, configMap = ContainerStorageService.LOCAL_PATH_CONFIG_MAP } = {}) {
|
|
331
|
+
if (!namespace) throw new TypeError('localPathConfigCommandFactory requires a namespace');
|
|
332
|
+
return `kubectl get configmap ${configMap} -n ${namespace} -o jsonpath='{.data.config\\.json}' 2>/dev/null || true`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Resolves every node directory that must carry the shared container label on this host.
|
|
337
|
+
* @param {{localPathRoots?: string[], extraPaths?: string[], controlPlane?: boolean}} [options]
|
|
338
|
+
* @returns {string[]} Non-overlapping roots.
|
|
339
|
+
*/
|
|
340
|
+
static containerStorageRootsFactory({ localPathRoots = [], extraPaths = [], controlPlane = false } = {}) {
|
|
341
|
+
return ContainerStorageService.normalizeStorageRoots([
|
|
342
|
+
...ContainerStorageService.PLATFORM_STORAGE_ROOTS,
|
|
343
|
+
...(controlPlane ? ContainerStorageService.CONTROL_PLANE_MOUNT_PATHS : []),
|
|
344
|
+
...(localPathRoots.length > 0 ? localPathRoots : [ContainerStorageService.LOCAL_PATH_DEFAULT_ROOT]),
|
|
345
|
+
...extraPaths,
|
|
346
|
+
]);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const {
|
|
351
|
+
absoluteStoragePaths,
|
|
352
|
+
isSafeStoragePath,
|
|
353
|
+
assertSafeStoragePaths,
|
|
354
|
+
containerStorageCommandsFactory,
|
|
355
|
+
containerStorageRootsFactory,
|
|
356
|
+
containerStorageSubtreeCommandsFactory,
|
|
357
|
+
coveringRootFactory,
|
|
358
|
+
ensureContainerStorage,
|
|
359
|
+
ensureContainerStorageSubtree,
|
|
360
|
+
localPathConfigCommandFactory,
|
|
361
|
+
localPathRootsFactory,
|
|
362
|
+
normalizeStorageRoots,
|
|
363
|
+
storageDirectoryCommandFactory,
|
|
364
|
+
} = ContainerStorageService;
|
|
365
|
+
|
|
366
|
+
export default ContainerStorageService;
|
|
367
|
+
|
|
368
|
+
export {
|
|
369
|
+
absoluteStoragePaths,
|
|
370
|
+
isSafeStoragePath,
|
|
371
|
+
assertSafeStoragePaths,
|
|
372
|
+
containerStorageCommandsFactory,
|
|
373
|
+
containerStorageRootsFactory,
|
|
374
|
+
containerStorageSubtreeCommandsFactory,
|
|
375
|
+
coveringRootFactory,
|
|
376
|
+
ensureContainerStorage,
|
|
377
|
+
ensureContainerStorageSubtree,
|
|
378
|
+
localPathConfigCommandFactory,
|
|
379
|
+
localPathRootsFactory,
|
|
380
|
+
normalizeStorageRoots,
|
|
381
|
+
storageDirectoryCommandFactory,
|
|
382
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SELinux policy, labeling, and enforcement command helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module src/server/security/selinux.js
|
|
5
|
+
* @namespace SELinuxService
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main SELinux utility.
|
|
11
|
+
* @class SELinuxService
|
|
12
|
+
* @memberof SELinuxService
|
|
13
|
+
*/
|
|
14
|
+
class SELinuxService {
|
|
15
|
+
/** Shared container label every unprivileged container domain can read and write. */
|
|
16
|
+
static SHARED_CONTAINER_TYPE = 'container_file_t';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Quotes one shell argument used by generated SELinux commands.
|
|
20
|
+
* @param {*} value - Value to quote.
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
static shellArgumentFactory(value) {
|
|
24
|
+
return `'${`${value ?? ''}`.replaceAll("'", `'"'"'`)}'`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds the Rocky/RHEL SELinux userspace installation command.
|
|
29
|
+
* @param {{sudo?: boolean}} [options]
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
static selinuxPackagesCommandFactory({ sudo = true } = {}) {
|
|
33
|
+
return `${sudo ? 'sudo ' : ''}dnf install -y policycoreutils policycoreutils-python-utils selinux-policy-targeted audit`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Builds commands that make Enforcing mode persistent and active.
|
|
38
|
+
* @param {{sudo?: boolean, restorePaths?: string[]}} [options]
|
|
39
|
+
* @returns {string[]}
|
|
40
|
+
*/
|
|
41
|
+
static selinuxEnforcingCommandsFactory({ sudo = true, restorePaths = [] } = {}) {
|
|
42
|
+
const prefix = sudo ? 'sudo ' : '';
|
|
43
|
+
return [
|
|
44
|
+
`if [ -f /etc/selinux/config ]; then ${prefix}sed -i -E 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config; fi`,
|
|
45
|
+
// A host running with SELinux Disabled has an unlabeled filesystem, so the
|
|
46
|
+
// config flip alone would boot it into Enforcing with nothing labeled.
|
|
47
|
+
// `setenforce` cannot activate the mode from Disabled either: the switch
|
|
48
|
+
// completes on the next boot, and only after this relabel pass.
|
|
49
|
+
`if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" = "Disabled" ]; then ${prefix}touch /.autorelabel; fi`,
|
|
50
|
+
...(restorePaths.length > 0
|
|
51
|
+
? [SELinuxService.selinuxRestoreconCommandFactory(restorePaths, { sudo })]
|
|
52
|
+
: []),
|
|
53
|
+
`if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" != "Disabled" ]; then ${prefix}setenforce 1; fi`,
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Builds a command that restores policy-defined file contexts.
|
|
59
|
+
* @param {string|string[]} paths - Files or directories to label.
|
|
60
|
+
* @param {{recursive?: boolean, sudo?: boolean}} [options]
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
static selinuxRestoreconCommandFactory(paths, { recursive = true, sudo = true } = {}) {
|
|
64
|
+
const values = (Array.isArray(paths) ? paths : [paths]).filter(Boolean);
|
|
65
|
+
if (values.length === 0) throw new TypeError('selinuxRestoreconCommandFactory requires at least one path');
|
|
66
|
+
const operations = values
|
|
67
|
+
.map(SELinuxService.shellArgumentFactory)
|
|
68
|
+
.map(
|
|
69
|
+
(path) =>
|
|
70
|
+
`{ [ ! -e ${path} ] || ${sudo ? 'sudo ' : ''}restorecon ${recursive ? '-RF ' : ''}${path}; }`,
|
|
71
|
+
)
|
|
72
|
+
.join(' && ');
|
|
73
|
+
return `if command -v restorecon >/dev/null 2>&1; then ${operations}; fi`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Builds an idempotent persistent file context mapping.
|
|
78
|
+
* @param {string} path - Directory or file prefix to map.
|
|
79
|
+
* @param {{type: string, sudo?: boolean}} options
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
static selinuxFileContextCommandFactory(path, { type, sudo = true } = {}) {
|
|
83
|
+
if (!path) throw new TypeError('selinuxFileContextCommandFactory requires a path');
|
|
84
|
+
if (!type) throw new TypeError('selinuxFileContextCommandFactory requires a type');
|
|
85
|
+
const prefix = sudo ? 'sudo ' : '';
|
|
86
|
+
const expression = SELinuxService.shellArgumentFactory(`${path}(/.*)?`);
|
|
87
|
+
return `if command -v selinuxenabled >/dev/null 2>&1 && selinuxenabled; then command -v semanage >/dev/null 2>&1 || { echo 'semanage is required for persistent file contexts' >&2; exit 1; }; ${prefix}semanage fcontext -a -t ${type} ${expression} 2>/dev/null || ${prefix}semanage fcontext -m -t ${type} ${expression}; fi`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Builds persistent labeling commands for host paths bind-mounted into
|
|
92
|
+
* unprivileged containers. `container_t` cannot read the policy defaults of
|
|
93
|
+
* those trees (`kubernetes_file_t`, `var_lib_t`), and the mapping is
|
|
94
|
+
* registered before the files exist so entries created later inherit the
|
|
95
|
+
* shared label instead of requiring another relabel pass.
|
|
96
|
+
* @param {string|string[]} paths - Files or directories to share.
|
|
97
|
+
* @param {{sudo?: boolean}} [options]
|
|
98
|
+
* @returns {string[]}
|
|
99
|
+
*/
|
|
100
|
+
static selinuxContainerSharedContextCommandsFactory(paths, { sudo = true } = {}) {
|
|
101
|
+
const values = (Array.isArray(paths) ? paths : [paths]).filter(Boolean);
|
|
102
|
+
if (values.length === 0)
|
|
103
|
+
throw new TypeError('selinuxContainerSharedContextCommandsFactory requires at least one path');
|
|
104
|
+
return [
|
|
105
|
+
...values.map((path) =>
|
|
106
|
+
SELinuxService.selinuxFileContextCommandFactory(path, { type: SELinuxService.SHARED_CONTAINER_TYPE, sudo }),
|
|
107
|
+
),
|
|
108
|
+
SELinuxService.selinuxRestoreconCommandFactory(values, { sudo }),
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Builds persistent labeling commands for an SSH directory.
|
|
114
|
+
* Standard /root and /home locations already have policy mappings; custom
|
|
115
|
+
* home locations receive an explicit ssh_home_t mapping.
|
|
116
|
+
* @param {{sshDirectory: string, sudo?: boolean}} options
|
|
117
|
+
* @returns {string[]}
|
|
118
|
+
*/
|
|
119
|
+
static selinuxSshContextCommandsFactory({ sshDirectory, sudo = true } = {}) {
|
|
120
|
+
if (!sshDirectory) throw new TypeError('selinuxSshContextCommandsFactory requires sshDirectory');
|
|
121
|
+
const prefix = sudo ? 'sudo ' : '';
|
|
122
|
+
const standard = sshDirectory === '/root/.ssh' || /^\/home\/[^/]+\/\.ssh$/.test(sshDirectory);
|
|
123
|
+
const commands = [];
|
|
124
|
+
if (!standard) {
|
|
125
|
+
const expression = SELinuxService.shellArgumentFactory(`${sshDirectory}(/.*)?`);
|
|
126
|
+
commands.push(
|
|
127
|
+
`if command -v selinuxenabled >/dev/null 2>&1 && selinuxenabled; then command -v semanage >/dev/null 2>&1 || { echo 'semanage is required for a custom SSH home' >&2; exit 1; }; ${prefix}semanage fcontext -a -t ssh_home_t ${expression} 2>/dev/null || ${prefix}semanage fcontext -m -t ssh_home_t ${expression}; fi`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
commands.push(SELinuxService.selinuxRestoreconCommandFactory(sshDirectory, { sudo }));
|
|
131
|
+
return commands;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Builds an idempotent ssh_port_t assignment for a custom SSH port.
|
|
136
|
+
* @param {{port?: number|string, sudo?: boolean}} [options]
|
|
137
|
+
* @returns {string[]}
|
|
138
|
+
*/
|
|
139
|
+
static selinuxSshPortCommandsFactory({ port = 22, sudo = true } = {}) {
|
|
140
|
+
const value = Number(port);
|
|
141
|
+
if (!Number.isInteger(value) || value < 1 || value > 65535) throw new RangeError('SSH port must be 1-65535');
|
|
142
|
+
if (value === 22) return [];
|
|
143
|
+
const prefix = sudo ? 'sudo ' : '';
|
|
144
|
+
return [
|
|
145
|
+
`if command -v selinuxenabled >/dev/null 2>&1 && selinuxenabled; then command -v semanage >/dev/null 2>&1 || { echo 'semanage is required for a custom SSH port' >&2; exit 1; }; ${prefix}semanage port -a -t ssh_port_t -p tcp ${value} 2>/dev/null || ${prefix}semanage port -m -t ssh_port_t -p tcp ${value}; fi`,
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Executes a generated command list.
|
|
151
|
+
* @param {string[]} [commands]
|
|
152
|
+
* @param {{execute: Function}} options
|
|
153
|
+
* @returns {*[]}
|
|
154
|
+
*/
|
|
155
|
+
static runSELinuxCommands(commands = [], { execute } = {}) {
|
|
156
|
+
if (typeof execute !== 'function') throw new TypeError('runSELinuxCommands requires an executor');
|
|
157
|
+
return commands.map((command) => execute(command));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const {
|
|
162
|
+
runSELinuxCommands,
|
|
163
|
+
selinuxContainerSharedContextCommandsFactory,
|
|
164
|
+
selinuxEnforcingCommandsFactory,
|
|
165
|
+
selinuxFileContextCommandFactory,
|
|
166
|
+
selinuxPackagesCommandFactory,
|
|
167
|
+
selinuxRestoreconCommandFactory,
|
|
168
|
+
selinuxSshContextCommandsFactory,
|
|
169
|
+
selinuxSshPortCommandsFactory,
|
|
170
|
+
shellArgumentFactory,
|
|
171
|
+
} = SELinuxService;
|
|
172
|
+
|
|
173
|
+
export default SELinuxService;
|
|
174
|
+
|
|
175
|
+
export {
|
|
176
|
+
runSELinuxCommands,
|
|
177
|
+
selinuxContainerSharedContextCommandsFactory,
|
|
178
|
+
selinuxEnforcingCommandsFactory,
|
|
179
|
+
selinuxFileContextCommandFactory,
|
|
180
|
+
selinuxPackagesCommandFactory,
|
|
181
|
+
selinuxRestoreconCommandFactory,
|
|
182
|
+
selinuxSshContextCommandsFactory,
|
|
183
|
+
selinuxSshPortCommandsFactory,
|
|
184
|
+
shellArgumentFactory,
|
|
185
|
+
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Manages backup operations for deployments.
|
|
3
|
-
* @module src/server/backup.js
|
|
3
|
+
* @module src/server/storage/backup.js
|
|
4
4
|
* @namespace UnderpostBakcUp
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
|
-
import { loggerFactory } from '
|
|
9
|
-
import Underpost from '
|
|
10
|
-
import { loadCronDeployEnv } from '
|
|
8
|
+
import { loggerFactory } from '../ops/logger.js';
|
|
9
|
+
import Underpost from '../../index.js';
|
|
10
|
+
import { loadCronDeployEnv } from '../ops/cron.js';
|
|
11
|
+
import { readDeployRoutes } from '../network/router.js';
|
|
11
12
|
|
|
12
13
|
const logger = loggerFactory(import.meta);
|
|
13
14
|
|
|
@@ -39,13 +40,13 @@ class BackUp {
|
|
|
39
40
|
static callback = async function (deployList, options = { git: false }) {
|
|
40
41
|
const firstDeployId = deployList && deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
|
|
41
42
|
loadCronDeployEnv();
|
|
42
|
-
if (
|
|
43
|
-
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
|
|
43
|
+
if (!deployList || deployList === 'dd') deployList = readDeployRoutes().join(',');
|
|
44
44
|
|
|
45
45
|
logger.info('init backups callback', deployList);
|
|
46
46
|
await logger.setUpInfo();
|
|
47
47
|
|
|
48
48
|
const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
|
|
49
|
+
const failures = [];
|
|
49
50
|
|
|
50
51
|
for (const _deployId of deployList.split(',')) {
|
|
51
52
|
const deployId = _deployId.trim();
|
|
@@ -71,7 +72,8 @@ class BackUp {
|
|
|
71
72
|
cd: '/home/dd/engine',
|
|
72
73
|
});
|
|
73
74
|
} catch (err) {
|
|
74
|
-
|
|
75
|
+
failures.push({ deployId, phase: 'database-export', error: err.message });
|
|
76
|
+
logger.error(`Error during database export for ${deployId}`, { error: err.message });
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// Repository backup: Cron container → SSH to host → host finds pod → kubectl exec git backup
|
|
@@ -83,9 +85,16 @@ class BackUp {
|
|
|
83
85
|
cd: '/home/dd/engine',
|
|
84
86
|
});
|
|
85
87
|
} catch (err) {
|
|
86
|
-
|
|
88
|
+
failures.push({ deployId, phase: 'repository-backup', error: err.message });
|
|
89
|
+
logger.error(`Error during repository backup for ${deployId}`, { error: err.message });
|
|
87
90
|
}
|
|
88
91
|
}
|
|
92
|
+
|
|
93
|
+
// One deploy-id failing must not abort the remaining ones, so the loop swallows
|
|
94
|
+
// per-phase errors — this summary is the only place the run's real outcome shows up.
|
|
95
|
+
// The failing `shellExec` has already latched `container-status=error` for monitoring.
|
|
96
|
+
if (failures.length > 0) logger.error('Backup completed with failures', { count: failures.length, failures });
|
|
97
|
+
else logger.info('Backup completed for all deploy-ids', { deployList });
|
|
89
98
|
};
|
|
90
99
|
}
|
|
91
100
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module for parsing data query parameters into Mongoose query options.
|
|
3
3
|
* Supports AG Grid filterModel/sortModel as well as simple legacy parameters.
|
|
4
|
-
* @module src/server/data-query.js
|
|
4
|
+
* @module src/server/storage/data-query.js
|
|
5
5
|
* @namespace DataQuery
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provides a utility class for downloading files from a URL and saving them to the local filesystem.
|
|
3
|
-
* @module src/server/downloader.js
|
|
3
|
+
* @module src/server/storage/downloader.js
|
|
4
4
|
* @namespace Downloader
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import axios from 'axios';
|
|
8
8
|
import fs from 'fs';
|
|
9
|
-
import { loggerFactory } from '
|
|
9
|
+
import { loggerFactory } from '../ops/logger.js';
|
|
10
10
|
|
|
11
11
|
const logger = loggerFactory(import.meta);
|
|
12
12
|
|