diff --git a/.dockerignore b/.dockerignore index 18dfe8d78..72c4d8543 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,8 +9,12 @@ node_modules *.pid /src/data/providers/* !/src/data/providers/sqlite.js +!/src/data/providers/mysql.js +!/src/data/providers/postgres.js !/src/data/providers/database-*.js /src/config/*-config.json .DS_Store iofogcontroller-*.tgz -iofog-iofogcontroller-*.tgz \ No newline at end of file +iofog-iofogcontroller-*.tgz +.env +src/iofog-controller.pid \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 34f6374f1..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,15 +0,0 @@ - - -- Does the iofog.org or README.md documentation need to change? - - [ ] Yes - - [ ] No - -### Description - diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 600a68db6..2f756b1d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,100 +1,93 @@ name: CI on: - push: + push: branches: - - develop - - release* + - main tags: [v*] paths-ignore: - README.md - CHANGELOG.md - LICENSE pull_request: - # Sequence of patterns matched against refs/heads - branches: - - develop - - release* + # Sequence of patterns matched against refs/heads + branches: + - main paths-ignore: - README.md - CHANGELOG.md - LICENSE - env: - project: 'focal-freedom-236620' - image: 'controller' + IMAGE_NAME: 'controller' + jobs: Build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: - contents: 'read' - id-token: 'write' - packages: 'write' + actions: write + checks: write + contents: write + deployments: write + id-token: write + issues: write + discussions: write + packages: write + pages: write + pull-requests: write + repository-projects: write + security-events: write + statuses: write name: Preflight steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 18 - - run: npm ci + node-version: 20 + - name: Replace values + shell: bash + env: + PAT: ${{ secrets.PAT }} + run: | + sed -i.back "s|PAT|${PAT}|g" .npmrc + - run: npm config set @datasance:registry https://npm.pkg.github.com/ + - run: npm install --build-from-source --force - run: npm run standard - run: | npm i -g better-npm-audit npx better-npm-audit audit -p - Tests: - needs: Build - runs-on: ubuntu-latest + Publish: + needs: [Build] + runs-on: ubuntu-22.04 permissions: - contents: 'read' - id-token: 'write' - packages: 'write' - issues: read + actions: write checks: write + contents: write + deployments: write + id-token: write + issues: write + discussions: write + packages: write + pages: write pull-requests: write - strategy: - matrix: - node: [ 16, 17, 16, 19 ] - name: Node ${{ matrix.node }} Test - steps: - - uses: actions/checkout@v3 - - name: Setup Node ${{ matrix.node }} Test - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - - name: Cache Node Modules - uses: actions/cache@v3 - with: - path: | - node_modules - key: ${{ runner.os }}-controller-node_modules-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-controller-node_modules- - - run: npm ci - - run: npm run test -- junit - - run: npm run postman_test - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - files: | - *-results.xml - - Publish: - needs: [Build, Tests] - runs-on: ubuntu-latest - permissions: - contents: 'read' - id-token: 'write' - packages: 'write' + repository-projects: write + security-events: write + statuses: write name: Publish Controller steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 18 - - run: npm ci + node-version: 20 + - name: Replace values + shell: bash + env: + PAT: ${{ secrets.PAT }} + run: | + sed -i.back "s|PAT|${PAT}|g" .npmrc + - run: npm config set @datasance:registry https://npm.pkg.github.com/ + - run: npm install --build-from-source --force - name: npm version id: package-version @@ -112,47 +105,52 @@ jobs: - name: npm pack with version from package version run: | - npm --no-git-tag-version version ${{ steps.version.outputs.pkg_version }} npm pack + npm publish --registry=https://npm.pkg.github.com/ + + - name: 'Get Previous tag' + id: previoustag + uses: "WyriHaximus/github-action-get-previous-tag@v1" + with: + fallback: 0.0.0 + - name: Set image tag + shell: bash + id: tags + run: | + if [[ ${{ github.ref_name }} =~ ^v.* ]] ; then + VERSION=${{ github.ref_name }} + echo "VERSION=${VERSION:1}" >> "${GITHUB_OUTPUT}" + else + VERSION=${{ steps.previoustag.outputs.tag }} + echo "VERSION=${VERSION:1}-${{ github.run_number }}" >> "${GITHUB_OUTPUT}" + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: v0.29.1 + - name: Login to Github Container Registry uses: docker/login-action@v2 with: registry: "ghcr.io" username: ${{ github.actor }} - password: ${{ github.token }} - + password: ${{ secrets.PAT }} + - name: Build and Push to ghcr uses: docker/build-push-action@v3 id: build_push_ghcr with: - file: Dockerfile.dev + file: Dockerfile + context: . + platforms: linux/amd64, linux/arm64 push: true + outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=Controller + build-args: GITHUB_TOKEN=${{ secrets.PAT }} tags: | - ghcr.io/eclipse-iofog/controller:${{ steps.version.outputs.pkg_version }} - ghcr.io/eclipse-iofog/controller:latest - build-args: PKG_VERSION=${{ steps.version.outputs.pkg_version }} - - - name: Build and Push to GCR - id: build_push_gcr - uses: RafikFarhad/push-to-gcr-github-action@v5-beta - with: - gcloud_service_key: ${{ secrets.GCLOUD_SERVICE_KEY }} - registry: gcr.io - project_id: ${{ env.project }} - image_name: ${{ env.image }} - image_tag: latest,${{ steps.version.outputs.pkg_version }} - dockerfile: Dockerfile.dev - build_args: PKG_VERSION=${{ steps.version.outputs.pkg_version }} - - - run: ls - - - name: Publish package to packagecloud - if: ${{ steps.build_push_gcr.outcome }} == 'success' - uses: danielmundi/upload-packagecloud@v1 - with: - PACKAGE-NAME: iofog-iofogcontroller-${{ steps.version.outputs.pkg_version }}.tgz - PACKAGECLOUD-USERNAME: iofog - PACKAGECLOUD-REPO: iofog-controller-snapshots - PACKAGECLOUD-DISTRIB: node - PACKAGECLOUD-TOKEN: ${{ secrets.packagecloud_token }} \ No newline at end of file + ghcr.io/datasance/${{ env.IMAGE_NAME }}:${{ steps.tags.outputs.VERSION }} + ghcr.io/datasance/${{ env.IMAGE_NAME }}:latest + ghcr.io/datasance/${{ env.IMAGE_NAME }}:main diff --git a/.gitignore b/.gitignore index 97d5e39fa..76ea49d75 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,14 @@ node_modules *.pid /src/data/providers/* !/src/data/providers/sqlite.js +!/src/data/providers/mysql.js +!/src/data/providers/postgres.js !/src/data/providers/database-*.js /src/config/*-config.json .DS_Store iofogcontroller-*.tgz diagnostic/ -iofog-iofogcontroller-*.tgz \ No newline at end of file +iofog-iofogcontroller-*.tgz +.npmrc +.env +src/iofog-controller.pid \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..11cf9fea1 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +//npm.pkg.github.com/:_authToken=PAT +@Datasance:registry=https://npm.pkg.github.com/ \ No newline at end of file diff --git a/.nsprc b/.nsprc index 634f5426d..9c2174be1 100644 --- a/.nsprc +++ b/.nsprc @@ -1,8 +1,3 @@ { - "1091459": { - "notes": "" - }, - "1091725": { - "notes": "" - } -} \ No newline at end of file + + } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..376594a5a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +FROM node:iron-bookworm AS builder + +ARG PKG_VERSION +# ARG GITHUB_TOKEN + +WORKDIR /tmp + +RUN npm i -g npm + +COPY package.json . + +COPY . . + +# # Set GitHub npm registry with authentication token +# RUN sed -i.back "s|PAT|${GITHUB_TOKEN}|g" .npmrc + +# RUN npm config set @datasance:registry https://npm.pkg.github.com/ + +RUN npm i --build-from-source --force + +RUN npm version $PKG_VERSION --allow-same-version --no-git-tag-version + +RUN npm pack + + +FROM registry.access.redhat.com/ubi9/nodejs-20-minimal:latest + +USER root +# Install dependencies for logging and development +RUN microdnf install -y g++ make && microdnf clean all + +# Install Python and pip +RUN microdnf install -y python3 && \ + ln -sf python3 /usr/bin/python && \ + python3 -m ensurepip && \ + pip3 install --no-cache --upgrade pip setuptools && \ + microdnf install shadow-utils && \ + microdnf clean all +RUN microdnf install -y tzdata && microdnf clean all +RUN microdnf -y remove microdnf +RUN useradd --uid 10000 --create-home runner +RUN mkdir -p /var/log/iofog-controller && \ + chown runner:runner /var/log/iofog-controller && \ + chmod 755 /var/log/iofog-controller +USER 10000 +WORKDIR /home/runner + +ENV NPM_CONFIG_PREFIX=/home/runner/.npm-global +ENV NPM_CONFIG_CACHE=/home/runner/.npm +ENV PATH=$PATH:/home/runner/.npm-global/bin + +COPY --from=builder /tmp/datasance-iofogcontroller-*.tgz /home/runner/iofog-controller.tgz + +ENV PID_BASE=/home/runner + +RUN npm i -g /home/runner/iofog-controller.tgz && \ + rm -rf /home/runner/iofog-controller.tgz && \ + iofog-controller config dev-mode --on + +RUN rm -rf /home/runner/.npm-global/lib/node_modules/@datasance/iofogcontroller/src/data/sqlite_files/* + +COPY LICENSE /licenses/LICENSE +LABEL org.opencontainers.image.description=controller +LABEL org.opencontainers.image.source=https://github.com/datasance/controller +LABEL org.opencontainers.image.licenses=EPL2.0 +CMD [ "node", "/home/runner/.npm-global/lib/node_modules/@datasance/iofogcontroller/src/server.js" ] diff --git a/Dockerfile.dev b/Dockerfile.dev index 09e2209cc..36e060e8b 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,6 +1,7 @@ -FROM node:hydrogen-buster AS builder +FROM node:iron-bookworm AS builder ARG PKG_VERSION +ARG GITHUB_TOKEN WORKDIR /tmp @@ -10,13 +11,18 @@ COPY package.json . COPY . . -RUN npm i --build-from-source +# Set GitHub npm registry with authentication token +RUN sed -i.back "s|PAT|${GITHUB_TOKEN}|g" .npmrc + +RUN npm config set @datasance:registry https://npm.pkg.github.com/ + +RUN npm i --build-from-source --force RUN npm version $PKG_VERSION --allow-same-version --no-git-tag-version RUN npm pack -FROM node:hydrogen-alpine3.17 +FROM node:iron-alpine3.18 RUN apk add sudo logrotate g++ make @@ -30,11 +36,13 @@ RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python RUN python3 -m ensurepip RUN pip3 install --no-cache --upgrade pip setuptools -COPY --from=builder /tmp/iofog-iofogcontroller-*.tgz /tmp/iofog-controller.tgz +COPY --from=builder /tmp/datasance-iofogcontroller-*.tgz /tmp/iofog-controller.tgz RUN npm i -g /tmp/iofog-controller.tgz && \ rm -rf /tmp/iofog-controller.tgz && \ iofog-controller config dev-mode --on -# RUN sudo rm -rf /usr/local/lib/node_modules/iofogcontroller/src/data/sqlite_files -CMD [ "node", "/usr/local/lib/node_modules/@iofog/iofogcontroller/src/server.js" ] +LABEL org.opencontainers.image.description=controller +LABEL org.opencontainers.image.source=https://github.com/datasance/controller +LABEL org.opencontainers.image.licenses=EPL2.0 +CMD [ "node", "/usr/local/lib/node_modules/@datasance/iofogcontroller/src/server.js" ] diff --git a/Dockerfile.rel b/Dockerfile.rel index a5cb14585..62df6f6b5 100644 --- a/Dockerfile.rel +++ b/Dockerfile.rel @@ -1,4 +1,4 @@ -FROM node:hydrogen-buster AS builder +FROM node:hydrogen-bookworm AS builder ARG PKG_VERSION @@ -30,11 +30,14 @@ ENV PYTHONUNBUFFERED=1 RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python RUN python3 -m ensurepip RUN pip3 install --no-cache --upgrade pip setuptools - -COPY --from=builder /tmp/iofog-iofogcontroller-*.tgz /tmp/iofog-controller.tgz +LABEL org.opencontainers.image.description controller +LABEL org.opencontainers.image.source=https://github.com/datasance/controller +LABEL org.opencontainers.image.licenses=EPL2.0 +COPY --from=builder /tmp/datasance-iofogcontroller-*.tgz /tmp/iofog-controller.tgz RUN npm i -g /tmp/iofog-controller.tgz && \ rm -rf /tmp/iofog-controller.tgz && \ iofog-controller config dev-mode --on -CMD [ "node", "/usr/local/lib/node_modules/@iofog/iofogcontroller/src/server.js" ] + +CMD [ "node", "/usr/local/lib/node_modules/@datasance/iofogcontroller/src/server.js" ] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..a8960fddf --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +DOCKER_IMAGE_VERSION=1.0 +DOCKER_IMAGE_NAME=emirhandurmus/controller +DOCKER_IMAGE_TAGNAME=$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_VERSION) + +default: build + +build: + docker build -t $(DOCKER_IMAGE_TAGNAME) -f Dockerfile.dev . + docker tag $(DOCKER_IMAGE_TAGNAME) $(DOCKER_IMAGE_NAME):latest + +push:build + docker push $(DOCKER_IMAGE_TAGNAME) + docker push $(DOCKER_IMAGE_NAME) + diff --git a/README.md b/README.md index ad333414f..da3e8d7c6 100644 --- a/README.md +++ b/README.md @@ -2,32 +2,30 @@ ### Status -![](https://img.shields.io/github/release/iofog/controller.svg?style=flat) +![](https://img.shields.io/github/release/datasance/controller.svg?style=flat) -![](https://img.shields.io/github/repo-size/iofog/controller.svg?style=flat) -![](https://img.shields.io/github/last-commit/iofog/controller.svg?style=flat) -![](https://img.shields.io/github/contributors/iofog/controller.svg?style=flat) -![](https://img.shields.io/github/issues/iofog/controller.svg?style=flat) +![](https://img.shields.io/github/repo-size/datasance/controller.svg?style=flat) +![](https://img.shields.io/github/last-commit/datasance/controller.svg?style=flat) +![](https://img.shields.io/github/contributors/datasance/controller.svg?style=flat) +![](https://img.shields.io/github/issues/datasance/controller.svg?style=flat) ![Supports amd64 Architecture][amd64-shield] ![Supports aarch64 Architecture][arm64-shield] -![Supports armhf Architecture][arm-shield] [arm64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg -[arm-shield]: https://img.shields.io/badge/armhf-yes-green.svg [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) ## Install -The entire ioFog platform is best deployed through the unified CLI: `iofogctl`. +The entire Datasance PoT platform is best deployed through the unified CLI: `potctl`. -Go to [iofog.org](https://iofog.org/docs/) to learn how to deploy the ioFog Control Plane and Agents. +Go to [Datasance Docs](https://docs.datasance.com) to learn how to deploy the ioFog Control Plane and Agents. ## Usage ``` iofog-controller ``` -For full installation and usage, visit [iofog.org](https://iofog.org/docs/). +For full installation and usage, visit [Datasance Docs](https://docs.datasance.com). diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100755 index a674ef6ab..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,227 +0,0 @@ -trigger: - tags: - include: - - v* - branches: - include: - - develop - - release* - paths: - exclude: - - README.md - - CHANGELOG.md - - LICENSE - -variables: - group: 'pipelines' - repository: 'focal-freedom-236620/controller' - ref: $(Build.SourceBranch) - imageTag: - buildTag: $(Build.BuildId) - branchTag: $(Build.SourceBranchName) - releaseCandidate: 'rc' - -stages: -- stage: Preflight - jobs: - - job: Build - pool: - vmImage: 'Ubuntu-20.04' - - steps: - - task: NodeTool@0 - inputs: - versionSource: 'spec' - versionSpec: '18.x' - displayName: 'Install Node.js' - - - script: | - npm -v - node -v - npm install - displayName: 'npm install and build' - - - script: | - npm run standard - displayName: 'Standardjs report' - - - script: | - npm i -g better-npm-audit - npx better-npm-audit audit -p - displayName: Check for vulnerabilities - - # npx better-npm-audit audit -p - # To be removed once new version of ncof is released - - - # - script: | - # npm run snyk -- --project-name=ControllerCI - # displayName: 'Snyk monitor' - # env: - # SNYK_TOKEN: $(snykToken) -- stage: Test - jobs: - - template: azure-templates/test.yml - parameters: - nodeVersion: '16' - - template: azure-templates/test.yml - parameters: - nodeVersion: '17' - - template: azure-templates/test.yml - parameters: - nodeVersion: '18' - - template: azure-templates/test.yml - parameters: - nodeVersion: '19' -- stage: Publish - jobs: - # - job: Platform - # dependsOn: Controller - # pool: server - # variables: - # commit: b-$(buildTag) - - # steps: - # - task: InvokeRESTAPI@1 - # displayName: 'trigger platform job' - # inputs: - # connectionType: 'connectedServiceName' - # serviceConnection: 'Pipelines' - # method: 'POST' - # urlSuffix: '/edgeworx/_apis/build/builds?api-version=5.0' - # body: "{\"Parameters\":\"{\\\"images.controller\\\": \\\"gcr.io/$(repository):$(commit)\\\"}\", \"Definition\":{\"id\":\"5\"}}" - # waitForCompletion: 'false' - - job: Controller - pool: - vmImage: 'Ubuntu-20.04' - steps: - - task: NodeTool@0 - inputs: - versionSpec: '18.x' - displayName: 'Install Node.js' - - - script: | - npm install - displayName: 'npm install' - - - script: | - npm pack - ls iofog-iofogcontroller-*.tgz - displayName: 'npm pack for release artefact' - - - script: | - if [[ $(ref) == refs/tags* ]]; then - TAG=$(echo $(ref) | sed "s|refs/tags/v||g") - echo "##vso[task.setvariable variable=imageTag]$TAG" - else - LATESTTAG=$(git tag | tail -1) - LATESTVERS=${LATESTTAG#?} - if [ -z "$LATESTVERS" ]; then LATESTVERS=0.0.0; fi - echo "##vso[task.setvariable variable=imageTag]$LATESTVERS-b$(buildTag)" - fi - echo $(imageTag) - displayName: 'Set image tag' - - - script: | - echo "gcr.io/$(repository):$(imageTag)" > GCR_DOCKER_IMAGE - displayName: 'Save Docker image name and tag to GCR_DOCKER_IMAGE into artifacts' - - - task: CopyFiles@2 - inputs: - SourceFolder: $(System.DefaultWorkingDirectory) - TargetFolder: $(Build.ArtifactStagingDirectory) - Contents: | - standardjs.out - *.tgz - GCR_DOCKER_IMAGE - OverWrite: true - displayName: 'artefacts to publish' - - - script: | - rm -fr iofog-iofogcontroller-*.tgz - git checkout package-lock.json - git config --global user.email "info@edgeworx.io" - git config --global user.name "Azure DevOps" - - PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') - if [[ $(ref) == refs/tags* ]]; then - npm --no-git-tag-version version $PACKAGE_VERSION - else - npm --no-git-tag-version version $PACKAGE_VERSION-b$(buildTag) - fi - echo "##vso[task.setvariable variable=pkg_version]$PACKAGE_VERSION" - displayName: 'npm version' - - - script: | - npm pack - displayName: 'npm pack with version containing build number' - - - bash: | - echo 'checking pack file exists..' - tar=$(ls iofog-iofogcontroller-*.tgz) - echo $tar - echo "##vso[task.setvariable variable=controller_tar]$tar" - displayName: 'setting vars' - name: setvarStep - - - task: Docker@2 - displayName: 'build docker' - inputs: - containerRegistry: 'Edgeworx GCP' - repository: $(repository) - command: 'build' - Dockerfile: "Dockerfile.dev" - arguments: --build-arg PKG_VERSION=$(pkg_version) - tags: | - $(imageTag) - $(branchTag) - latest - condition: or(and(succeeded(), startsWith(variables['build.sourceBranch'], 'refs/heads/release/')), and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/develop')), and(succeeded(), startsWith(variables['build.sourceBranch'], 'refs/tags/'))) - - - task: Docker@2 - displayName: 'push docker' - inputs: - containerRegistry: 'Edgeworx GCP' - repository: $(repository) - command: 'push' - Dockerfile: "Dockerfile.dev" - tags: | - $(imageTag) - $(branchTag) - latest - condition: or(and(succeeded(), startsWith(variables['build.sourceBranch'], 'refs/heads/release/')), and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/develop')), and(succeeded(), startsWith(variables['build.sourceBranch'], 'refs/tags/'))) - - - task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'controller' - - - task: DownloadSecureFile@1 - inputs: - secureFile: 'package_cloud' - displayName: 'download package cloud token file' - - - task: UseRubyVersion@0 - inputs: - versionSpec: '3.1.4' - addToPath: true - displayName: 'install rubygem to be used to install package_cloud cli' - - - script: | - gem install package_cloud - package_cloud -h - echo "config file..." - ls $(Agent.TempDirectory)/package_cloud - displayName: 'install package_cloud cli' - - - script: | - echo $(controller_tar) - package_cloud push iofog/iofog-controller-snapshots/node/1 $(controller_tar) --config=$(Agent.TempDirectory)/package_cloud - displayName: 'push to package cloud' - - script: | - package_cloud yank iofog/iofog-controller-snapshots/node/1 iofog-iofogcontroller-0.0.0-dev.tgz --config=$(Agent.TempDirectory)/package_cloud - npm --no-git-tag-version version 0.0.0-dev --allow-downgrade - npm pack - package_cloud push iofog/iofog-controller-snapshots/node/1 iofog-iofogcontroller-0.0.0-dev.tgz --config=$(Agent.TempDirectory)/package_cloud - displayName: 'push develop package to package cloud' - condition: and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/develop')) diff --git a/azure-templates/test.yml b/azure-templates/test.yml deleted file mode 100644 index 8df410dea..000000000 --- a/azure-templates/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -parameters: - nodeVersion: '' -jobs: -- job: ${{ format('Node_{0}', parameters.nodeVersion) }} - pool: - vmImage: 'Ubuntu-20.04' - steps: - - task: NodeTool@0 - inputs: - versionSpec: ${{ format('{0}.x', parameters.nodeVersion) }} - displayName: 'Install Node.js' - - script: | - npm i - displayName: Install dependencies - - - script: | - npm run test -- junit - displayName: 'unit tests' - - - script: | - npm run postman_test - displayName: 'integration tests' - # Publish Test Results - # Publish test results to Azure Pipelines - - script: | - ls -l - displayName: 'list files (looking for test results)' - - task: PublishTestResults@2 - inputs: - testResultsFormat: 'JUnit' # Options: JUnit, NUnit, VSTest, xUnit, cTest - testResultsFiles: '*-results.xml' - searchFolder: '$(System.DefaultWorkingDirectory)' # Optional - #mergeTestResults: false # Optional - #failTaskOnFailedTests: false # Optional - #testRunTitle: # Optional - buildPlatform: ${{ format('Node_{0}', parameters.nodeVersion) }} # Optional - testRunTitle: ${{ format('Node_{0}', parameters.nodeVersion) }} # Optional - #buildConfiguration: # Optional - #publishRunAttachments: true # Optional \ No newline at end of file diff --git a/docs/.swagger-codegen/VERSION b/docs/.swagger-codegen/VERSION index 5ccbb5bb7..2560437e4 100644 --- a/docs/.swagger-codegen/VERSION +++ b/docs/.swagger-codegen/VERSION @@ -1 +1 @@ -2.4.29 \ No newline at end of file +2.4.31 \ No newline at end of file diff --git a/docs/swagger.json b/docs/swagger.json index 3e57c5ac5..16aac50dc 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,8 +1,8 @@ { - "swagger" : "3.0.0", + "openapi" : "3.0.0", "info" : { - "version" : "3.0.0", - "title" : "ioFog Controller" + "version" : "1.0.0", + "title" : "Datasance PoT Controller" }, "tags" : [ { "name" : "Controller", @@ -61,22 +61,6 @@ } } }, - "/email-activation" : { - "get" : { - "tags" : [ "Controller" ], - "summary" : "Returns email activation status", - "operationId" : "getEmailActivationStatus", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "Email activation status" - }, - "500" : { - "description" : "Internal Server Error" - } - } - } - }, "/fog-types" : { "get" : { "tags" : [ "Controller" ], @@ -103,11 +87,9 @@ "tags" : [ "ioFog" ], "summary" : "Returns list of ioFog nodes", "operationId" : "getIOFogNodes", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -135,11 +117,9 @@ "tags" : [ "ioFog" ], "summary" : "Creates a new ioFog node", "operationId" : "createIOFogNode", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "201" : { @@ -172,11 +152,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -207,11 +185,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "202" : { @@ -242,11 +218,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { @@ -282,11 +256,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "201" : { @@ -315,11 +287,6 @@ "summary" : "Set change version command", "operationId" : "setVersionCommand", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "ioFog node id", @@ -330,6 +297,9 @@ "description" : "change version command", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -360,16 +330,14 @@ "summary" : "remote reboot fog agent", "operationId" : "setRebootCommand", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "ioFog node id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -391,19 +359,17 @@ "/iofog/{uuid}/prune" : { "post" : { "tags" : [ "ioFog" ], - "summary" : "prune reboot fog agent", + "summary" : "prune remote fog agent", "operationId" : "setPruneCommand", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "ioFog node id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -432,11 +398,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -469,11 +433,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -501,11 +463,9 @@ "tags" : [ "Application" ], "summary" : "Lists all applications", "operationId" : "listApplication", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -526,18 +486,17 @@ "description" : "Internal Server Error" } } - }, - "post" : { + } + }, + "/application/system" : { + "get" : { "tags" : [ "Application" ], - "summary" : "Creates an application", - "operationId" : "createApplication", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "summary" : "Lists all system applications", + "operationId" : "listSystemApplication", + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], - "deprecated" : true, "responses" : { "200" : { "description" : "Success", @@ -550,25 +509,31 @@ "400" : { "description" : "Bad Request" }, + "404" : { + "description" : "Not Found" + }, "500" : { "description" : "Internal Server Error" } } } }, - "/application/yaml" : { - "post" : { + "/application/system/{name}" : { + "delete" : { "tags" : [ "Application" ], - "summary" : "Creates an application using a YAML file", - "operationId" : "createApplicationYAML", + "summary" : "Deletes a system application", + "operationId" : "deleteSystemApplication", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", + "name" : "name", + "in" : "path", + "description" : "Application name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { - "200" : { + "204" : { "description" : "Success", "headers" : { "X-Timestamp" : { @@ -579,27 +544,23 @@ "400" : { "description" : "Bad Request" }, + "404" : { + "description" : "Not Found" + }, "500" : { "description" : "Internal Server Error" } } } }, - "/application/{name}" : { - "get" : { + "/application/yaml" : { + "post" : { "tags" : [ "Application" ], - "summary" : "Gets an application details", - "operationId" : "getApplication", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "name", - "in" : "path", - "description" : "Application name", - "required" : true + "summary" : "Creates an application using a YAML file", + "operationId" : "createApplicationYAML", + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -613,32 +574,28 @@ "400" : { "description" : "Bad Request" }, - "404" : { - "description" : "Not Found" - }, "500" : { "description" : "Internal Server Error" } } - }, - "put" : { + } + }, + "/application/{name}" : { + "get" : { "tags" : [ "Application" ], - "summary" : "Updates an application", - "operationId" : "updateApplication", + "summary" : "Gets an application details", + "operationId" : "getApplication", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application name", "required" : true } ], - "deprecated" : true, + "security" : [ { + "userToken" : [ ] + } ], "responses" : { - "204" : { + "200" : { "description" : "Success", "headers" : { "X-Timestamp" : { @@ -662,16 +619,14 @@ "summary" : "Deletes an application", "operationId" : "deleteApplication", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -697,16 +652,14 @@ "summary" : "Updates an application metadata", "operationId" : "patchApplication", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -734,16 +687,14 @@ "summary" : "Updates an application using a YAML file", "operationId" : "updateApplicationYAML", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -770,11 +721,9 @@ "tags" : [ "Application Template" ], "summary" : "Lists all application templates", "operationId" : "listApplicationTemplates", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -797,46 +746,14 @@ } } }, - "/applicationTemplate" : { - "post" : { - "tags" : [ "Application Template" ], - "summary" : "Creates an application template", - "operationId" : "createApplicationTemplate", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - } ], - "deprecated" : true, - "responses" : { - "200" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" - }, - "500" : { - "description" : "Internal Server Error" - } - } - } - }, "/applicationTemplate/yaml" : { "post" : { "tags" : [ "Application Template" ], "summary" : "Creates an application template using a YAML file", "operationId" : "createApplicationTemplateYAML", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -862,16 +779,14 @@ "summary" : "Gets an application template", "operationId" : "getApplicationTemplate", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application template name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -892,54 +807,19 @@ } } }, - "put" : { - "tags" : [ "Application Template" ], - "summary" : "Updates or creates an application template", - "operationId" : "updateOrCreateApplicationTemplate", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "name", - "in" : "path", - "description" : "Application template name", - "required" : true - } ], - "deprecated" : true, - "responses" : { - "200" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, "delete" : { "tags" : [ "Application Template" ], "summary" : "Deletes an application template", "operationId" : "deleteApplicationTemplate", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application template name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -965,16 +845,14 @@ "summary" : "Patches an application template", "operationId" : "patchApplicationTemplate", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application template name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -1002,16 +880,14 @@ "summary" : "Updates or creates an application template", "operationId" : "updateOrCreateApplicationTemplateFromYaml", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Application template name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -1092,11 +968,9 @@ "tags" : [ "Agent" ], "summary" : "Get an ioFog node configuration", "operationId" : "getIOFogNodeConfig", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1119,11 +993,9 @@ "tags" : [ "Agent" ], "summary" : "Updates an ioFog node configuration", "operationId" : "updateIOFogNodeConfig", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1151,11 +1023,9 @@ "tags" : [ "Agent" ], "summary" : "Gets ioFog node changes", "operationId" : "getIOFogNodeChanges", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1181,11 +1051,9 @@ "tags" : [ "Agent" ], "summary" : "Resets ioFog node changes list", "operationId" : "resetIOFogNodeChanges", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1213,11 +1081,9 @@ "tags" : [ "Agent" ], "summary" : "Posts agent status to ioFog node", "operationId" : "postAgentStatus", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1245,11 +1111,9 @@ "tags" : [ "Agent" ], "summary" : "Gets microservices running on an ioFog node", "operationId" : "getAgentMicroservicesList", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1279,11 +1143,9 @@ "in" : "path", "description" : "Microservice UUID", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + } ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1311,11 +1173,9 @@ "tags" : [ "Agent" ], "summary" : "Gets list of Docker registries", "operationId" : "getRegistriesList", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1340,11 +1200,9 @@ "tags" : [ "Agent" ], "summary" : "Get an ioFog node tunnel configuration", "operationId" : "getIOFogNodeTunnelConfig", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1372,11 +1230,9 @@ "tags" : [ "Agent" ], "summary" : "Get an ioFog node strace info", "operationId" : "getIOFogNodeStraceInfo", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1402,11 +1258,9 @@ "tags" : [ "Agent" ], "summary" : "Posts agent strace to ioFog node", "operationId" : "postIOFogNodeStraceBuffer", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1437,11 +1291,9 @@ "tags" : [ "Agent" ], "summary" : "Get change version command", "operationId" : "getChangeVersion", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1469,11 +1321,9 @@ "tags" : [ "Agent" ], "summary" : "Updates HAL hardware info", "operationId" : "putHalHardwareInfo", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1501,11 +1351,9 @@ "tags" : [ "ioFog" ], "summary" : "Retrieves HAL USB info", "operationId" : "getAgentHalUsbInfo", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -1531,11 +1379,9 @@ "tags" : [ "Agent" ], "summary" : "Updates HAL USB info", "operationId" : "putHalUsbInfo", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1563,11 +1409,9 @@ "tags" : [ "Agent" ], "summary" : "Deletes an ioFog node", "operationId" : "deleteAgentNode", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1592,11 +1436,9 @@ "tags" : [ "Agent" ], "summary" : "Get image snapshot info", "operationId" : "getImageSnapshot", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "200" : { @@ -1622,11 +1464,9 @@ "tags" : [ "Agent" ], "summary" : "Put image snapshot info on controller", "operationId" : "putImageSnapshot", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1654,11 +1494,9 @@ "tags" : [ "Agent" ], "summary" : "Post tracking info", "operationId" : "postTracking", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "Agent Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "agentToken" : [ ] } ], "responses" : { "204" : { @@ -1678,11 +1516,9 @@ "tags" : [ "Catalog" ], "summary" : "Gets microservices catalog", "operationId" : "getMicroservicesCatalog", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -1705,11 +1541,9 @@ "tags" : [ "Catalog" ], "summary" : "Creates a new microservice catalog item", "operationId" : "createMicroserviceCatalogItem", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "201" : { @@ -1741,16 +1575,14 @@ "summary" : "Gets microservice catalog item info", "operationId" : "getMicroserviceCatalogItem", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "id", "in" : "path", "description" : "Catalog Item Id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Catalog Item Info", @@ -1776,16 +1608,14 @@ "summary" : "Deletes a microservice catalog item", "operationId" : "deleteMicroserviceCatalogItem", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "id", "in" : "path", "description" : "Catalog Item Id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -1811,16 +1641,14 @@ "summary" : "Updates a microservice catalog item", "operationId" : "updateMicroserviceCatalogItem", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "id", "in" : "path", "description" : "Catalog Item Id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -1848,18 +1676,25 @@ } } }, - "/flow" : { + "/microservices" : { "get" : { - "tags" : [ "Flow" ], - "summary" : "Gets list of flows", - "operationId" : "getFlowsList", + "tags" : [ "Microservices" ], + "summary" : "Gets list of microservices", + "operationId" : "getMicroservicesList", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "name" : "flowId", + "in" : "query", + "description" : "Flow Id", + "required" : false + }, { + "name" : "application", + "in" : "query", + "description" : "Application name", + "required" : false + } ], + "security" : [ { + "userToken" : [ ] } ], - "deprecated" : true, "responses" : { "200" : { "description" : "Success", @@ -1876,18 +1711,17 @@ "description" : "Internal Server Error" } } - }, + } + }, + "/microservices/yaml" : { "post" : { - "tags" : [ "Flow" ], - "summary" : "Creates a new flow", - "operationId" : "createFlow", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "tags" : [ "Microservices" ], + "summary" : "Creates a new microservice in an Application", + "operationId" : "createMicroserviceYAML", + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], - "deprecated" : true, "responses" : { "201" : { "description" : "Created", @@ -1903,29 +1737,29 @@ "401" : { "description" : "Not Authorized" }, + "409" : { + "description" : "Duplicate Name" + }, "500" : { "description" : "Internal Server Error" } } } }, - "/flow/{id}" : { + "/microservices/{uuid}" : { "get" : { - "tags" : [ "Flow" ], - "summary" : "Gets flow info", - "operationId" : "getFlowInfo", + "tags" : [ "Microservices" ], + "summary" : "Gets a microservice info", + "operationId" : "getMicroserviceInfo", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "id", + "name" : "uuid", "in" : "path", - "description" : "Flow Id", + "description" : "Microservice Uuid", "required" : true } ], - "deprecated" : true, + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -1939,7 +1773,7 @@ "description" : "Not Authorized" }, "404" : { - "description" : "Invalid Flow Id" + "description" : "Not Found" }, "500" : { "description" : "Internal Server Error" @@ -1947,57 +1781,18 @@ } }, "delete" : { - "tags" : [ "Flow" ], - "summary" : "Deletes a flow", - "operationId" : "deleteFlow", + "tags" : [ "Microservices" ], + "summary" : "Deletes a microservice", + "operationId" : "deleteMicroservice", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "id", + "name" : "uuid", "in" : "path", - "description" : "Flow Id", + "description" : "Microservice Uuid", "required" : true } ], - "deprecated" : true, - "responses" : { - "204" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "401" : { - "description" : "Not Authorized" - }, - "404" : { - "description" : "Invalid Flow Id" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "patch" : { - "tags" : [ "Flow" ], - "summary" : "Updates a flow", - "operationId" : "updateFlow", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "id", - "in" : "path", - "description" : "Flow Id", - "required" : true + "security" : [ { + "userToken" : [ ] } ], - "deprecated" : true, "responses" : { "204" : { "description" : "Success", @@ -2014,101 +1809,61 @@ "description" : "Not Authorized" }, "404" : { - "description" : "Invalid Flow Id" + "description" : "Not Found" }, "500" : { "description" : "Internal Server Error" } } - } - }, - "/microservices" : { - "get" : { + }, + "patch" : { "tags" : [ "Microservices" ], - "summary" : "Gets list of microservices", - "operationId" : "getMicroservicesList", + "summary" : "Updates a microservice", + "operationId" : "updateMicroservice", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", + "name" : "uuid", + "in" : "path", + "description" : "Microservice Uuid", "required" : true - }, { - "name" : "flowId", - "in" : "query", - "description" : "Flow Id", - "required" : false - }, { - "name" : "application", - "in" : "query", - "description" : "Application name", - "required" : false } ], - "responses" : { - "200" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "401" : { - "description" : "Not Authorized" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "post" : { + "security" : [ { + "userToken" : [ ] + } ] + } + }, + "/microservices/system/{uuid}" : { + "patch" : { "tags" : [ "Microservices" ], - "summary" : "Creates a new microservice on an ioFog node", - "operationId" : "createMicroservice", + "summary" : "Updates a system microservice", + "operationId" : "updateSystemMicroservice", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", + "name" : "uuid", + "in" : "path", + "description" : "Microservice Uuid", "required" : true } ], - "deprecated" : true, - "responses" : { - "201" : { - "description" : "Created", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" - }, - "401" : { - "description" : "Not Authorized" - }, - "409" : { - "description" : "Duplicate Name" - }, - "500" : { - "description" : "Internal Server Error" - } - } + "security" : [ { + "userToken" : [ ] + } ] } }, - "/microservices/yaml" : { - "post" : { + "/microservices/yaml/{uuid}" : { + "patch" : { "tags" : [ "Microservices" ], - "summary" : "Creates a new microservice in an Application", - "operationId" : "createMicroserviceYAML", + "summary" : "Updates a microservice", + "operationId" : "updateMicroserviceYAML", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", + "name" : "uuid", + "in" : "path", + "description" : "Microservice Uuid", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { - "201" : { - "description" : "Created", + "204" : { + "description" : "Success", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2121,6 +1876,9 @@ "401" : { "description" : "Not Authorized" }, + "404" : { + "description" : "Not Found" + }, "409" : { "description" : "Duplicate Name" }, @@ -2130,25 +1888,23 @@ } } }, - "/microservices/{uuid}" : { + "/microservices/{uuid}/port-mapping" : { "get" : { "tags" : [ "Microservices" ], - "summary" : "Gets a microservice info", - "operationId" : "getMicroserviceInfo", + "summary" : "Get a port mapping list for microservice", + "operationId" : "getMicroservicePortMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { - "description" : "Success", + "description" : "Created", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2166,63 +1922,22 @@ } } }, - "delete" : { + "post" : { "tags" : [ "Microservices" ], - "summary" : "Deletes a microservice", - "operationId" : "deleteMicroservice", + "summary" : "Creates a port mapping for microservice", + "operationId" : "createMicroservicePortMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true } ], - "responses" : { - "204" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" - }, - "401" : { - "description" : "Not Authorized" - }, - "404" : { - "description" : "Not Found" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "patch" : { - "tags" : [ "Microservices" ], - "summary" : "Updates a microservice", - "operationId" : "updateMicroservice", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "uuid", - "in" : "path", - "description" : "Microservice Uuid", - "required" : true + "security" : [ { + "userToken" : [ ] } ], - "deprecated" : true, "responses" : { - "204" : { - "description" : "Success", + "201" : { + "description" : "Created", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2230,7 +1945,7 @@ } }, "400" : { - "description" : "Bad Request" + "description" : "Not Valid" }, "401" : { "description" : "Not Authorized" @@ -2238,34 +1953,29 @@ "404" : { "description" : "Not Found" }, - "409" : { - "description" : "Duplicate Name" - }, "500" : { "description" : "Internal Server Error" } } } }, - "/microservices/yaml/{uuid}" : { - "patch" : { + "/microservices/system/{uuid}/port-mapping" : { + "post" : { "tags" : [ "Microservices" ], - "summary" : "Updates a microservice", - "operationId" : "updateMicroserviceYAML", + "summary" : "Creates a port mapping for system microservice", + "operationId" : "createSystemMicroservicePortMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { - "204" : { - "description" : "Success", + "201" : { + "description" : "Created", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2273,7 +1983,7 @@ } }, "400" : { - "description" : "Bad Request" + "description" : "Not Valid" }, "401" : { "description" : "Not Authorized" @@ -2281,49 +1991,40 @@ "404" : { "description" : "Not Found" }, - "409" : { - "description" : "Duplicate Name" - }, "500" : { "description" : "Internal Server Error" } } } }, - "/microservices/{uuid}/routes/{receiverUuid}" : { - "post" : { + "/microservices/{uuid}/port-mapping/{internalPort}" : { + "delete" : { "tags" : [ "Microservices" ], - "summary" : "Creates a route from microservice to receiver", - "operationId" : "createMicroserviceRoute", + "summary" : "Deletes a port mapping for microservice", + "operationId" : "deleteMicroservicePortMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true }, { - "name" : "receiverUuid", + "name" : "internalPort", "in" : "path", - "description" : "Receiver Microservice Uuid", + "description" : "Internal Port", "required" : true } ], - "deprecated" : true, + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { - "description" : "Created", + "description" : "Success", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" } } }, - "400" : { - "description" : "Not Valid" - }, "401" : { "description" : "Not Authorized" }, @@ -2334,28 +2035,27 @@ "description" : "Internal Server Error" } } - }, + } + }, + "/microservices/system/{uuid}/port-mapping/{internalPort}" : { "delete" : { "tags" : [ "Microservices" ], - "summary" : "Deletes a route microservice", - "operationId" : "deleteMicroserviceRoute", + "summary" : "Deletes a port mapping for system microservice", + "operationId" : "deleteSystemMicroservicePortMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true }, { - "name" : "receiverUuid", + "name" : "internalPort", "in" : "path", - "description" : "Receiver Microservice Uuid", + "description" : "Internal Port", "required" : true } ], - "deprecated" : true, + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -2365,9 +2065,6 @@ } } }, - "400" : { - "description" : "Not Valid" - }, "401" : { "description" : "Not Authorized" }, @@ -2380,25 +2077,23 @@ } } }, - "/microservices/{uuid}/port-mapping" : { + "/microservices/{uuid}/volume-mapping" : { "get" : { "tags" : [ "Microservices" ], - "summary" : "Get a port mapping list for microservice", - "operationId" : "getMicroservicePortMapping", + "summary" : "Get a volume mapping list for microservice", + "operationId" : "getMicroserviceVolumeMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { - "description" : "Created", + "description" : "Success", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2418,19 +2113,17 @@ }, "post" : { "tags" : [ "Microservices" ], - "summary" : "Creates a port mapping for microservice", - "operationId" : "createMicroservicePortMapping", + "summary" : "Creates a volume mapping for microservice", + "operationId" : "createMicroserviceVolumeMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "201" : { "description" : "Created", @@ -2455,36 +2148,32 @@ } } }, - "/microservices/{uuid}/port-mapping/{internalPort}" : { - "delete" : { + "/microservices/system/{uuid}/volume-mapping" : { + "post" : { "tags" : [ "Microservices" ], - "summary" : "Deletes a port mapping for microservice", - "operationId" : "deleteMicroservicePortMapping", + "summary" : "Creates a volume mapping for system microservice", + "operationId" : "createSystemMicroserviceVolumeMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true - }, { - "name" : "internalPort", - "in" : "path", - "description" : "Internal Port", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { - "204" : { - "description" : "Success", + "201" : { + "description" : "Created", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" } } }, + "400" : { + "description" : "Not Valid" + }, "401" : { "description" : "Not Authorized" }, @@ -2497,60 +2186,28 @@ } } }, - "/microservices/{uuid}/volume-mapping" : { - "get" : { - "tags" : [ "Microservices" ], - "summary" : "Get a volume mapping list for microservice", - "operationId" : "getMicroserviceVolumeMapping", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { - "name" : "uuid", - "in" : "path", - "description" : "Microservice Uuid", - "required" : true - } ], - "responses" : { - "200" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "401" : { - "description" : "Not Authorized" - }, - "404" : { - "description" : "Not Found" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "post" : { - "tags" : [ "Microservices" ], - "summary" : "Creates a volume mapping for microservice", - "operationId" : "createMicroserviceVolumeMapping", + "/microservices/{uuid}/volume-mapping/{id}" : { + "delete" : { + "tags" : [ "Microservices" ], + "summary" : "Deletes a volume mapping for microservice", + "operationId" : "deleteMicroserviceVolumeMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", "required" : true + }, { + "name" : "id", + "in" : "path", + "description" : "Volume id", + "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { - "201" : { - "description" : "Created", + "204" : { + "description" : "Success", "headers" : { "X-Timestamp" : { "description" : "FogController server timestamp" @@ -2572,17 +2229,12 @@ } } }, - "/microservices/{uuid}/volume-mapping/{id}" : { + "/microservices/system/{uuid}/volume-mapping/{id}" : { "delete" : { "tags" : [ "Microservices" ], - "summary" : "Deletes a volume mapping for microservice", - "operationId" : "deleteMicroserviceVolumeMapping", + "summary" : "Deletes a volume mapping for system microservice", + "operationId" : "deleteSystemMicroserviceVolumeMapping", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice Uuid", @@ -2593,6 +2245,9 @@ "description" : "Volume id", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -2623,16 +2278,14 @@ "summary" : "Download image snapshot", "operationId" : "downloadImageSnapshot", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice UUID", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -2658,16 +2311,14 @@ "summary" : "Send request to create image snapshot", "operationId" : "createImageSnapshot", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice UUID", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "201" : { "description" : "Created", @@ -2695,11 +2346,6 @@ "summary" : "Gets Strace Data for Microservice", "operationId" : "getMicroserviceStrace", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice UUID", @@ -2709,6 +2355,9 @@ "in" : "query", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -2738,11 +2387,9 @@ "in" : "path", "description" : "Microservice UUID", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { @@ -2772,16 +2419,14 @@ "summary" : "Enables Microservice Strace Option", "operationId" : "enableMicroserviceStrace", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "uuid", "in" : "path", "description" : "Microservice UUID", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -2816,11 +2461,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -2851,11 +2494,9 @@ "in" : "path", "description" : "ioFog node id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { @@ -2886,11 +2527,9 @@ "tags" : [ "Registries" ], "summary" : "Gets list of registries", "operationId" : "getRegistryList", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -2913,11 +2552,9 @@ "tags" : [ "Registries" ], "summary" : "Creates new registry", "operationId" : "createRegistry", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "201" : { @@ -2950,11 +2587,9 @@ "in" : "path", "description" : "Registry id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { @@ -2985,11 +2620,9 @@ "in" : "path", "description" : "Registry id", "required" : true - }, { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true + } ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { @@ -3039,66 +2672,14 @@ } } }, - "/user/logout" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Logout", - "operationId" : "logout", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true - } ], - "responses" : { - "204" : { - "description" : "Success" - }, - "401" : { - "description" : "Not Authorized" - }, - "500" : { - "description" : "Internal Server Error" - } - } - } - }, - "/user/signup" : { + "/user/refresh" : { "post" : { "tags" : [ "User" ], - "summary" : "Signup", - "operationId" : "signup", + "summary" : "Refresh accessToken with refreshToken", + "operationId" : "refresh", "parameters" : [ ], "responses" : { - "201" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" - }, - "500" : { - "description" : "Internal Server Error" - } - } - } - }, - "/user/signup/resend-activation" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Resend activation email", - "operationId" : "resendActivationEmail", - "parameters" : [ { - "name" : "email", - "in" : "query", - "required" : true - } ], - "responses" : { - "204" : { + "200" : { "description" : "Success", "headers" : { "X-Timestamp" : { @@ -3107,117 +2688,26 @@ } }, "400" : { - "description" : "Bad Request" + "description" : "bad request" }, - "500" : { - "description" : "Internal Server Error" + "401" : { + "description" : "incorrect credentials" } } } }, - "/user/activate" : { + "/user/logout" : { "post" : { "tags" : [ "User" ], - "summary" : "Activate account", - "operationId" : "activateAccount", + "summary" : "Logout", + "operationId" : "logout", "parameters" : [ ], - "responses" : { - "303" : { - "description" : "Redirect to login page", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - }, - "Location" : { - "description" : "Login page url" - } - } - }, - "404" : { - "description" : "Not Found" - }, - "500" : { - "description" : "Internal Server Error" - } - } - } - }, - "/user/profile" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current user profile data", - "operationId" : "getUserProfile", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User token", - "required" : true - } ], - "responses" : { - "200" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "401" : { - "description" : "Not Authorized" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "delete" : { - "tags" : [ "User" ], - "summary" : "Delete account", - "operationId" : "deleteAccount", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "security" : [ { + "userToken" : [ ] } ], "responses" : { "204" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "401" : { - "description" : "Not Authorized" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Update user profile", - "operationId" : "updateUserProfile", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - } ], - "responses" : { - "200" : { - "description" : "Updated user profile", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "400" : { - "description" : "Bad Request" + "description" : "Success" }, "401" : { "description" : "Not Authorized" @@ -3228,41 +2718,17 @@ } } }, - "/user/password" : { - "delete" : { + "/user/profile" : { + "get" : { "tags" : [ "User" ], - "summary" : "Reset password", - "operationId" : "resetPassword", + "summary" : "Get current user profile data", + "operationId" : "getUserProfile", "parameters" : [ ], - "responses" : { - "204" : { - "description" : "Success", - "headers" : { - "X-Timestamp" : { - "description" : "FogController server timestamp" - } - } - }, - "404" : { - "description" : "Not Found" - }, - "500" : { - "description" : "Internal Server Error" - } - } - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "change password", - "operationId" : "changePassword", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "security" : [ { + "userToken" : [ ] } ], "responses" : { - "204" : { + "200" : { "description" : "Success", "headers" : { "X-Timestamp" : { @@ -3270,9 +2736,6 @@ } } }, - "400" : { - "description" : "Bad Request" - }, "401" : { "description" : "Not Authorized" }, @@ -3287,11 +2750,9 @@ "tags" : [ "Routing" ], "summary" : "Get routes", "operationId" : "getRoutes", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -3314,11 +2775,9 @@ "tags" : [ "Routing" ], "summary" : "Creates a new route", "operationId" : "createRoute", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "201" : { @@ -3350,16 +2809,14 @@ "summary" : "Gets a route info", "operationId" : "getRoute", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Route name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Route Info", @@ -3385,16 +2842,14 @@ "summary" : "Deletes a route", "operationId" : "deleteRoute", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Route name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -3420,16 +2875,14 @@ "summary" : "Updates a route", "operationId" : "updateRoute", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Route name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -3462,11 +2915,9 @@ "tags" : [ "Edge Resource" ], "summary" : "Get Edge Resources", "operationId" : "getEdgeResources", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -3492,11 +2943,6 @@ "summary" : "Get Specific Edge Resource", "operationId" : "getEdgeResourceDetail", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource name", @@ -3507,6 +2953,9 @@ "description" : "Edge Resource version", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -3529,11 +2978,6 @@ "summary" : "Update/Create Specific Edge Resource", "operationId" : "putEdgeResource", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource name", @@ -3544,6 +2988,9 @@ "description" : "Edge Resource version", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -3566,11 +3013,6 @@ "summary" : "Deletes an Edge Resource", "operationId" : "deleteEdgeResource", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource name", @@ -3581,6 +3023,9 @@ "description" : "Edge Resource version", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -3608,16 +3053,14 @@ "summary" : "Get Specific Edge Resource versions", "operationId" : "getEdgeResourceVersions", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource name", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "200" : { "description" : "Success", @@ -3641,11 +3084,9 @@ "tags" : [ "Edge Resource" ], "summary" : "Create Specific Edge Resource", "operationId" : "postEdgeResource", - "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true + "parameters" : [ ], + "security" : [ { + "userToken" : [ ] } ], "responses" : { "200" : { @@ -3671,11 +3112,6 @@ "summary" : "Attach Edge Resource to Agent", "operationId" : "postEdgeResourceLink", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource Name", @@ -3686,6 +3122,9 @@ "description" : "Edge Resource Version", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", @@ -3711,11 +3150,6 @@ "summary" : "Detach Edge Resource from Agent", "operationId" : "deleteEdgeResourceLink", "parameters" : [ { - "name" : "Authorization", - "in" : "header", - "description" : "User Token", - "required" : true - }, { "name" : "name", "in" : "path", "description" : "Edge Resource Name", @@ -3726,6 +3160,9 @@ "description" : "Edge Resource Version", "required" : true } ], + "security" : [ { + "userToken" : [ ] + } ], "responses" : { "204" : { "description" : "Success", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4c055721d..2c62aac3e 100755 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,7 +1,7 @@ -swagger: "3.0.0" +openapi : "3.0.0" info: - version: 3.0.0 - title: ioFog Controller + version: 3.5.10 + title: Datasance PoT Controller paths: /status: get: @@ -18,21 +18,6 @@ paths: $ref: "#/components/schemas/ServiceStatusResponse" "500": description: Internal Server Error - /email-activation: - get: - tags: - - Controller - summary: Returns email activation status - operationId: getEmailActivationStatus - responses: - "200": - description: Email activation status - content: - application/json: - schema: - $ref: "#/components/schemas/EmailActivationStatusResponse" - "500": - description: Internal Server Error /fog-types: get: tags: @@ -59,13 +44,8 @@ paths: - ioFog summary: Returns list of ioFog nodes operationId: getIOFogNodes - parameters: - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -95,13 +75,8 @@ paths: - ioFog summary: Creates a new ioFog node operationId: createIOFogNode - parameters: - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/UpdateIOFogNodeRequestBody" responses: @@ -135,12 +110,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/UpdateIOFogNodeRequestBody" responses: @@ -171,12 +142,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "202": description: Accepted @@ -203,12 +170,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -240,12 +203,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "201": description: Success @@ -271,12 +230,6 @@ paths: summary: Set change version command operationId: setVersionCommand parameters: - - in: header - name: Authorization - description: User token - required: true - schema: - type: string - in: path name: uuid description: ioFog node id @@ -292,6 +245,8 @@ paths: enum: - upgrade - rollback + security: + - authToken: [] responses: "204": description: Success @@ -315,18 +270,14 @@ paths: summary: remote reboot fog agent operationId: setRebootCommand parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: ioFog node id required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -343,21 +294,17 @@ paths: post: tags: - ioFog - summary: prune reboot fog agent + summary: prune remote fog agent operationId: setPruneCommand parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: ioFog node id required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -383,12 +330,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -420,12 +363,8 @@ paths: required: true schema: type: string - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -450,13 +389,8 @@ paths: - Application summary: Lists all applications operationId: listApplication - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -475,27 +409,14 @@ paths: description: Not Found "500": description: Internal Server Error - post: + /application/system: + get: tags: - Application - summary: Creates an application - operationId: createApplication - deprecated: true - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - oneOf: - - $ref: "#/components/schemas/ApplicationCreateRequest" - - $ref: "#/components/schemas/ApplicationCreateFromTemplateRequest" - required: true + summary: Lists all system applications + operationId: listSystemApplication + security: + - authToken: [] responses: "200": description: Success @@ -507,34 +428,28 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ApplicationCreateResponse" + $ref: "#/components/schemas/ApplicationListResponse" "400": description: Bad Request + "404": + description: Not Found "500": description: Internal Server Error - /application/yaml: - post: + /application/system/{name}: + get: tags: - Application - summary: Creates an application using a YAML file - operationId: createApplicationYAML + summary: Gets an application details + operationId: getApplication parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: name + description: Application name required: true schema: type: string - requestBody: - required: true - content: - application/yaml: - schema: - type: object - properties: - application: - type: string - format: binary + security: + - authToken: [] responses: "200": description: Success @@ -546,38 +461,27 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ApplicationCreateResponse" + $ref: "#/components/schemas/ApplicationGetResponse" "400": description: Bad Request + "404": + description: Not Found "500": description: Internal Server Error - - "/application/{name}": - put: + delete: tags: - Application - summary: Updates an application - deprecated: true - operationId: updateApplication + summary: Deletes a system application + operationId: deleteSystemApplication parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application name required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/ApplicationCreateRequest" - required: true + security: + - authToken: [] responses: "204": description: Success @@ -592,24 +496,56 @@ paths: description: Not Found "500": description: Internal Server Error + + /application/yaml: + post: + tags: + - Application + summary: Creates an application using a YAML file + operationId: createApplicationYAML + security: + - authToken: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + application: + type: string + format: binary + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/ApplicationCreateResponse" + "400": + description: Bad Request + "500": + description: Internal Server Error + "/application/{name}": patch: tags: - Application summary: Updates an application metadata operationId: patchApplication parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application name required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -643,18 +579,14 @@ paths: summary: Deletes an application operationId: deleteApplication parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application name required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -675,18 +607,14 @@ paths: summary: Gets an application details operationId: getApplication parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application name required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -712,22 +640,18 @@ paths: summary: Updates an application using a YAML file operationId: updateApplicationYAML parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application name required: true schema: type: string + security: + - authToken: [] requestBody: required: true content: - application/yaml: + multipart/form-data: schema: type: object properties: @@ -754,13 +678,8 @@ paths: - Application Template summary: Lists all application templates operationId: listApplicationTemplates - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -779,55 +698,18 @@ paths: description: Not Found "500": description: Internal Server Error - /applicationTemplate: - post: - tags: - - Application Template - summary: Creates an application template - operationId: createApplicationTemplate - deprecated: true - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - requestBody: - $ref: "#/components/requestBodies/ApplicationTemplateCreateRequest" - responses: - "200": - description: Success - headers: - X-Timestamp: - description: FogController server timestamp - schema: - type: number - content: - application/json: - schema: - $ref: "#/components/schemas/ApplicationTemplateCreateResponse" - "400": - description: Bad Request - "500": - description: Internal Server Error /applicationTemplate/yaml: post: tags: - Application Template summary: Creates an application template using a YAML file operationId: createApplicationTemplateYAML - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: required: true content: - application/yaml: + multipart/form-data: schema: type: object properties: @@ -851,61 +733,20 @@ paths: "500": description: Internal Server Error "/applicationTemplate/{name}": - put: - tags: - - Application Template - summary: Updates or creates an application template - operationId: updateOrCreateApplicationTemplate - deprecated: true - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - - in: path - name: name - description: Application template name - required: true - schema: - type: string - requestBody: - $ref: "#/components/requestBodies/ApplicationTemplateCreateRequest" - responses: - "200": - description: Success - headers: - X-Timestamp: - description: FogController server timestamp - schema: - type: number - content: - application/json: - schema: - $ref: "#/components/schemas/ApplicationTemplateCreateResponse" - "400": - description: Bad Request - "500": - description: Internal Server Error patch: tags: - Application Template summary: Patches an application template operationId: patchApplicationTemplate parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application template name required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -932,18 +773,14 @@ paths: summary: Deletes an application template operationId: deleteApplicationTemplate parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application template name required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -964,18 +801,14 @@ paths: summary: Gets an application template operationId: getApplicationTemplate parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application template name required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -1001,22 +834,18 @@ paths: summary: Updates or creates an application template operationId: updateOrCreateApplicationTemplateFromYaml parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Application template name required: true schema: type: string + security: + - authToken: [] requestBody: required: true content: - application/yaml: + multipart/form-data: schema: type: object properties: @@ -1098,19 +927,36 @@ paths: description: Not Authorized "500": description: Internal Server Error + /agent/cert: + get: + tags: + - Agent + summary: Move Controller CA to Agent + operationId: agentControllerCert + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error /agent/config: get: tags: - Agent summary: Get an ioFog node configuration operationId: getIOFogNodeConfig - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1132,13 +978,8 @@ paths: - Agent summary: Updates an ioFog node configuration operationId: updateIOFogNodeConfig - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -1159,22 +1000,45 @@ paths: description: Not Authorized "500": description: Internal Server Error - /agent/config/changes: - get: + /agent/config/gps: + patch: tags: - Agent - summary: Gets ioFog node changes - operationId: getIOFogNodeChanges - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string - responses: - "200": - description: Success + summary: Updates an ioFog node GPS configuration + operationId: updateIOFogNodeGps + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IOFogNodeGpsRequest" + required: true + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error + /agent/config/changes: + get: + tags: + - Agent + summary: Gets ioFog node changes + operationId: getIOFogNodeChanges + security: + - authToken: [] + responses: + "200": + description: Success headers: X-Timestamp: description: FogController server timestamp @@ -1195,13 +1059,8 @@ paths: - Agent summary: Resets ioFog node changes list operationId: resetIOFogNodeChanges - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1222,13 +1081,8 @@ paths: - Agent summary: Posts agent status to ioFog node operationId: postAgentStatus - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -1255,13 +1109,8 @@ paths: - Agent summary: Gets microservices running on an ioFog node operationId: getAgentMicroservicesList - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1291,12 +1140,8 @@ paths: description: Microservice UUID schema: type: string - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1321,13 +1166,8 @@ paths: - Agent summary: Gets list of Docker registries operationId: getRegistriesList - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1350,13 +1190,8 @@ paths: - Agent summary: Get an ioFog node tunnel configuration operationId: getIOFogNodeTunnelConfig - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1381,13 +1216,8 @@ paths: - Agent summary: Get an ioFog node strace info operationId: getIOFogNodeStraceInfo - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1411,13 +1241,8 @@ paths: - Agent summary: Posts agent strace to ioFog node operationId: postIOFogNodeStraceBuffer - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -1446,13 +1271,8 @@ paths: - Agent summary: Get change version command operationId: getChangeVersion - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1477,13 +1297,8 @@ paths: - Agent summary: Updates HAL hardware info operationId: putHalHardwareInfo - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/HalInfo" responses: @@ -1506,13 +1321,8 @@ paths: - ioFog summary: Retrieves HAL USB info operationId: getAgentHalUsbInfo - parameters: - - in: header - name: Authorization - description: User token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1536,13 +1346,8 @@ paths: - Agent summary: Updates HAL USB info operationId: putHalUsbInfo - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/HalInfo" responses: @@ -1565,13 +1370,8 @@ paths: - Agent summary: Deletes an ioFog node operationId: deleteAgentNode - parameters: - - in: header - name: Authorization - description: Agent token - required: true - schema: - type: string + security: + - authToken: [] responses: "204": description: No Content @@ -1590,13 +1390,8 @@ paths: - Agent summary: Get image snapshot info operationId: getImageSnapshot - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1620,13 +1415,8 @@ paths: - Agent summary: Put image snapshot info on controller operationId: putImageSnapshot - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -1653,13 +1443,8 @@ paths: - Agent summary: Post tracking info operationId: postTracking - parameters: - - in: header - name: Authorization - description: Agent Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -1679,13 +1464,8 @@ paths: - Catalog summary: Gets microservices catalog operationId: getMicroservicesCatalog - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -1707,13 +1487,8 @@ paths: - Catalog summary: Creates a new microservice catalog item operationId: createMicroserviceCatalogItem - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/CreateUpdateCatalogItemRequestBody" responses: @@ -1746,18 +1521,14 @@ paths: summary: Gets microservice catalog item info operationId: getMicroserviceCatalogItem parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: id description: Catalog Item Id required: true schema: type: string + security: + - authToken: [] responses: "200": description: Catalog Item Info @@ -1782,18 +1553,14 @@ paths: summary: Updates a microservice catalog item operationId: updateMicroserviceCatalogItem parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: id description: Catalog Item Id required: true schema: type: string + security: + - authToken: [] requestBody: $ref: "#/components/requestBodies/CreateUpdateCatalogItemRequestBody" responses: @@ -1820,18 +1587,14 @@ paths: summary: Deletes a microservice catalog item operationId: deleteMicroserviceCatalogItem parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: id description: Catalog Item Id required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -1846,20 +1609,28 @@ paths: description: Invalid Catalog Item Id "500": description: Internal Server Error - /flow: + /microservices: get: tags: - - Flow - summary: Gets list of flows - deprecated: true - operationId: getFlowsList + - Microservices + summary: Gets list of microservices + operationId: getMicroservicesList parameters: - - in: header - name: Authorization - description: User Token - required: true + - in: query + name: flowId + deprecated: true + description: Flow Id + required: false + schema: + type: integer + - in: query + name: application + description: Application name + required: false schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -1871,26 +1642,29 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/GetFlowsResponse" + $ref: "#/components/schemas/GetMicroservicesResponse" "401": description: Not Authorized "500": description: Internal Server Error + /microservices/yaml: post: tags: - - Flow - summary: Creates a new flow - deprecated: true - operationId: createFlow - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + - Microservices + summary: Creates a new microservice in an Application + operationId: createMicroserviceYAML + security: + - authToken: [] requestBody: - $ref: "#/components/requestBodies/NewFlowRequest" + required: true + content: + multipart/form-data: + schema: + type: object + properties: + microsoervice: + type: string + format: binary responses: "201": description: Created @@ -1904,34 +1678,31 @@ paths: schema: type: object properties: - id: + uuid: type: string "400": description: Bad Request "401": description: Not Authorized + "409": + description: Duplicate Name "500": description: Internal Server Error - "/flow/{id}": + "/microservices/{uuid}": get: tags: - - Flow - summary: Gets flow info - operationId: getFlowInfo - deprecated: true + - Microservices + summary: Gets a microservice info + operationId: getMicroserviceInfo parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path - name: id - description: Flow Id + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -1943,34 +1714,27 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/FlowInfoResponse" + $ref: "#/components/schemas/MicroserviceInfoResponse" "401": description: Not Authorized "404": - description: Invalid Flow Id + description: Not Found "500": description: Internal Server Error - patch: + delete: tags: - - Flow - summary: Updates a flow - operationId: updateFlow - deprecated: true + - Microservices + summary: Deletes a microservice + operationId: deleteMicroservice parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path - name: id - description: Flow Id + name: uuid + description: Microservice Uuid required: true schema: type: string - requestBody: - $ref: "#/components/requestBodies/NewFlowRequest" + security: + - authToken: [] responses: "204": description: Success @@ -1984,68 +1748,54 @@ paths: "401": description: Not Authorized "404": - description: Invalid Flow Id + description: Not Found "500": description: Internal Server Error - delete: + patch: tags: - - Flow - summary: Deletes a flow - operationId: deleteFlow - deprecated: true + - Microservices + summary: Updates a microservice + operationId: updateMicroservice parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path - name: id - description: Flow Id + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] responses: "204": - description: Success + description: Updated headers: X-Timestamp: description: FogController server timestamp schema: type: number + "400": + description: Bad Request "401": description: Not Authorized "404": - description: Invalid Flow Id + description: Invalid Registry Id "500": description: Internal Server Error - /microservices: + /microservices/pub/{tag}: get: tags: - Microservices - summary: Gets list of microservices - operationId: getMicroservicesList + summary: Get list of microservices with filtered pub tag + operationId: listMicroserviceInfoByPubTag parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: tag + description: Microservice Pub Tags required: true schema: type: string - - in: query - name: flowId - deprecated: true - description: Flow Id - required: false - schema: - type: integer - - in: query - name: application - description: Application name - required: false - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -2062,29 +1812,24 @@ paths: description: Not Authorized "500": description: Internal Server Error - post: + /microservices/sub/{tag}: + get: tags: - Microservices - summary: Creates a new microservice on an ioFog node - operationId: createMicroservice - deprecated: true + summary: Get list of microservices with filtered sub tag + operationId: listMicroserviceInfoBySubTag parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: tag + description: Microservice Pub Tags required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/NewMicroserviceRequest" - description: New Microservice Info - required: true + security: + - authToken: [] responses: - "201": - description: Created + "200": + description: Success headers: X-Timestamp: description: FogController server timestamp @@ -2093,44 +1838,36 @@ paths: content: application/json: schema: - type: object - properties: - uuid: - type: string - "400": - description: Bad Request + $ref: "#/components/schemas/GetMicroservicesResponse" "401": description: Not Authorized - "409": - description: Duplicate Name "500": description: Internal Server Error - /microservices/yaml: - post: + /microservices/system: + get: tags: - Microservices - summary: Creates a new microservice in an Application - operationId: createMicroserviceYAML + summary: Gets list of system microservices + operationId: getSystemMicroservicesList + security: + - authToken: [] parameters: - - in: header - name: Authorization - description: User Token - required: true + - in: query + name: flowId + deprecated: true + description: Flow Id + required: false + schema: + type: integer + - in: query + name: application + description: Application name + required: false schema: type: string - requestBody: - required: true - content: - application/yaml: - schema: - type: object - properties: - microsoervice: - type: string - format: binary responses: - "201": - description: Created + '200': + description: Success headers: X-Timestamp: description: FogController server timestamp @@ -2141,35 +1878,38 @@ paths: schema: type: object properties: - uuid: - type: string - "400": - description: Bad Request - "401": + microservices: + type: array + items: + type: object + properties: + uuid: + type: string + name: + type: string + config: + type: object + '401': description: Not Authorized - "409": - description: Duplicate Name - "500": + '404': + description: Not Found + '500': description: Internal Server Error - "/microservices/{uuid}": + /microservices/system/{uuid}: get: tags: - Microservices - summary: Gets a microservice info - operationId: getMicroserviceInfo + summary: Gets a system microservice info + operationId: getSystemMicroserviceInfo parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -2191,29 +1931,58 @@ paths: patch: tags: - Microservices - summary: Updates a microservice - deprecated: true - operationId: updateMicroservice + summary: Updates a system microservice + operationId: updateSystemMicroservice parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + responses: + "204": + description: Updated + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Invalid Registry Id + "500": + description: Internal Server Error + /microservices/yaml/{uuid}: + patch: + tags: + - Microservices + summary: Updates a microservice + operationId: updateMicroserviceYAML + parameters: - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] requestBody: + required: true content: - application/json: + multipart/form-data: schema: - $ref: "#/components/schemas/UpdateMicroserviceRequest" - description: Microservice Info - required: true + type: object + properties: + microservice: + type: string + format: binary responses: "204": description: Success @@ -2232,72 +2001,63 @@ paths: description: Duplicate Name "500": description: Internal Server Error - delete: + /microservices/{uuid}/config: + get: tags: - Microservices - summary: Deletes a microservice - operationId: deleteMicroservice + summary: Gets a microservice config + operationId: getMicroserviceConfig parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] responses: - "204": + "200": description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - "400": - description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/microservicesConfig" "401": description: Not Authorized "404": description: Not Found "500": description: Internal Server Error - /microservices/yaml/{uuid}: patch: tags: - Microservices - summary: Updates a microservice - operationId: updateMicroserviceYAML + summary: Updates a microservice config + operationId: updateMicroserviceConfig parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] requestBody: - required: true content: - application/yaml: + application/json: schema: - type: object - properties: - microservice: - type: string - format: binary + $ref: "#/components/schemas/microservicesConfig" + description: information about microservice config + required: true responses: "204": - description: Success + description: Updated headers: X-Timestamp: description: FogController server timestamp @@ -2309,47 +2069,100 @@ paths: description: Not Authorized "404": description: Not Found - "409": - description: Duplicate Name "500": description: Internal Server Error - - "/microservices/{uuid}/routes/{receiverUuid}": - post: + delete: tags: - Microservices - summary: Creates a route from microservice to receiver - operationId: createMicroserviceRoute - deprecated: true + summary: Deletes a microservice config + operationId: deleteMicroserviceConfig parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /microservices/system/{uuid}/config: + get: + tags: + - Microservices + summary: Gets a system microservice config + operationId: getSystemMicroserviceConfig + parameters: - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/microservicesConfig" + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + patch: + tags: + - Microservices + summary: Updates a system microservice config + operationId: updateSystemMicroserviceConfig + parameters: - in: path - name: receiverUuid - description: Receiver Microservice Uuid + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/microservicesConfig" + description: information about microservice config + required: true responses: "204": - description: Created + description: Updated headers: X-Timestamp: description: FogController server timestamp schema: type: number "400": - description: Not Valid + description: Bad Request "401": description: Not Authorized "404": @@ -2359,28 +2172,17 @@ paths: delete: tags: - Microservices - summary: Deletes a route microservice - operationId: deleteMicroserviceRoute - deprecated: true + summary: Deletes a system microservice config + operationId: deleteSystemMicroserviceConfig parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string - - in: path - name: receiverUuid - description: Receiver Microservice Uuid - required: true - schema: - type: string + security: + - authToken: [] responses: "204": description: Success @@ -2389,8 +2191,6 @@ paths: description: FogController server timestamp schema: type: number - "400": - description: Not Valid "401": description: Not Authorized "404": @@ -2404,18 +2204,14 @@ paths: summary: Creates a port mapping for microservice operationId: createMicroservicePortMapping parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -2449,20 +2245,56 @@ paths: summary: Get a port mapping list for microservice operationId: getMicroservicePortMapping parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + responses: + "200": + description: Created + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/PortMappingsListResponse" + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /microservices/system/{uuid}/port-mapping: + post: + tags: + - Microservices + summary: Creates a port mapping for system microservice + operationId: createSystemMicroservicePortMapping + parameters: - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PortMappingsRequest" + description: information about port mapping + required: true responses: - "200": + "201": description: Created headers: X-Timestamp: @@ -2472,7 +2304,9 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/PortMappingsListResponse" + $ref: "#/components/schemas/PortMappingsPublicResponse" + "400": + description: Not Valid "401": description: Not Authorized "404": @@ -2486,12 +2320,41 @@ paths: summary: Deletes a port mapping for microservice operationId: deleteMicroservicePortMapping parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid + required: true + schema: + type: string + - in: path + name: internalPort + description: Internal Port required: true schema: type: string + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /microservices/system/{uuid}/port-mapping/{internalPort}: + delete: + tags: + - Microservices + summary: Deletes a port mapping for system microservice + operationId: deleteSystemMicroservicePortMapping + parameters: - in: path name: uuid description: Microservice Uuid @@ -2504,6 +2367,8 @@ paths: required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -2525,18 +2390,14 @@ paths: summary: Creates a volume mapping for microservice operationId: createMicroserviceVolumeMapping parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -2573,18 +2434,14 @@ paths: summary: Get a volume mapping list for microservice operationId: getMicroserviceVolumeMapping parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -2603,153 +2460,140 @@ paths: description: Not Found "500": description: Internal Server Error - "/microservices/{uuid}/volume-mapping/{id}": - delete: + "/microservices/system/{uuid}/volume-mapping": + post: tags: - Microservices - summary: Deletes a volume mapping for microservice - operationId: deleteMicroserviceVolumeMapping + summary: Creates a volume mapping for system microservice + operationId: createSystemMicroserviceVolumeMapping parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid description: Microservice Uuid required: true schema: type: string - - in: path - name: id - description: Volume id - required: true - schema: - type: string + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMapping" + description: information about volume mapping + required: true responses: - "204": - description: Success + "201": + description: Created headers: X-Timestamp: description: FogController server timestamp schema: type: number - "400": - description: Not Valid + content: + application/json: + schema: + type: object + properties: + id: + type: number + "400": + description: Not Valid "401": description: Not Authorized "404": description: Not Found "500": description: Internal Server Error - "/microservices/{uuid}/image-snapshot": - post: + "/microservices/{uuid}/volume-mapping/{id}": + delete: tags: - - Diagnostics - summary: Send request to create image snapshot - operationId: createImageSnapshot + - Microservices + summary: Deletes a volume mapping for microservice + operationId: deleteMicroserviceVolumeMapping parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid required: true schema: type: string - in: path - name: uuid - description: Microservice UUID + name: id + description: Volume id required: true schema: type: string + security: + - authToken: [] responses: - "201": - description: Created + "204": + description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - content: - application/json: - schema: - type: object - properties: - id: - type: string + "400": + description: Not Valid "401": description: Not Authorized "404": - description: Invalid Microservice UUID + description: Not Found "500": description: Internal Server Error - get: + "/microservices/system/{uuid}/volume-mapping/{id}": + delete: tags: - - Diagnostics - summary: Download image snapshot - operationId: downloadImageSnapshot + - Microservices + summary: Deletes a volume mapping for system microservice + operationId: deleteSystemMicroserviceVolumeMapping parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice Uuid required: true schema: type: string - in: path - name: uuid - description: Microservice UUID + name: id + description: Volume id required: true schema: type: string + security: + - authToken: [] responses: - "200": + "204": description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - content: - application/gzip: - schema: - type: string - format: binary + "400": + description: Not Valid "401": description: Not Authorized "404": - description: Invalid Microservice UUID + description: Not Found "500": description: Internal Server Error - "/microservices/{uuid}/strace": + "/microservices/system/{uuid}/rebuild": patch: tags: - - Diagnostics - summary: Enables Microservice Strace Option - operationId: enableMicroserviceStrace + - Microservices + summary: Rebuilds a system microservice + operationId: rebuildSystemMicroservice parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid - description: Microservice UUID + description: Microservice Uuid required: true schema: type: string - requestBody: - content: - application/json: - schema: - type: object - properties: - enable: - type: boolean - description: Strace info to enable or disable feature - required: true + security: + - authToken: [] responses: "204": description: Success @@ -2758,85 +2602,56 @@ paths: description: FogController server timestamp schema: type: number - "400": - description: Bad Request "401": description: Not Authorized "404": - description: Invalid Microservice UUID + description: Not Found "500": description: Internal Server Error - get: + "/microservices/{uuid}/rebuild": + patch: tags: - - Diagnostics - summary: Gets Strace Data for Microservice - operationId: getMicroserviceStrace + - Microservices + summary: Rebuilds a microservice + operationId: rebuildMicroservice parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: uuid - description: Microservice UUID - required: true - schema: - type: string - - in: query - name: format + description: Microservice Uuid required: true schema: type: string - enum: - - file - - string + security: + - authToken: [] responses: - "200": + "204": description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - content: - application/json: - schema: - type: object - properties: - data: - type: string "401": description: Not Authorized "404": - description: Invalid Microservice UUID + description: Not Found "500": description: Internal Server Error - put: + "/microservices/{uuid}/start": + patch: tags: - - Diagnostics - summary: Posts Microservice Strace file to FTP - operationId: postMicroserviceStraceToFTP + - Microservices + summary: Starts a microservice + operationId: startMicroservice parameters: - in: path - required: true name: uuid - description: Microservice UUID - schema: - type: string - - in: header - name: Authorization - description: User Token + description: Microservice Uuid required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/MicroserviceStraceFTPBody" - required: true + security: + - authToken: [] responses: "204": description: Success @@ -2845,39 +2660,27 @@ paths: description: FogController server timestamp schema: type: number - "400": - description: Bad Request "401": description: Not Authorized "404": - description: Invalid Microservice UUID + description: Not Found "500": description: Internal Server Error - "/iofog/{uuid}/tunnel": + "/microservices/{uuid}/stop": patch: tags: - - Tunnel - summary: Opens/closes ssh tunnel - operationId: openIOFogNodeSshTunnel + - Microservices + summary: Stops a microservice + operationId: stopMicroservice parameters: - in: path name: uuid - description: ioFog node id - required: true - schema: - type: string - - in: header - name: Authorization - description: User Token + description: Microservice Uuid required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/ActionBody" - required: true + security: + - authToken: [] responses: "204": description: Success @@ -2886,132 +2689,115 @@ paths: description: FogController server timestamp schema: type: number - "400": - description: Bad Request "401": description: Not Authorized "404": - description: Invalid Node Id + description: Not Found "500": description: Internal Server Error - get: + "/microservices/{uuid}/exec": + post: tags: - - Tunnel - summary: Gets current info about ioFog node ssh tunnel status - operationId: getIOFogNodeSshTunnelStatusInfo + - Microservices + summary: Enables a exec for microservice + operationId: enableMicroserviceExec parameters: - in: path name: uuid - description: ioFog node id - required: true - schema: - type: string - - in: header - name: Authorization - description: User token + description: Microservice UUID required: true schema: type: string + security: + - authToken: [] responses: - "200": - description: Success + "201": + description: Created headers: X-Timestamp: description: FogController server timestamp schema: type: number - content: - application/json: - schema: - $ref: "#/components/schemas/IOFogNodeTunnelStatusInfoResponse" "401": description: Not Authorized "404": - description: Invalid Node Id + description: Invalid Microservice UUID "500": description: Internal Server Error - /registries: - post: + delete: tags: - - Registries - summary: Creates new registry - operationId: createRegistry + - Microservices + summary: Disables a exec for microservice + operationId: disableMicroserviceExec parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: uuid + description: Microservice UUID required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/RegistryBody" + security: + - authToken: [] responses: - "201": - description: Created + "204": + description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - "400": - description: Bad Request "401": description: Not Authorized + "404": + description: Invalid Microservice UUID "500": description: Internal Server Error - get: + "/microservices/system/{uuid}/exec": + post: tags: - - Registries - summary: Gets list of registries - operationId: getRegistryList + - Microservices + summary: Enables a exec for system microservice + operationId: enableSystemMicroserviceExec parameters: - - in: header - name: Authorization - description: User token + - in: path + name: uuid + description: Microservice UUID required: true schema: type: string + security: + - authToken: [] responses: - "200": - description: Success + "201": + description: Created headers: X-Timestamp: description: FogController server timestamp schema: type: number - content: - application/json: - schema: - $ref: "#/components/schemas/RegistriesListResponse" "401": description: Not Authorized + "404": + description: Invalid Microservice UUID "500": description: Internal Server Error - "/registries/{id}": delete: tags: - - Registries - summary: Deletes a registry - operationId: deleteRegistry + - Microservices + summary: Disables a exec for system microservice + operationId: disableSystemMicroserviceExec parameters: - in: path - name: id - description: Registry id - required: true - schema: - type: string - - in: header - name: Authorization - description: User token + name: uuid + description: Microservice UUID required: true schema: type: string + security: + - authToken: [] responses: "204": - description: Deleted + description: Success headers: X-Timestamp: description: FogController server timestamp @@ -3020,61 +2806,59 @@ paths: "401": description: Not Authorized "404": - description: Invalid Registry Id + description: Invalid Microservice UUID "500": description: Internal Server Error - patch: + "/microservices/{uuid}/image-snapshot": + post: tags: - - Registries - summary: Updates a registry - operationId: updateRegistry + - Diagnostics + summary: Send request to create image snapshot + operationId: createImageSnapshot parameters: - in: path - name: id - description: Registry id - required: true - schema: - type: string - - in: header - name: Authorization - description: User token + name: uuid + description: Microservice UUID required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/RegistryBody" - required: true + security: + - authToken: [] responses: - "204": - description: Updated + "201": + description: Created headers: X-Timestamp: description: FogController server timestamp schema: type: number - "400": - description: Bad Request + content: + application/json: + schema: + type: object + properties: + id: + type: string "401": description: Not Authorized "404": - description: Invalid Registry Id + description: Invalid Microservice UUID "500": description: Internal Server Error - /user/login: - post: + get: tags: - - User - summary: Login - operationId: login - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/LoginRequest" - required: true + - Diagnostics + summary: Download image snapshot + operationId: downloadImageSnapshot + parameters: + - in: path + name: uuid + description: Microservice UUID + required: true + schema: + type: string + security: + - authToken: [] responses: "200": description: Success @@ -3084,48 +2868,81 @@ paths: schema: type: number content: - application/json: + application/gzip: schema: - $ref: "#/components/schemas/LoginSuccessResponse" - "400": - description: bad request + type: string + format: binary "401": - description: incorrect credentials - /user/logout: - post: + description: Not Authorized + "404": + description: Invalid Microservice UUID + "500": + description: Internal Server Error + "/microservices/{uuid}/strace": + patch: tags: - - User - summary: Logout - operationId: logout + - Diagnostics + summary: Enables Microservice Strace Option + operationId: enableMicroserviceStrace parameters: - - in: header - name: Authorization - description: User token + - in: path + name: uuid + description: Microservice UUID required: true schema: type: string + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + enable: + type: boolean + description: Strace info to enable or disable feature + required: true responses: "204": description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request "401": description: Not Authorized + "404": + description: Invalid Microservice UUID "500": description: Internal Server Error - /user/signup: - post: + get: tags: - - User - summary: Signup - operationId: signup - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/SignupRequest" - description: new user data - required: true + - Diagnostics + summary: Gets Strace Data for Microservice + operationId: getMicroserviceStrace + parameters: + - in: path + name: uuid + description: Microservice UUID + required: true + schema: + type: string + - in: query + name: format + required: true + schema: + type: string + enum: + - file + - string + security: + - authToken: [] responses: - "201": + "200": description: Success headers: X-Timestamp: @@ -3135,23 +2952,36 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/SignupSuccessResponse" - "400": - description: Bad Request + type: object + properties: + data: + type: string + "401": + description: Not Authorized + "404": + description: Invalid Microservice UUID "500": description: Internal Server Error - /user/signup/resend-activation: - get: + put: tags: - - User - summary: Resend activation email - operationId: resendActivationEmail + - Diagnostics + summary: Posts Microservice Strace file to FTP + operationId: postMicroserviceStraceToFTP parameters: - - in: query - name: email + - in: path required: true + name: uuid + description: Microservice UUID schema: type: string + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MicroserviceStraceFTPBody" + required: true responses: "204": description: Success @@ -3162,50 +2992,63 @@ paths: type: number "400": description: Bad Request + "401": + description: Not Authorized + "404": + description: Invalid Microservice UUID "500": description: Internal Server Error - /user/activate: - post: + "/iofog/{uuid}/tunnel": + patch: tags: - - User - summary: Activate account - operationId: activateAccount + - Tunnel + summary: Opens/closes ssh tunnel + operationId: openIOFogNodeSshTunnel + parameters: + - in: path + name: uuid + description: ioFog node id + required: true + schema: + type: string + security: + - authToken: [] requestBody: content: application/json: schema: - $ref: "#/components/schemas/UserActivateRequest" - description: activation code + $ref: "#/components/schemas/ActionBody" required: true responses: - "303": - description: Redirect to login page + "204": + description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - Location: - description: Login page url - schema: - type: string + "400": + description: Bad Request + "401": + description: Not Authorized "404": - description: Not Found + description: Invalid Node Id "500": description: Internal Server Error - /user/profile: get: tags: - - User - summary: Get current user profile data - operationId: getUserProfile + - Tunnel + summary: Gets current info about ioFog node ssh tunnel status + operationId: getIOFogNodeSshTunnelStatusInfo parameters: - - in: header - name: Authorization - description: User token + - in: path + name: uuid + description: ioFog node id required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -3217,33 +3060,50 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UserProfileDetailsResponse" + $ref: "#/components/schemas/IOFogNodeTunnelStatusInfoResponse" "401": description: Not Authorized + "404": + description: Invalid Node Id "500": description: Internal Server Error - patch: + /registries: + post: tags: - - User - summary: Update user profile - operationId: updateUserProfile - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + - Registries + summary: Creates new registry + operationId: createRegistry + security: + - authToken: [] requestBody: content: application/json: schema: - $ref: "#/components/schemas/UserProfileUpdatesRequest" - description: Updated profile data - required: true + $ref: "#/components/schemas/RegistryBody" + responses: + "201": + description: Created + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error + get: + tags: + - Registries + summary: Gets list of registries + operationId: getRegistryList + security: + - authToken: [] responses: "200": - description: Updated user profile + description: Success headers: X-Timestamp: description: FogController server timestamp @@ -3252,34 +3112,29 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UserProfileDetailsResponse" - "400": - description: Bad Request + $ref: "#/components/schemas/RegistriesListResponse" "401": description: Not Authorized "500": description: Internal Server Error + "/registries/{id}": delete: tags: - - User - summary: Delete account - operationId: deleteAccount + - Registries + summary: Deletes a registry + operationId: deleteRegistry parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: id + description: Registry id required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/DeleteParameters" - description: parameters for delete + security: + - authToken: [] responses: "204": - description: Success + description: Deleted headers: X-Timestamp: description: FogController server timestamp @@ -3287,31 +3142,33 @@ paths: type: number "401": description: Not Authorized + "404": + description: Invalid Registry Id "500": description: Internal Server Error - /user/password: patch: tags: - - User - summary: change password - operationId: changePassword + - Registries + summary: Updates a registry + operationId: updateRegistry parameters: - - in: header - name: Authorization - description: User Token + - in: path + name: id + description: Registry id required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: schema: - $ref: "#/components/schemas/PasswordChangeRequest" - description: current and new password + $ref: "#/components/schemas/RegistryBody" required: true responses: "204": - description: Success + description: Updated headers: X-Timestamp: description: FogController server timestamp @@ -3321,30 +3178,103 @@ paths: description: Bad Request "401": description: Not Authorized + "404": + description: Invalid Registry Id "500": description: Internal Server Error - delete: + /user/login: + post: tags: - User - summary: Reset password - operationId: resetPassword + summary: Login + operationId: login requestBody: content: application/json: schema: - $ref: "#/components/schemas/PasswordResetRequest" - description: email + $ref: "#/components/schemas/LoginRequest" required: true responses: - "204": + "200": description: Success headers: X-Timestamp: description: FogController server timestamp schema: type: number - "404": - description: Not Found + content: + application/json: + schema: + $ref: "#/components/schemas/LoginSuccessResponse" + "400": + description: bad request + "401": + description: incorrect credentials + /user/refresh: + post: + tags: + - User + summary: Refresh accessToken with refreshToken + operationId: refresh + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshRequest" + required: true + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshSuccessResponse" + "400": + description: bad request + "401": + description: incorrect credentials + /user/logout: + post: + tags: + - User + summary: Logout + operationId: logout + security: + - authToken: [] + responses: + "204": + description: Success + "401": + description: Not Authorized + "500": + description: Internal Server Error + /user/profile: + get: + tags: + - User + summary: Get current user profile data + operationId: getUserProfile + security: + - authToken: [] + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/UserProfileDetailsResponse" + "401": + description: Not Authorized "500": description: Internal Server Error /routes: @@ -3353,13 +3283,8 @@ paths: - Routing summary: Get routes operationId: getRoutes - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -3381,13 +3306,8 @@ paths: - Routing summary: Creates a new route operationId: createRoute - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3425,18 +3345,14 @@ paths: summary: Gets a route info operationId: getRoute parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Route name required: true schema: type: string + security: + - authToken: [] responses: "200": description: Route Info @@ -3461,18 +3377,14 @@ paths: summary: Updates a route operationId: updateRoute parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Route name required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3504,18 +3416,14 @@ paths: summary: Deletes a route operationId: deleteRoute parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Route name required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -3536,13 +3444,8 @@ paths: - Edge Resource summary: Get Edge Resources operationId: getEdgeResources - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] responses: "200": description: Success @@ -3566,12 +3469,6 @@ paths: summary: Get Specific Edge Resource operationId: getEdgeResourceDetail parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource name @@ -3584,6 +3481,8 @@ paths: required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -3606,12 +3505,6 @@ paths: summary: Update/Create Specific Edge Resource operationId: putEdgeResource parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource name @@ -3624,6 +3517,8 @@ paths: required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3653,12 +3548,6 @@ paths: summary: Deletes an Edge Resource operationId: deleteEdgeResource parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource name @@ -3671,6 +3560,8 @@ paths: required: true schema: type: string + security: + - authToken: [] responses: "204": description: Success @@ -3692,18 +3583,14 @@ paths: summary: Get Specific Edge Resource versions operationId: getEdgeResourceVersions parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource name required: true schema: type: string + security: + - authToken: [] responses: "200": description: Success @@ -3726,13 +3613,8 @@ paths: - Edge Resource summary: Create Specific Edge Resource operationId: postEdgeResource - parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3763,12 +3645,6 @@ paths: summary: Attach Edge Resource to Agent operationId: postEdgeResourceLink parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource Name @@ -3781,6 +3657,8 @@ paths: required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3808,12 +3686,6 @@ paths: summary: Detach Edge Resource from Agent operationId: deleteEdgeResourceLink parameters: - - in: header - name: Authorization - description: User Token - required: true - schema: - type: string - in: path name: name description: Edge Resource Name @@ -3826,6 +3698,8 @@ paths: required: true schema: type: string + security: + - authToken: [] requestBody: content: application/json: @@ -3847,75 +3721,1522 @@ paths: description: Not Found "500": description: Internal Server Error -tags: - - name: Controller - description: Manage your controller - - name: ioFog - description: Manage your agents - - name: Application - description: Manage your applications - - name: Application Template - description: Manage your application templates - - name: Catalog - description: Manage your catalog - - name: Registries - description: Manage your registries - - name: Microservices - description: Manage your microservices - - name: Routing - description: Manage your routes - - name: Edge Resource - description: Manage your Edge Resources - - name: Diagnostics - description: Diagnostic your microservices - - name: Tunnel - description: Manage ssh tunnels - - name: Agent - description: Used by your agents to communicate with your controller - - name: User - description: Manage your users -servers: - - url: http://localhost:51121/api/v3 - - url: https://localhost:51121/api/v3 -components: - requestBodies: - UpdateIOFogNodeRequestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateIOFogNodeRequestBody" - required: true - CreateUpdateCatalogItemRequestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/CreateUpdateCatalogItemRequestBody" - description: Microservice Catalog Item Info - required: true - HalInfo: - content: - application/json: + /secrets: + post: + tags: + - Secrets + summary: Creates a new secret + operationId: createSecret + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SecretCreate" + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/SecretResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "409": + description: Secret Already Exists + "500": + description: Internal Server Error + get: + tags: + - Secrets + summary: Lists all secrets + operationId: listSecrets + security: + - authToken: [] + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/SecretListResponse" + "401": + description: Not Authorized + "500": + description: Internal Server Error + "/secrets/{name}": + get: + tags: + - Secrets + summary: Gets a secret by name + operationId: getSecret + parameters: + - in: path + name: name + description: Secret name + required: true schema: - $ref: "#/components/schemas/HalInfo" - required: true - ApplicationTemplateCreateRequest: - content: - application/json: + type: string + security: + - authToken: [] + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/SecretResponse" + "401": + description: Not Authorized + "404": + description: Secret Not Found + "500": + description: Internal Server Error + patch: + tags: + - Secrets + summary: Updates an existing secret + operationId: updateSecret + parameters: + - in: path + name: name + description: Secret name + required: true schema: - $ref: "#/components/schemas/ApplicationTemplateCreateRequest" - required: true - NewFlowRequest: - content: - application/json: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SecretUpdate" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/SecretResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Secret Not Found + "500": + description: Internal Server Error + delete: + tags: + - Secrets + summary: Deletes a secret + operationId: deleteSecret + parameters: + - in: path + name: name + description: Secret name + required: true schema: - $ref: "#/components/schemas/NewFlowRequest" - description: New Flow Info - required: true - schemas: - EdgeResourcesListResponse: - type: object - properties: - edgeResources: + type: string + security: + - authToken: [] + responses: + "200": + description: Success + "401": + description: Not Authorized + "404": + description: Secret Not Found + "500": + description: Internal Server Error + /secrets/yaml: + post: + tags: + - Secrets + summary: Create a secret from YAML file + operationId: createSecretFromYAML + security: + - authToken: [] + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + secret: + type: string + format: binary + responses: + '201': + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/SecretResponse" + '400': + description: Bad Request + "401": + description: Not Authorized + "409": + description: Secret Already Exists + "500": + description: Internal Server Error + "/secrets/yaml/{name}": + patch: + tags: + - Secrets + summary: Updates an existing secret using YAML + operationId: updateSecretFromYAML + parameters: + - in: path + name: name + description: Secret name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + secret: + type: string + format: binary + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/SecretResponse" + '400': + description: Bad Request + '401': + description: Unauthorized + '404': + description: Secret Not Found + /certificates/ca/{name}: + get: + tags: + - Certificates + summary: Get a Certificate Authority (CA) by name + operationId: getCA + parameters: + - in: path + name: name + description: CA name + required: true + schema: + type: string + security: + - authToken: [] + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CAResponse" + '401': + description: Unauthorized + '404': + description: CA not found + '500': + description: Internal Server Error + delete: + tags: + - Certificates + summary: Delete a Certificate Authority (CA) by name + operationId: deleteCA + parameters: + - in: path + name: name + description: CA name + required: true + schema: + type: string + security: + - authToken: [] + responses: + '200': + description: Success + '401': + description: Unauthorized + '404': + description: CA not found + '500': + description: Internal Server Error + + /certificates: + post: + tags: + - Certificates + summary: Create a new certificate + operationId: createCertificate + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateCreateRequest" + responses: + '201': + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateResponse" + '400': + description: Bad Request + '401': + description: Unauthorized + '404': + description: Not Found - Referenced CA not found + '409': + description: Conflict - Certificate already exists + get: + tags: + - Certificates + summary: List all certificates + operationId: listCertificates + security: + - authToken: [] + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateListResponse" + '401': + description: Unauthorized + '500': + description: Internal Server Error + + /certificates/expiring: + get: + tags: + - Certificates + summary: List certificates that will expire soon + operationId: listExpiringCertificates + parameters: + - in: query + name: days + description: Number of days ahead to check for expiration (default 30) + required: false + schema: + type: integer + default: 30 + security: + - authToken: [] + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateListResponse" + '400': + description: Bad Request + '401': + description: Unauthorized + '500': + description: Internal Server Error + + /certificates/{name}: + get: + tags: + - Certificates + summary: Get a certificate by name + operationId: getCertificate + parameters: + - in: path + name: name + description: Certificate name + required: true + schema: + type: string + security: + - authToken: [] + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateResponse" + '401': + description: Unauthorized + '404': + description: Certificate not found + '500': + description: Internal Server Error + delete: + tags: + - Certificates + summary: Delete a certificate by name + operationId: deleteCertificate + parameters: + - in: path + name: name + description: Certificate name + required: true + schema: + type: string + security: + - authToken: [] + responses: + '200': + description: Success + '401': + description: Unauthorized + '404': + description: Certificate not found + '500': + description: Internal Server Error + + /certificates/{name}/renew: + post: + tags: + - Certificates + summary: Renew a certificate + operationId: renewCertificate + parameters: + - in: path + name: name + description: Certificate name + required: true + schema: + type: string + security: + - authToken: [] + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CertificateRenewResponse" + '400': + description: Bad Request + '401': + description: Unauthorized + '404': + description: Certificate not found + '500': + description: Internal Server Error + /certificates/yaml: + post: + tags: + - Certificates + summary: Create a certificate or CA from YAML file + operationId: createCertificateFromYAML + security: + - authToken: [] + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + certificate: + type: string + format: binary + responses: + '201': + description: Created + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/CAResponse" + - $ref: "#/components/schemas/CertificateResponse" + '400': + description: Bad Request + '401': + description: Unauthorized + '404': + description: Not Found - Referenced CA not found + '409': + description: Conflict - Certificate or CA already exists + /services: + get: + tags: + - Services + summary: Gets list of services + operationId: getServicesList + security: + - authToken: [] + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Service" + "401": + description: Not Authorized + "500": + description: Internal Server Error + post: + tags: + - Services + summary: Creates a new service + operationId: createService + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + responses: + "201": + description: Created + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + "400": + description: Bad Request + "401": + description: Not Authorized + "409": + description: Duplicate Name + "500": + description: Internal Server Error + /services/{name}: + get: + tags: + - Services + summary: Gets a service info + operationId: getServiceInfo + parameters: + - in: path + name: name + description: Service name + required: true + schema: + type: string + security: + - authToken: [] + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + delete: + tags: + - Services + summary: Deletes a service + operationId: deleteService + parameters: + - in: path + name: name + description: Service name + required: true + schema: + type: string + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + patch: + tags: + - Services + summary: Patches a service + operationId: patchService + parameters: + - in: path + name: name + description: Service name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /services/yaml: + post: + tags: + - Services + summary: Creates a new service from YAML + operationId: createServiceYAML + security: + - authToken: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + service: + type: string + format: binary + responses: + "201": + description: Created + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + type: object + properties: + id: + type: number + name: + type: string + type: + type: string + resource: + type: string + targetPort: + type: number + defaultBridge: + type: string + bridgePort: + type: number + updatedAt: + type: string + format: date-time + createdAt: + type: string + format: date-time + "400": + description: Bad Request + "401": + description: Not Authorized + "409": + description: Duplicate Name + "500": + description: Internal Server Error + "/services/yaml/{name}": + patch: + tags: + - Services + summary: Updates a service using YAML + operationId: updateServiceYAML + parameters: + - in: path + name: name + description: Service name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + service: + type: string + format: binary + responses: + "200": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /configmaps: + post: + tags: + - ConfigMap + summary: Creates a new ConfigMap + operationId: createConfigMap + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapCreate" + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "409": + description: ConfigMap Already Exists + "500": + description: Internal Server Error + get: + tags: + - ConfigMap + summary: Lists all ConfigMaps + operationId: listConfigMaps + security: + - authToken: [] + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapListResponse" + "401": + description: Not Authorized + "500": + description: Internal Server Error + /configmaps/yaml: + post: + tags: + - ConfigMap + summary: Creates a new ConfigMap from YAML + operationId: createConfigMapFromYaml + security: + - authToken: [] + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + configMap: + type: string + format: binary + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "409": + description: ConfigMap Already Exists + "500": + description: Internal Server Error + /configmaps/{name}: + get: + tags: + - ConfigMap + summary: Gets a ConfigMap by name + operationId: getConfigMap + security: + - authToken: [] + parameters: + - in: path + name: name + required: true + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapResponse" + "401": + description: Not Authorized + "404": + description: ConfigMap Not Found + "500": + description: Internal Server Error + patch: + tags: + - ConfigMap + summary: Updates a ConfigMap + operationId: updateConfigMap + security: + - authToken: [] + parameters: + - in: path + name: name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapUpdate" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: ConfigMap Not Found + "500": + description: Internal Server Error + delete: + tags: + - ConfigMap + summary: Deletes a ConfigMap + operationId: deleteConfigMap + security: + - authToken: [] + parameters: + - in: path + name: name + required: true + schema: + type: string + responses: + "200": + description: Success + "401": + description: Not Authorized + "404": + description: ConfigMap Not Found + "500": + description: Internal Server Error + /configmaps/yaml/{name}: + patch: + tags: + - ConfigMap + summary: Updates a ConfigMap from YAML + operationId: updateConfigMapFromYaml + security: + - authToken: [] + parameters: + - in: path + name: name + required: true + schema: + type: string + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + configMap: + type: string + format: binary + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/ConfigMapResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: ConfigMap Not Found + "500": + description: Internal Server Error + /volumeMounts: + get: + tags: + - VolumeMounts + summary: Returns list of volume mounts + operationId: listVolumeMounts + security: + - authToken: [] + responses: + "200": + description: List of volume mounts + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/VolumeMount" + "401": + description: Not Authorized + "500": + description: Internal Server Error + post: + tags: + - VolumeMounts + summary: Creates a new volume mount + operationId: createVolumeMount + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMountCreate" + responses: + "200": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error + + /volumeMounts/yaml: + post: + tags: + - VolumeMounts + summary: Creates a new volume mount from YAML + operationId: createVolumeMountYaml + security: + - authToken: [] + requestBody: + required: true + content: + application/x-yaml: + schema: + type: string + responses: + "200": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error + + "/volumeMounts/{name}": + get: + tags: + - VolumeMounts + summary: Gets volume mount info + operationId: getVolumeMount + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + patch: + tags: + - VolumeMounts + summary: Updates existing volume mount + operationId: updateVolumeMount + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMountUpdate" + responses: + "200": + description: Updated + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + delete: + tags: + - VolumeMounts + summary: Deletes a volume mount + operationId: deleteVolumeMount + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + responses: + "202": + description: Accepted + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + + "/volumeMounts/yaml/{name}": + patch: + tags: + - VolumeMounts + summary: Updates existing volume mount from YAML + operationId: updateVolumeMountYaml + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/x-yaml: + schema: + type: string + responses: + "200": + description: Updated + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + + "/volumeMounts/{name}/link": + get: + tags: + - VolumeMounts + summary: Gets volume mount link info + operationId: getVolumeMountLink + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + responses: + "200": + description: Success + content: + application/json: + schema: + type: object + properties: + fogUuids: + type: array + items: + type: string + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + post: + tags: + - VolumeMounts + summary: Links volume mount to fog nodes + operationId: linkVolumeMount + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMountLink" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + delete: + tags: + - VolumeMounts + summary: Unlinks volume mount from fog nodes + operationId: unlinkVolumeMount + parameters: + - in: path + name: name + description: Volume mount name + required: true + schema: + type: string + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMountUnlink" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeMount" + "400": + description: Bad Request + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + /events: + get: + tags: + - Events + summary: List audit events + description: Retrieve audit events with optional filters and pagination. + operationId: listEvents + security: + - authToken: [] + parameters: + - in: query + name: limit + description: Maximum number of events to return (default 200, max 1000) + schema: + type: integer + minimum: 1 + maximum: 1000 + required: false + - in: query + name: offset + description: Number of events to skip before collecting results + schema: + type: integer + minimum: 0 + required: false + - in: query + name: startTime + description: Start of time range (Unix timestamp in ms or ISO 8601) + schema: + type: string + required: false + - in: query + name: endTime + description: End of time range (Unix timestamp in ms or ISO 8601) + schema: + type: string + required: false + - in: query + name: endpointType + description: Filter by endpoint type (agent or user) + schema: + type: string + required: false + - in: query + name: resourceType + description: Filter by resource type (agent, microservice, etc.) + schema: + type: string + required: false + - in: query + name: status + description: Filter by status (SUCCESS or FAILED) + schema: + type: string + required: false + - in: query + name: method + description: Filter by HTTP/WS method(s) + schema: + type: array + items: + type: string + style: form + explode: true + required: false + - in: query + name: actorId + description: Filter by actor identifier (username or fog UUID) + schema: + type: string + required: false + - in: query + name: eventType + description: Filter by event type (HTTP, WS_CONNECT, WS_DISCONNECT) + schema: + type: string + required: false + responses: + "200": + description: Events list + content: + application/json: + schema: + $ref: "#/components/schemas/EventListResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error + delete: + tags: + - Events + summary: Delete audit events + description: Delete all events when `days` is 0, or delete events older than the provided number of days. + operationId: deleteEvents + security: + - authToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EventDeleteRequest" + responses: + "200": + description: Deletion summary + content: + application/json: + schema: + $ref: "#/components/schemas/EventDeleteResponse" + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error +tags: + - name: Controller + description: Manage your controller + - name: ioFog + description: Manage your agents + - name: Application + description: Manage your applications + - name: Application Template + description: Manage your application templates + - name: Catalog + description: Manage your catalog + - name: Registries + description: Manage your registries + - name: Microservices + description: Manage your microservices + - name: Routing + description: Manage your routes + - name: Edge Resource + description: Manage your Edge Resources + - name: Diagnostics + description: Diagnostic your microservices + - name: Tunnel + description: Manage ssh tunnels + - name: Agent + description: Used by your agents to communicate with your controller + - name: User + description: Manage your users + - name: Secrets + description: Manage your secrets + - name: Certificates + description: Manage your certificates + - name: Services + description: Manage your services + - name: VolumeMounts + description: Manage your volume mounts + - name: ConfigMap + description: Manage your config maps + - name: Events + description: Manage audit events +servers: + - url: http://localhost:51121/api/v3 +components: + securitySchemes: + authToken: + type: http + scheme: bearer + bearerFormat: JWT + requestBodies: + UpdateIOFogNodeRequestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateIOFogNodeRequestBody" + required: true + CreateUpdateCatalogItemRequestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CreateUpdateCatalogItemRequestBody" + description: Microservice Catalog Item Info + required: true + HalInfo: + content: + application/json: + schema: + $ref: "#/components/schemas/HalInfo" + required: true + ApplicationTemplateCreateRequest: + content: + application/json: + schema: + $ref: "#/components/schemas/ApplicationTemplateCreateRequest" + required: true + NewFlowRequest: + content: + application/json: + schema: + $ref: "#/components/schemas/NewFlowRequest" + description: New Flow Info + required: true + schemas: + EventRecord: + type: object + properties: + id: + type: integer + timestamp: + type: integer + format: int64 + eventType: + type: string + endpointType: + type: string + actorId: + type: string + nullable: true + method: + type: string + nullable: true + resourceType: + type: string + nullable: true + resourceId: + type: string + nullable: true + endpointPath: + type: string + ipAddress: + type: string + nullable: true + status: + type: string + statusCode: + type: integer + nullable: true + statusMessage: + type: string + nullable: true + requestId: + type: string + nullable: true + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + required: + - id + - timestamp + - eventType + - endpointType + - endpointPath + - status + - createdAt + - updatedAt + EventListResponse: + type: object + properties: + events: + type: array + items: + $ref: "#/components/schemas/EventRecord" + total: + type: integer + limit: + type: integer + offset: + type: integer + required: + - events + - total + - limit + - offset + EventDeleteRequest: + type: object + properties: + days: + type: integer + minimum: 0 + maximum: 365 + required: + - days + EventDeleteResponse: + type: object + properties: + deletedCount: + type: integer + deletedBefore: + type: string + format: date-time + nullable: true + deletedAt: + type: string + format: date-time + deletedAll: + type: boolean + required: + - deletedCount + - deletedAt + - deletedAll + EdgeResourcesListResponse: + type: object + properties: + edgeResources: type: array items: type: object @@ -4047,6 +5368,8 @@ components: type: string config: type: string + annotations: + type: string catalogItemId: type: integer registryId: @@ -4061,7 +5384,9 @@ components: type: string iofogUuid: type: string - rootHostAccess: + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logSize: type: number @@ -4080,6 +5405,24 @@ components: type: string value: type: string + runAsUser: + type: string + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string cmd: type: array items: @@ -4088,6 +5431,14 @@ components: type: array items: $ref: "#/components/schemas/PortMappingsRequest" + pubTags: + type: array + items: + type: string + subTags: + type: array + items: + type: string routes: type: array items: @@ -4117,8 +5468,6 @@ components: type: string isActivated: type: boolean - userId: - type: number microservices: type: array items: @@ -4170,6 +5519,8 @@ components: type: string config: type: string + annotations: + type: string catalogItemId: type: integer registryId: @@ -4186,7 +5537,9 @@ components: type: string agentName: type: string - rootHostAccess: + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logSize: type: number @@ -4205,6 +5558,24 @@ components: type: string value: type: string + runAsUser: + type: number + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string cmd: type: array items: @@ -4213,6 +5584,14 @@ components: type: array items: $ref: "#/components/schemas/PortMappingsRequest" + pubTags: + type: array + items: + type: string + subTags: + type: array + items: + type: string routes: type: array items: @@ -4247,8 +5626,6 @@ components: type: string description: type: string - userId: - type: number application: type: object properties: @@ -4281,11 +5658,6 @@ components: example: ok timestamp: type: number - EmailActivationStatusResponse: - type: object - properties: - isEmailActivationEnabled: - type: boolean IOFogTypesResponse: type: object properties: @@ -4389,6 +5761,10 @@ components: type: string dockerUrl: type: string + containerEngine: + type: string + deploymentType: + type: string diskLimit: type: number diskDirectory: @@ -4437,8 +5813,6 @@ components: type: number fogTypeId: type: number - userId: - type: number routerMode: type: string enum: @@ -4467,6 +5841,12 @@ components: dockerUrl: type: string default: unix:///var/run/docker.sock + containerEngine: + type: string + default: docker + deploymentType: + type: string + default: native diskLimit: type: number default: 50 @@ -4630,6 +6010,10 @@ components: type: string dockerUrl: type: string + containerEngine: + type: string + deploymentType: + type: string diskLimit: type: number diskDirectory: @@ -4673,6 +6057,10 @@ components: type: string dockerUrl: type: string + containerEngine: + type: string + deploymentType: + type: string diskLimit: type: number diskDirectory: @@ -4707,6 +6095,13 @@ components: type: number dockerPruningFrequency: type: number + IOFogNodeGpsRequest: + type: object + properties: + latitude: + type: number + longitude: + type: number AgentStatus: type: object properties: @@ -4836,9 +6231,13 @@ components: type: string config: type: string + annotations: + type: string rebuild: type: boolean - rootHostAccess: + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logSize: type: number @@ -4862,6 +6261,24 @@ components: type: array items: $ref: "#/components/schemas/AgentEnvRequest" + runAsUser: + type: string + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string cmd: type: array items: @@ -4912,6 +6329,9 @@ components: accessMode: type: string example: rw + type: + type: string + example: bind PortMappingsResponse: type: object properties: @@ -4922,14 +6342,12 @@ components: protocol: type: string enum: - - tcp - - udp - proxy: - type: boolean + - tcp + - udp required: - - internal - - external - - protocol + - internal + - external + - protocol PortMappingsRequest: type: object properties: @@ -4940,20 +6358,27 @@ components: protocol: type: string enum: - - tcp - - udp - proxy: - type: boolean + - tcp + - udp required: - - internal - - external + - internal + - external PortMappingsPublicResponse: type: object properties: - publicIp: - type: string - publicPort: + internal: + type: number + external: type: number + protocol: + type: string + enum: + - tcp + - udp + required: + - internal + - external + - protocol PortMappingsListResponse: type: object properties: @@ -4968,12 +6393,15 @@ components: type: number external: type: number - publicMode: - type: boolean - publicIp: + protocol: type: string - publicPort: - type: number + enum: + - tcp + - udp + required: + - internal + - external + - protocol PortMappingAgentRequest: type: object properties: @@ -4983,6 +6411,8 @@ components: portExternal: type: string example: 80 + microservicesConfig: + type: string RegistriesListResponse: type: object properties: @@ -4999,20 +6429,12 @@ components: type: string isPublic: type: boolean - isSecure: - type: boolean - certificate: - type: string - requiresCert: - type: boolean username: type: string password: type: string userEmail: type: string - userId: - type: string RegistryBody: type: object properties: @@ -5026,10 +6448,6 @@ components: type: string email: type: string - requiresCert: - type: boolean - certificate: - type: string ActionBody: type: object properties: @@ -5169,7 +6587,11 @@ components: type: string config: type: string - rootHostAccess: + annotations: + type: string + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logLimit: type: number @@ -5192,6 +6614,24 @@ components: type: string value: type: string + runAsUser: + type: string + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string cmd: type: array items: @@ -5226,6 +6666,8 @@ components: type: string config: type: string + annotations: + type: string catalogItemId: type: integer images: @@ -5242,7 +6684,9 @@ components: type: string agentName: type: string - rootHostAccess: + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logSize: type: number @@ -5254,6 +6698,36 @@ components: type: array items: $ref: "#/components/schemas/PortMappingsRequest" + runAsUser: + type: string + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string + cmd: + type: array + items: + type: string + pubTags: + type: array + items: + type: string + subTags: + type: array + items: + type: string UpdateMicroserviceRequest: type: object required: @@ -5263,13 +6737,17 @@ components: type: string config: type: string + annotations: + type: string rebuild: type: boolean iofogUuid: type: string agentName: type: string - rootHostAccess: + hostNetworkMode: + type: boolean + isPrivileged: type: boolean logLimit: type: number @@ -5285,6 +6763,36 @@ components: type: string fogTypeId: type: string + runAsUser: + type: string + platform: + type: string + runtime: + type: string + cdiDevices: + type: array + items: + type: string + capAdd: + type: array + items: + type: string + capDrop: + type: array + items: + type: string + cmd: + type: array + items: + type: string + pubTags: + type: array + items: + type: string + subTags: + type: array + items: + type: string IOFogNodeTunnelStatusInfoResponse: type: object properties: @@ -5303,104 +6811,63 @@ components: required: - email - password + - totp + RefreshRequest: + type: string + required: + - refreshToken properties: email: type: string password: type: string + totp: + type: string LoginSuccessResponse: type: object required: - accessToken + - refreshToken properties: accessToken: type: string - SignupSuccessResponse: - type: object - required: - - userId - - firstName - - lastName - - email - - emailActivated - properties: - userId: - type: number - firstName: - type: string - lastName: - type: string - email: - type: string - emailActivated: - type: boolean - UserProfileDetailsResponse: - type: object - required: - - firstName - - lastName - - email - properties: - firstName: - type: string - lastName: - type: string - email: - type: string - UserProfileUpdatesRequest: - type: object - properties: - firstName: - type: string - lastName: - type: string - UserActivateRequest: - type: object - required: - - activationCode - properties: - activationCode: - type: string - PasswordResetRequest: - type: object - required: - - email - properties: - email: + refreshToken: type: string - PasswordChangeRequest: + RefreshSuccessResponse: type: object required: - - oldPassword - - newPassword + - accessToken + - refreshToken properties: - oldPassword: + accessToken: type: string - newPassword: + refreshToken: type: string - DeleteParameters: - type: object - required: - - force - properties: - force: - type: boolean - SignupRequest: + UserProfileDetailsResponse: type: object - required: - - firstName - - lastName - - email - - password properties: - firstName: - type: string - lastName: - type: string - email: - type: string - password: - type: string + userinfo: + type: array + items: + properties: + sub: + type: string + SubscriptionKey: + type: string + email_verified: + type: string + name: + type: string + preferred_username: + type: string + locale: + type: string + given_name: + type: string + family_name: + type: string + email: + type: string VersionCommandResponse: type: object required: @@ -5493,4 +6960,474 @@ components: sourceMicroserviceUuid: type: string destMicroserviceUuid: - type: string \ No newline at end of file + type: string + SecretCreate: + type: object + required: + - name + - type + - data + properties: + name: + type: string + minLength: 1 + maxLength: 255 + type: + type: string + enum: [Opaque, tls] + data: + type: object + SecretUpdate: + type: object + required: + - data + properties: + data: + type: object + SecretResponse: + type: object + required: + - id + - name + - type + - data + - created_at + - updated_at + properties: + id: + type: integer + name: + type: string + type: + type: string + enum: [Opaque, tls] + data: + type: object + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + SecretListResponse: + type: object + required: + - secrets + properties: + secrets: + type: array + items: + type: object + required: + - id + - name + - type + - created_at + - updated_at + properties: + id: + type: integer + name: + type: string + type: + type: string + enum: [Opaque, tls] + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + # Certificate schemas + CACreateRequest: + type: object + required: + - name + - subject + - type + properties: + name: + type: string + description: Name of the CA + subject: + type: string + description: Subject of the CA (CN) + expiration: + type: integer + description: Expiration time in milliseconds + type: + type: string + enum: [k8s-secret, direct, self-signed] + description: Type of CA + secretName: + type: string + description: Name of the secret (required for direct type) + + CAResponse: + type: object + properties: + name: + type: string + description: Name of the CA + subject: + type: string + description: Subject of the CA + is_ca: + type: boolean + description: True if this is a CA + valid_from: + type: string + format: date-time + description: Validity start date + valid_to: + type: string + format: date-time + description: Validity end date + serial_number: + type: string + description: Certificate serial number + data: + type: object + properties: + certificate: + type: string + description: PEM encoded certificate + private_key: + type: string + description: PEM encoded private key + + CAListResponse: + type: object + properties: + cas: + type: array + items: + type: object + properties: + name: + type: string + description: Name of the CA + subject: + type: string + description: Subject of the CA + valid_from: + type: string + format: date-time + description: Validity start date + valid_to: + type: string + format: date-time + description: Validity end date + days_remaining: + type: integer + description: Days until expiration + is_expired: + type: boolean + description: True if certificate is expired + + CertificateCreateRequest: + type: object + required: + - name + - subject + - hosts + properties: + name: + type: string + description: Name of the certificate + subject: + type: string + description: Subject of the certificate (CN) + hosts: + type: string + description: Comma-separated list of hosts + expiration: + type: integer + description: Expiration time in milliseconds + ca: + type: object + properties: + type: + type: string + enum: [k8s-secret, direct, self-signed] + description: Type of CA + secretName: + type: string + description: Name of the CA secret + cert: + type: string + description: PEM encoded certificate (for direct type) + key: + type: string + description: PEM encoded private key (for direct type) + + CertificateResponse: + type: object + properties: + name: + type: string + description: Name of the certificate + subject: + type: string + description: Subject of the certificate + hosts: + type: string + description: Comma-separated list of hosts + is_ca: + type: boolean + description: True if this is a CA + valid_from: + type: string + format: date-time + description: Validity start date + valid_to: + type: string + format: date-time + description: Validity end date + serial_number: + type: string + description: Certificate serial number + ca_name: + type: string + description: Name of the signing CA + certificate_chain: + type: array + items: + type: object + properties: + name: + type: string + subject: + type: string + days_remaining: + type: integer + description: Days until expiration + is_expired: + type: boolean + description: True if certificate is expired + data: + type: object + properties: + certificate: + type: string + description: PEM encoded certificate + private_key: + type: string + description: PEM encoded private key + + CertificateListResponse: + type: object + properties: + certificates: + type: array + items: + type: object + properties: + name: + type: string + description: Name of the certificate + subject: + type: string + description: Subject of the certificate + hosts: + type: string + description: Comma-separated list of hosts + is_ca: + type: boolean + description: True if this is a CA + valid_from: + type: string + format: date-time + description: Validity start date + valid_to: + type: string + format: date-time + description: Validity end date + days_remaining: + type: integer + description: Days until expiration + is_expired: + type: boolean + description: True if certificate is expired + ca_name: + type: string + description: Name of the signing CA + + CertificateRenewResponse: + type: object + properties: + name: + type: string + description: Name of the certificate + subject: + type: string + description: Subject of the certificate + hosts: + type: string + description: Comma-separated list of hosts + valid_from: + type: string + format: date-time + description: New validity start date + valid_to: + type: string + format: date-time + description: New validity end date + renewed: + type: boolean + description: True if certificate was successfully renewed + Service: + type: object + properties: + name: + type: string + type: + type: string + resource: + type: string + defaultBridge: + type: string + bridgePort: + type: number + targetPort: + type: number + tags: + type: array + items: + type: string + ConfigMapCreate: + type: object + required: + - name + - data + properties: + name: + type: string + minLength: 1 + maxLength: 255 + data: + type: object + ConfigMapUpdate: + type: object + required: + - data + properties: + data: + type: object + ConfigMapResponse: + type: object + required: + - id + - name + - data + - created_at + - updated_at + properties: + id: + type: integer + name: + type: string + data: + type: object + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + ConfigMapListResponse: + type: object + required: + - configMaps + properties: + configMaps: + type: array + items: + type: object + required: + - id + - name + - created_at + - updated_at + properties: + id: + type: integer + name: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + VolumeMount: + type: object + properties: + uuid: + type: string + name: + type: string + secretName: + type: string + configMapName: + type: string + version: + type: integer + required: + - uuid + - name + - version + + VolumeMountCreate: + type: object + properties: + name: + type: string + secretName: + type: string + configMapName: + type: string + required: + - name + oneOf: + - required: + - secretName + - required: + - configMapName + + VolumeMountUpdate: + type: object + properties: + name: + type: string + secretName: + type: string + configMapName: + type: string + oneOf: + - required: + - secretName + - required: + - configMapName + + VolumeMountLink: + type: object + properties: + fogUuids: + type: array + items: + type: string + minItems: 1 + required: + - fogUuids + + VolumeMountUnlink: + type: object + properties: + fogUuids: + type: array + items: + type: string + minItems: 1 + required: + - fogUuids \ No newline at end of file diff --git a/generate-swagger.sh b/generate-swagger.sh index 77bc013bb..cf964e824 100755 --- a/generate-swagger.sh +++ b/generate-swagger.sh @@ -1 +1,2 @@ +#!/bin/bash docker run -v ./docs:/docs mitjaziv/swagger-codegen-cli generate -i /docs/swagger.yaml -l swagger -o /docs \ No newline at end of file diff --git a/logrotate.conf b/logrotate.conf index b94c050dc..9690627c0 100644 --- a/logrotate.conf +++ b/logrotate.conf @@ -1,10 +1,17 @@ /var/log/iofog-controller/iofog-controller.log { - rotate 10 - size 10m + daily + rotate 50 + size 100M compress notifempty missingok + create 0644 10000 10000 + nodateext postrotate - kill -HUP `cat /usr/local/lib/node_modules/iofogcontroller/src/iofog-controller.pid` + if [ -f /home/runner/iofog-controller.pid ]; then + kill -HUP `cat /home/runner/iofog-controller.pid`; + elif [ -f /opt/iofog/controller/lib/node_modules/@datasance/iofogcontroller/src/iofog-controller.pid ]; then + kill -HUP `cat /opt/iofog/controller/lib/node_modules/@datasance/iofogcontroller/src/iofog-controller.pid`; + fi endscript -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2fcddbca9..7f5db837f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,137 +1,132 @@ { - "name": "@iofog/iofogcontroller", - "version": "3.0.2-dev", + "name": "@datasance/iofogcontroller", + "version": "3.5.11", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@iofog/iofogcontroller", - "version": "3.0.2-dev", + "name": "@datasance/iofogcontroller", + "version": "3.5.11", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { - "@iofog/ecn-viewer": "3.0.2", - "axios": "1.0.0-alpha.1", - "body-parser": "^1.20.1", - "child_process": "1.0.2", - "command-line-args": "5.0.2", - "command-line-usage": "5.0.5", + "@datasance/ecn-viewer": "1.2.6", + "@kubernetes/client-node": "^0.22.3", + "@msgpack/msgpack": "^3.1.2", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.200.0", + "@opentelemetry/instrumentation-express": "^0.48.1", + "@opentelemetry/instrumentation-http": "^0.200.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-node": "^0.200.0", + "axios": "1.12.2", + "bignumber.js": "^9.3.0", + "body-parser": "^1.20.3", + "command-line-args": "5.2.1", + "command-line-usage": "7.0.3", "concurrent-queue": "7.0.2", - "cookie-parser": "1.4.3", + "cookie-parser": "1.4.7", "cors": "2.8.5", "daemonize2": "0.4.2", - "ejs": "3.1.7", - "express": "4.17.3", - "formidable": "1.2.1", + "dotenv": "^16.5.0", + "ejs": "3.1.10", + "express": "4.21.2", + "express-session": "1.18.2", + "formidable": "3.5.4", "ftp": "0.3.10", - "helmet": "3.21.2", + "helmet": "7.1.0", "is-elevated": "3.0.0", - "js-yaml": "3.14.1", - "jsonschema": "1.2.5", - "minimatch": "3.1.2", - "moment": "2.29.4", - "moment-timezone": "0.5.38", - "morgan": "1.9.1", + "jose": "^4.15.9", + "js-yaml": "4.1.1", + "jsonschema": "1.4.1", + "keycloak-connect": "^26.1.1", + "moment": "2.30.1", "multer": "1.4.5-lts.1", - "nconf": "0.12.0", - "nodemailer": "6.7.3", - "nodemailer-smtp-transport": "2.4.2", - "os": "0.1.1", - "path": "0.12.7", - "pino": "6.6.1", - "pino-std-serializers": "2.5.0", + "mysql2": "3.10.1", + "nconf": "0.12.1", + "node-fetch-npm": "^2.0.4", + "node-forge": "^1.3.3", + "pg": "8.12.0", + "pino": "9.13.1", + "pino-std-serializers": "7.0.0", "portscanner": "2.2.0", - "qs": "6.10.3", - "request": "2.88.0", - "request-promise": "4.2.4", - "retry-as-promised": "3.1.0", - "semantic-release": "19.0.3", - "semver": "5.6.0", - "sequelize": "6.29.0", - "sqlite3": "^5.1.5", + "qs": "6.12.1", + "rhea": "^3.0.4", + "sequelize": "6.37.7", + "sqlite3": "^5.1.7", "string-format": "2.0.0", - "swagger-ui-express": "^4.6.2", - "umzug": "2.2.0", - "underscore": "1.13.1", + "uuid": "11.1.0", + "ws": "^8.18.0", "xss-clean": "0.1.1" }, "bin": { "iofog-controller": "src/main.js" }, "devDependencies": { - "acorn": "7.1.1", - "bdd-lazy-var": "2.5.2", - "chai": "4.2.0", - "chai-as-promised": "7.1.1", - "chai-http": "4.2.1", - "eslint": "5.14.1", - "eslint-config-google": "0.12.0", - "mocha": "9.2.2", - "mocha-junit-reporter": "2.0.0", - "newman": "5.3.2", + "acorn": "8.11.3", + "bdd-lazy-var": "2.6.1", + "chai": "5.1.1", + "chai-as-promised": "7.1.2", + "chai-http": "4.4.0", + "eslint": "9.28.0", + "eslint-config-google": "0.14.0", + "js-yaml": "^4.1.1", + "mocha": "10.6.0", + "mocha-junit-reporter": "2.2.1", + "newman": "^6.2.1", "newman-reporter-junitfull": "1.1.1", - "nyc": "15.0.0", - "sequelize-cli": "5.5.0", - "sinon": "7.5.0", - "sinon-chai": "3.3.0", - "snyk": "^1.1064.0", + "nyc": "15.1.0", + "sequelize-cli": "6.6.2", + "sinon": "17.0.1", + "sinon-chai": "3.7.0", + "snyk": "^1.1291.0", "standard": "12.0.1" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -141,13 +136,19 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -159,224 +160,163 @@ } }, "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "yallist": "^3.0.2" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.28.5" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -385,47 +325,44 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", - "debug": "^4.1.0", - "globals": "^11.1.0" + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -437,2820 +374,2561 @@ } }, "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } }, - "node_modules/@iofog/ecn-viewer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@iofog/ecn-viewer/-/ecn-viewer-3.0.2.tgz", - "integrity": "sha512-Uq8BWdFc164bKn21biLKvRufr0XKaLyJklGiOLIIz6e0Xu2f5Xp0Bvc3Mi2DdgLdlO7f1xohE5Ms3cO1rGF1Vw==" + "node_modules/@datasance/ecn-viewer": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@datasance/ecn-viewer/-/ecn-viewer-1.2.6.tgz", + "integrity": "sha512-/NV1ll6Vt97P3Fdb8oNDZLHGNNNoUW8zF+VVB0RFEEZpUBZqq5vWx72RyNoTP6Jdr7NqnZUb6PA/1Z9GTN3fvw==" }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, "engines": { - "node": ">=8" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/@eslint/config-array": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=6" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "ms": "^2.1.3" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", + "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" + "@types/json-schema": "^7.0.15" }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "deprecated": "Please update to a newer version.", + "dev": true + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.2.tgz", + "integrity": "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA==", "dependencies": { - "yallist": "^4.0.0" + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" }, "engines": { - "node": ">=10" + "node": ">=12.10.0" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", "dependencies": { - "abbrev": "1" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" }, "bin": { - "nopt": "bin/nopt.js" + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { "node": ">=6" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=10" + "node": ">=18.18.0" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" + "node": ">=12" } }, - "node_modules/@npmcli/fs/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@npmcli/fs/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "optional": true, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" + "node": ">=12" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "optional": true, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, "dependencies": { - "glob": "^7.1.3" + "ansi-regex": "^6.0.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@octokit/auth-token": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", - "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz", - "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==", - "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "node": ">=12" }, - "engines": { - "node": ">= 14" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@octokit/endpoint": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz", - "integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "minipass": "^7.0.4" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", - "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { - "node": ">= 14" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz", - "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" + "node": ">=8" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz", - "integrity": "sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.3.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" + "node": ">=8" } }, - "node_modules/@octokit/request": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz", - "integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">= 14" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/request-error/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "wrappy": "1" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@octokit/rest": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz", - "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "@octokit/core": "^4.1.0", - "@octokit/plugin-paginate-rest": "^6.0.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dependencies": { - "graceful-fs": "4.2.10" - }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, "engines": { - "node": ">=12.22.0" + "node": ">=8" } }, - "node_modules/@pnpm/npm-conf": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", - "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "dependencies": { - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@postman/form-data": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", - "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@postman/tunnel-agent": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", - "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, "engines": { - "node": "*" + "node": ">=6.0.0" } }, - "node_modules/@semantic-release/commit-analyzer": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", - "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", "engines": { - "node": ">=6.0" + "node": ">= 10.16.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/github": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.0.7.tgz", - "integrity": "sha512-VtgicRIKGvmTHwm//iqTh/5NGQwsncOMR5vQK9pMT92Aem7dv37JFKKRuulUsAnUOIlO4G8wH3gPiBAA0iW0ww==", - "dependencies": { - "@octokit/rest": "^19.0.0", - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^11.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^3.0.0", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" - }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", "engines": { - "node": ">=14.17" + "node": ">= 10.16.0" }, "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@semantic-release/github/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@kubernetes/client-node": { + "version": "0.22.3", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.22.3.tgz", + "integrity": "sha512-dG8uah3+HDJLpJEESshLRZlAZ4PgDeV9mZXT0u1g7oy4KMRzdZ7n5g0JEIlL6QhK51/2ztcIqURAnjfjJt6Z+g==", "dependencies": { - "ms": "2.1.2" + "byline": "^5.0.0", + "isomorphic-ws": "^5.0.0", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^10.2.0", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "stream-buffers": "^3.0.2", + "tar": "^7.0.0", + "tslib": "^2.4.1", + "ws": "^8.18.0" }, + "optionalDependencies": { + "openid-client": "^6.1.3" + } + }, + "node_modules/@kubernetes/client-node/@cypress/request@3.0.8": {}, + "node_modules/@msgpack/msgpack": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.2.tgz", + "integrity": "sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 18" } }, - "node_modules/@semantic-release/github/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@semantic-release/npm": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.2.tgz", - "integrity": "sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g==", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^11.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^8.3.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" - }, - "engines": { - "node": ">=16 || ^14.17" + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "engines": { + "node": "^14.21.3 || >=16" }, - "peerDependencies": { - "semantic-release": ">=19.0.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@semantic-release/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" } }, - "node_modules/@semantic-release/npm/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/@semantic-release/npm/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/npm/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", "dependencies": { - "yallist": "^4.0.0" + "@opentelemetry/api": "^1.3.0" }, "engines": { - "node": ">=10" + "node": ">=8.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.0.tgz", + "integrity": "sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA==", "engines": { - "node": ">=6" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/ci-detect", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "chownr", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "opener", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", - "semver", - "ssri", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], + "node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", - "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-+3MDfa5YQPGM3WXxW9kqGD85Q7s9wlEMVNhXXG7tYFLnIeaseUt9YtCeFhEDFzfEktacdFpOtXmJuNW8cHbU5A==", "dependencies": { - "path-key": "^3.0.0" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" }, "engines": { - "node": ">=8" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.200.0.tgz", + "integrity": "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" + }, "engines": { - "node": ">=0.1.90" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" - }, - "bin": { - "arborist": "bin/index.js" + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.200.0.tgz", + "integrity": "sha512-GmahpUU/55hxfH4TP77ChOfftADsCq/nuri73I/AVLe2s4NIglvTsaACkFVZAVmnXXyPS00Fk3x27WS3yO07zA==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-trace-base": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/ci-detect": { + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/resources": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-uHawPRvKIrhqH09GloTuYeq2BjyieYHIpiklOvxm9zhrCL2eRsnI/6g9v2BZTVtGp8tEgIa7rCQ6Ltxw6NBgew==", "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/disparity-colors": { + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "ansi-styles": "^4.3.0" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.200.0.tgz", + "integrity": "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw==", "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.200.0.tgz", + "integrity": "sha512-E+uPj0yyvz81U9pvLZp3oHtFrEzNSqKGVkIViTQY1rH3TOobeJPSpLnTVXACnCwkPR5XeTvPnK3pZ2Kni8AFMg==", "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" }, "engines": { - "node": ">= 10" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.200.0.tgz", + "integrity": "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw==", "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/move-file": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-hmeZrUkFl1YMsgukSuHCFPYeF9df0hHoKeHUthRKFCxiURs+GwF1VuabuHmBMZnjTbsuvNjOB+JSs37Csem/5Q==", "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/node-gyp": { + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.200.0.tgz", + "integrity": "sha512-Goi//m/7ZHeUedxTGVmEzH19NgqJY+Bzr6zXo1Rni1+hwqaksEyJ44gdlEMREu6dzX1DlAaH/qSykSVzdrdafA==", "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "infer-owner": "^1.0.4" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.200.0.tgz", + "integrity": "sha512-V9TDSD3PjK1OREw2iT9TUTzNYEVWJk4Nhodzhp9eiz4onDMYmPy3LaGbPv81yIR6dUb/hNp/SIhpiCHwFUq2Vg==", "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@tootallnate/once": { + "node_modules/@opentelemetry/exporter-zipkin": { "version": "2.0.0", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.0.tgz", + "integrity": "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, "engines": { - "node": ">= 10" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "debug": "4" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">= 6.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/instrumentation": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.200.0.tgz", + "integrity": "sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "@opentelemetry/api-logs": "0.200.0", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "shimmer": "^1.2.1" }, "engines": { - "node": ">= 8.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.48.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.48.1.tgz", + "integrity": "sha512-j8NYOf9DRWtchbWor/zA0poI42TpZG9tViIKA0e1lC+6MshTqSJYtgNv8Fn1sx1Wn/TRyp+5OgSXiE4LDfvpEg==", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { - "node": ">=8" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.200.0.tgz", + "integrity": "sha512-9tqGbCJikhYU68y3k9mi6yWsMyMeCcwoQuHvIXan5VvvPPQ5WIZaV6Mxu/MCVe4swRNoFs8Th+qyj0TZV5ELvw==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/instrumentation": "0.200.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, "engines": { - "node": ">=8" - } + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.200.0.tgz", + "integrity": "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ==", "dependencies": { - "color-convert": "^2.0.1" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-transformer": "0.200.0" }, "engines": { - "node": ">=8" + "node": "^18.19.0 || >=20.6.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.200.0.tgz", + "integrity": "sha512-CK2S+bFgOZ66Bsu5hlDeOX6cvW5FVtVjFFbWuaJP0ELxJKBB6HlbLZQ2phqz/uLj1cWap5xJr/PsR3iGoB7Vqw==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.200.0.tgz", + "integrity": "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw==", "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "protobufjs": "^7.3.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "semver": "^7.0.0" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cacache": { - "version": "16.1.3", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.0.tgz", + "integrity": "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA==", "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" + "@opentelemetry/core": "2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chalk": { - "version": "4.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.0.tgz", + "integrity": "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@opentelemetry/core": "2.0.0" }, "engines": { - "node": ">=10" + "node": "^18.19.0 || >=20.6.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dependencies": { - "ip-regex": "^4.1.0" + "@opentelemetry/semantic-conventions": "1.28.0" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.200.0.tgz", + "integrity": "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA==", "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" }, "engines": { - "node": ">= 10" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "string-width": "^4.2.0" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "10.* || >= 12.*" + "node": "^18.19.0 || >=20.6.0" }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.0.tgz", + "integrity": "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, "engines": { - "node": ">=0.8" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "mkdirp-infer-owner": "^2.0.0" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.200.0.tgz", + "integrity": "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-logs-otlp-http": "0.200.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", + "@opentelemetry/exporter-prometheus": "0.200.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-trace-otlp-http": "0.200.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", + "@opentelemetry/exporter-zipkin": "2.0.0", + "@opentelemetry/instrumentation": "0.200.0", + "@opentelemetry/propagator-b3": "2.0.0", + "@opentelemetry/propagator-jaeger": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/sdk-trace-node": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", "dependencies": { - "color-name": "~1.1.4" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.0.tgz", + "integrity": "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw==", "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=8.0.0" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=4" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.0.tgz", + "integrity": "sha512-omdilCZozUjQwY3uZRBwbaRMJ3p09l4t187Lsdf0dGMye9WKD4NGcpgZRvqhI1dwcH6og+YXQEtoO9Wx3ykilg==", "dependencies": { - "ms": "2.1.2" + "@opentelemetry/context-async-hooks": "2.0.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" }, "engines": { - "node": ">=6.0" + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", + "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "engines": { - "node": "*" + "node": ">=14" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dependencies": { - "clone": "^1.0.2" + "@noble/hashes": "^1.1.5" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "dev": true, "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "inBundle": true, - "license": "BSD-3-Clause", + "node_modules/@postman/tunnel-agent": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz", + "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, "engines": { - "node": ">=0.3.1" + "node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { - "iconv-lite": "^0.6.2" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT" + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "inBundle": true, - "license": "MIT" + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@sentry-internal/tracing": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz", + "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/gauge": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/@sentry/core": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz", + "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==", + "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/glob": { - "version": "8.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/@sentry/integrations": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz", + "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4", + "localforage": "^1.8.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/@sentry/node": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.120.4.tgz", + "integrity": "sha512-qq3wZAXXj2SRWhqErnGCSJKUhPSlZ+RGnCZjhfjHpP49KNpcd9YdPTIUsFMgeyjdh6Ew6aVCv23g1hTP0CHpYw==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "@sentry-internal/tracing": "7.120.4", + "@sentry/core": "7.120.4", + "@sentry/integrations": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" }, "engines": { - "node": ">= 0.4.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/@sentry/types": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz", + "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/@sentry/utils": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz", + "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==", + "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "@sentry/types": "7.120.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "type-detect": "4.0.8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^5.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", + "node_modules/@testim/chrome-version": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", + "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==", + "optional": true + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, "engines": { - "node": ">=0.8.19" + "node": ">= 6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "optional": true + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC" + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "undici-types": "~7.16.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ini": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/@types/superagent": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.13.tgz", + "integrity": "sha512-YIGelp3ZyMiH0/A09PMAORO0EBGlF5xIKfDpK74wdYvWUs2o96b5CItJcWPdH409b7SAXIIG6p8NdU/4U2Maww==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "optional": true, "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "@types/node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ip": { + "node_modules/abbrev": { "version": "2.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "cidr-regex": "^3.1.1" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.4.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", - "inBundle": true, - "license": "MIT" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/afterward": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/afterward/-/afterward-2.0.0.tgz", + "integrity": "sha512-7n9Vkbb8cmMRKKSfe5qgyqX4Yjdaty0QP/+GXYawZK8Vcq+8E5FCmbWbwfCoiBnDoAY/edKLNg2TwgGcwdA+3Q==", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "define-error": "~1.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" - }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "optional": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 14" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", - "inBundle": true, - "license": "ISC", + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "optional": true, "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "semver": "^7.3.7", - "walk-up-path": "^1.0.0" + "humanize-ms": "^1.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 8.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "devOptional": true, "dependencies": { - "@npmcli/arborist": "^5.6.3" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpack": { + "node_modules/ansi-colors": { "version": "4.1.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" - }, + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", - "semver": "^7.3.7", - "ssri": "^9.0.0" - }, + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.0" - }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", - "semver": "^7.3.7" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", - "inBundle": true, - "license": "ISC", + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "optional": true + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "optional": true, "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, "dependencies": { - "brace-expansion": "^2.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass": { - "version": "3.3.4", - "inBundle": true, - "license": "ISC", + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, "dependencies": { - "yallist": "^4.0.0" - }, + "safe-buffer": "~5.2.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-collect": { + "node_modules/array-buffer-byte-length": { "version": "1.0.2", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", - "inBundle": true, - "license": "MIT", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "encoding": "^0.1.13" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" + "safer-buffer": "~2.1.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "optional": true, "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "tslib": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", - "inBundle": true, - "license": "ISC" + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node": ">= 4.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, "engines": { "node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/nopt": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "chalk": "^4.0.0" - }, + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, "dependencies": { - "semver": "^7.1.1" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - }, + "node_modules/babel-code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.8.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", - "inBundle": true, - "license": "ISC", + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true + }, + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "bin": { - "npm-packlist": "bin/index.js" + "ansi-regex": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "node_modules/babel-code-frame/node_modules/supports-color": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.8.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/baseline-browser-mapping": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.3.tgz", + "integrity": "sha512-8QdH6czo+G7uBsNo0GiUfouPN1lRzKdJTGnKXwe12gkFbnnOUaUKGN55dMkfy+mnxmvjwl9zcI4VncczcVXDhA==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "optional": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "tweetnacl": "^0.14.3" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" + "node_modules/bdd-lazy-var": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bdd-lazy-var/-/bdd-lazy-var-2.6.1.tgz", + "integrity": "sha512-X3ADwcFji/IHIrYJhTTpaiWhoOx4pl4whdAx1dmvdeUPsMUb7fVYFvf/Q33VEAEAVkEwi5rgNSZ0Y9oOVeQV+A==", + "dev": true, + "peerDependencies": { + "jasmine": ">=2", + "jasmine-core": ">=2", + "jest": ">=20", + "mocha": ">=2.3" + }, + "peerDependenciesMeta": { + "jasmine": { + "optional": true + }, + "jasmine-core": { + "optional": true + }, + "jest": { + "optional": true + }, + "mocha": { + "optional": true + } } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "engines": { + "node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/pacote": { - "version": "13.6.2", - "inBundle": true, - "license": "ISC", + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "file-uri-to-path": "1.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "inBundle": true, - "license": "MIT", + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "node_modules/bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" + "node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promzard": { - "version": "0.3.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "1" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/body-parser/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dependencies": { - "mute-stream": "~0.0.4" + "side-channel": "^1.1.0" }, "engines": { - "node": ">=0.8" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "devOptional": true, "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "base64-js": "^1.1.2" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "glob": "^7.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "browserslist": "cli.js" }, "engines": { - "node": "*" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { "type": "github", @@ -3265,4770 +2943,1673 @@ "url": "https://feross.org/support" } ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/semver": { - "version": "7.3.7", - "inBundle": true, - "license": "ISC", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "optional": true, "engines": { - "node": ">=10" + "node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { - "yallist": "^4.0.0" + "streamsearch": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">=10.16.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks": { - "version": "2.7.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "node": ">= 0.8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" }, "engines": { "node": ">= 10" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ssri": { - "version": "9.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "optional": true, "dependencies": { - "minipass": "^3.1.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/cacache/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, "dependencies": { - "has-flag": "^4.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tar": { - "version": "6.1.11", - "inBundle": true, - "license": "ISC", + "node_modules/cacache/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/cacache/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "optional": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, "dependencies": { - "unique-slug": "^3.0.0" + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "dependencies": { - "imurmurhash": "^0.1.4" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { - "builtins": "^5.0.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dependencies": { - "defaults": "^1.0.3" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "callsites": "^0.2.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "node_modules/caller-path/node_modules/callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/@semantic-release/npm/node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" + "node_modules/caniuse-lite": { + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/@semantic-release/npm/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, + "node_modules/capture-stack-trace": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", + "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", "engines": { - "node": ">=6" + "node": ">=0.10.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/npm/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true }, - "node_modules/@semantic-release/npm/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "node_modules/chai": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@semantic-release/npm/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "check-error": "^1.0.2" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "chai": ">= 2.1.2 < 6" } }, - "node_modules/@semantic-release/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/chai-as-promised/node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/@semantic-release/npm/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/chai-http": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.4.0.tgz", + "integrity": "sha512-uswN3rZpawlRaa5NiDUHcDZ3v2dw5QgLyAwnQ2tnVNuP7CwIsOFuYJ0xR1WiR7ymD4roBnJIzOUep7w9jQMFJA==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "@types/chai": "4", + "@types/superagent": "4.1.13", + "charset": "^1.0.1", + "cookiejar": "^2.1.4", + "is-ip": "^2.0.0", + "methods": "^1.1.2", + "qs": "^6.11.2", + "superagent": "^8.0.9" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/@semantic-release/npm/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", - "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", - "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "get-stream": "^6.0.0", - "import-from": "^4.0.0", - "into-stream": "^6.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14.17" + "node": ">=10" }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", "dependencies": { - "ms": "2.1.2" + "chalk": "^4.1.2" }, "engines": { - "node": ">=6.0" + "node": ">=12" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/chardet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.0.0.tgz", + "integrity": "sha512-xVgPpulCooDjY6zH4m9YW3jbkaBe3FKIAvF5sj5t7aBNsVl2ljIE+xwJ4iNgiDZHFQvNIpjdKdVOQvvk5ZfxbQ==", + "dev": true + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/charset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", + "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "engines": { + "node": ">= 16" } }, - "node_modules/@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, - "node_modules/@tootallnate/once": { + "node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", - "dev": true - }, - "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "node_modules/chromedriver": { + "version": "143.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-143.0.0.tgz", + "integrity": "sha512-zsDjk9nLeQsMcnFXP4huqCOqneIdox3ECIR4laBOH7sog1+K2rTgrK60ogSeYaHUnx9OTAMFkwJL29ekVgVQgw==", + "hasInstallScript": true, + "optional": true, "dependencies": { - "@types/ms": "*" + "@testim/chrome-version": "^1.1.4", + "axios": "^1.12.0", + "compare-versions": "^6.1.0", + "extract-zip": "^2.0.1", + "proxy-agent": "^6.4.0", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.2" + }, + "bin": { + "chromedriver": "bin/chromedriver" + }, + "engines": { + "node": ">=20" } }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "node_modules/@types/node": { - "version": "18.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", - "integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/superagent": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", - "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", - "dev": true, - "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "node_modules/@types/validator": { - "version": "13.7.12", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.12.tgz", - "integrity": "sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA==" - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "node_modules/circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", "dev": true }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "devOptional": true, "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.10" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/afterward": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/afterward/-/afterward-2.0.0.tgz", - "integrity": "sha512-7n9Vkbb8cmMRKKSfe5qgyqX4Yjdaty0QP/+GXYawZK8Vcq+8E5FCmbWbwfCoiBnDoAY/edKLNg2TwgGcwdA+3Q==", "dependencies": { - "define-error": "~1.0.0" + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dev": true, "dependencies": { - "debug": "4" + "string-width": "^4.2.3" }, "engines": { - "node": ">= 6.0.0" + "node": ">=4" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "string-width": "^4.2.0" }, "engines": { - "node": ">=6.0" + "node": "10.* || >= 12.*" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true }, - "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", - "optional": true, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">=12" } }, - "node_modules/agentkeepalive/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "ms": "2.1.2" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=7.0.0" } }, - "node_modules/agentkeepalive/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "optional": true, - "engines": { - "node": ">= 0.6" + "bin": { + "color-support": "bin.js" } }, - "node_modules/agentkeepalive/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">=4.0.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, "engines": { - "node": ">=6" + "node": ">=12.20.0" } }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "engines": { - "node": ">=4" + "node": ">=12.17" } }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, + "node_modules/command-line-usage/node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "engines": { - "node": ">=0.10.0" + "node": ">=12.17" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=16" } }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==" + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "optional": true }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/archy": { + "node_modules/concat-stream/node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "safe-buffer": "~5.1.0" } }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/concurrent-queue": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/concurrent-queue/-/concurrent-queue-7.0.2.tgz", + "integrity": "sha512-icXDqc0JBdcQ3ubXiXcqVhuFeRrec39zVD2X5z7FKwwj0pImnfLWtAhGyX4CcBDD+YoqLesClOeRss+pZnm6/Q==", "dependencies": { - "safe-buffer": "~5.2.0" + "afterward": "~2.0.0", + "define-error": "~1.0.0", + "eventuate": "~4.0.0", + "object-assign": "~4.0.1", + "on-error": "~2.1.0", + "once": "~1.3.2", + "promise-polyfill": "~2.1.0" } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==" + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true }, - "node_modules/argv-tools": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/argv-tools/-/argv-tools-0.1.2.tgz", - "integrity": "sha512-wxqoymY0BEu9NblZVQiOTOAiJUjPhaa/kbNMjC2h6bnrmUSgnxKgWJo3lzXvi3bHJRwXyqK/dHzMlZVRT89Cxg==", - "dependencies": { - "array-back": "^2.0.0", - "find-replace": "^2.0.1" - }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true + }, + "node_modules/contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", + "dev": true, "engines": { - "node": ">=4.0.0" + "node": ">=0.10.0" } }, - "node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "typical": "^2.6.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==" + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" + "cookie": "0.7.2", + "cookie-signature": "1.0.6" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dependencies": { - "safer-buffer": "~2.1.0" + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=0.8" + "node": ">= 8" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true, "engines": { "node": "*" } }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/csv-parse": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", + "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==", + "dev": true + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "dev": true, + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, "engines": { - "node": ">=4" + "node": ">=0.12" } }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "node_modules/daemonize2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/daemonize2/-/daemonize2-0.4.2.tgz", + "integrity": "sha512-dzB3qdxvcJ2AWyESI8xv90qZ4wZt4P+lvQUT1sVKcrbEKSvBk/8zkDlZvMyaWmoKe7DXLGu00z59b7K9gkzbqQ==", "engines": { - "node": ">=8.0.0" + "node": ">0.8.x" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "assert-plus": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "optional": true, "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "node_modules/axios": { - "version": "1.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.0.0-alpha.1.tgz", - "integrity": "sha512-p+meG161943WT+K7sJYquHR46xxi/z0tk7vnSmEf/LrfEAyiP+0uTMMYk1OEo1IRF18oGRhnFxN1y8fLcXaTMw==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "node": ">= 14" } }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-code-frame/node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", - "dev": true + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/babel-code-frame/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha512-gV/pe1YIaKNgLYnd1g9VNW80tcb7oV5qvNUxG7NM8rbDpnl6RGunzlAtlGSb0wEs3nesu2vHNiX9TSsZ+Y+RjA==", "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "node": ">=0.10.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dependencies": { - "safe-buffer": "5.1.2" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/bdd-lazy-var": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/bdd-lazy-var/-/bdd-lazy-var-2.5.2.tgz", - "integrity": "sha512-EBFk8uOTALu8crLChd+KfhHwv+frhxqE235jcy6NY1G1weB0Gyr5H7u0zvUnr1yduzX6M1I/ERaVGdY1cd+6ZQ==", - "dev": true - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "engines": { - "node": ">=8" + "node": ">=4.0.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "devOptional": true }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "strip-bom": "^4.0.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "node_modules/define-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-error/-/define-error-1.0.0.tgz", + "integrity": "sha512-HLdUb9mNENZ/tjnZGlITfOnx7wSM7a6e+WEDyhKSrsN/g5dJUS6kepG6qJApRLAdjRofQ2W8R3yrtI6GeyGGVg==", + "dependencies": { + "capture-stack-trace": "~1.0.0" + } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "optional": true, "dependencies": { - "fill-range": "^7.0.1" + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "node_modules/deglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", + "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", "dev": true, "dependencies": { - "base64-js": "^1.1.2" + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^3.0.9", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "node_modules/deglob/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, - "bin": { - "browserslist": "cli.js" + "engines": { + "node": "*" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/deglob/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=0.4.0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "optional": true }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "engines": { - "node": ">=10.16.0" + "node": ">=0.10" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "optional": true, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { - "node": ">= 10" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/cacache/node_modules/mkdirp": { + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dezalgo": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.3.1" } }, - "node_modules/cacache/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "optional": true, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, "dependencies": { - "aggregate-error": "^3.0.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://dotenvx.com" } }, - "node_modules/cacache/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g==", + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", "dev": true, "dependencies": { - "callsites": "^0.2.0" + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" }, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/caller-path/node_modules/callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A==", + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==" - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "node_modules/capture-stack-trace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "node_modules/electron-to-chromium": { + "version": "1.5.266", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz", + "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "iconv-lite": "^0.6.2" } }, - "node_modules/chai-http": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.2.1.tgz", - "integrity": "sha512-S2Ezy5uSVuOYleeXppfUKtTU/xbHCZyKkwjheNJ/76SGFTUPDpDkkpVdPNgC3sAO1Ap5J5LJ+/rXdLG8EGhCDA==", - "dev": true, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "@types/chai": "4", - "@types/superagent": "^3.8.3", - "cookiejar": "^2.1.1", - "is-ip": "^2.0.0", - "methods": "^1.1.2", - "qs": "^6.5.1", - "superagent": "^3.7.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "engines": { - "node": "*" + "once": "^1.4.0" } }, - "node_modules/charset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", - "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", - "dev": true, - "engines": { - "node": ">=4.0.0" + "node_modules/end-of-stream/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==" + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", - "dev": true - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/cli-color": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", - "dev": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { - "ansi-regex": "^2.1.1", - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.14", - "timers-ext": "^0.1.5" + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/cli-cursor": { + "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "restore-cursor": "^2.0.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/cli-progress": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.10.0.tgz", - "integrity": "sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw==", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "dependencies": { - "string-width": "^4.2.0" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-progress/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/cli-progress/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true }, - "node_modules/cli-progress/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, - "node_modules/cli-progress/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "d": "^1.0.2", + "ext": "^1.7.0" }, "engines": { - "node": ">=8" + "node": ">=0.12" } }, - "node_modules/cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "colors": "1.4.0" + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" } }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "devOptional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/command-line-args": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.0.2.tgz", - "integrity": "sha512-/qPcbL8zpqg53x4rAaqMFlRV4opN3pbla7I7k9x8kyOBMQoGT6WltjN6sXZuxOXw6DgdK7Ad+ijYS5gjcr7vlA==", - "dependencies": { - "argv-tools": "^0.1.1", - "array-back": "^2.0.0", - "find-replace": "^2.0.1", - "lodash.camelcase": "^4.3.0", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz", - "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==", - "dependencies": { - "array-back": "^2.0.0", - "chalk": "^2.4.1", - "table-layout": "^0.4.3", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/concurrent-queue": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/concurrent-queue/-/concurrent-queue-7.0.2.tgz", - "integrity": "sha512-icXDqc0JBdcQ3ubXiXcqVhuFeRrec39zVD2X5z7FKwwj0pImnfLWtAhGyX4CcBDD+YoqLesClOeRss+pZnm6/Q==", - "dependencies": { - "afterward": "~2.0.0", - "define-error": "~1.0.0", - "eventuate": "~4.0.0", - "object-assign": "~4.0.1", - "on-error": "~2.1.0", - "once": "~1.3.2", - "promise-polyfill": "~2.1.0" - } - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", - "dependencies": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-EZyO2G+zVFsMjU8jDtxs2iLS1DmryYNjC0s4/IHtsS6pWPUJSr0kt0UPOctRZosebPHYekb7bNcIBt4YW0S9bg==", - "dependencies": { - "cookie": "0.3.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true - }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/csv-parse": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", - "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==", - "dev": true - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/daemonize2": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/daemonize2/-/daemonize2-0.4.2.tgz", - "integrity": "sha512-dzB3qdxvcJ2AWyESI8xv90qZ4wZt4P+lvQUT1sVKcrbEKSvBk/8zkDlZvMyaWmoKe7DXLGu00z59b7K9gkzbqQ==", - "engines": { - "node": ">0.8.x" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha512-APql/TZ6FdLEpf2z7/X2a2zyqK8juYtqaSVqxw9mYoQ64CXkfU15AeLh8pUszT8+fnYjgm6t0aIYpWKJbnLkuA==" - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha512-gV/pe1YIaKNgLYnd1g9VNW80tcb7oV5qvNUxG7NM8rbDpnl6RGunzlAtlGSb0wEs3nesu2vHNiX9TSsZ+Y+RjA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-error/-/define-error-1.0.0.tgz", - "integrity": "sha512-HLdUb9mNENZ/tjnZGlITfOnx7wSM7a6e+WEDyhKSrsN/g5dJUS6kepG6qJApRLAdjRofQ2W8R3yrtI6GeyGGVg==", - "dependencies": { - "capture-stack-trace": "~1.0.0" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", - "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", - "dev": true, - "dependencies": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^3.0.9", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - } - }, - "node_modules/deglob/node_modules/ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-prefetch-control": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", - "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dottie": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.3.tgz", - "integrity": "sha512-4liA0PuRkZWQFQjwBypdxPfZaRWiv5tkhMXY2hzsa2pNf5s7U3m9cwUchfNKe8wZQxdGPQQzO6Rm2uGe0rvohQ==" - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", - "dev": true, - "dependencies": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "bin": { - "editorconfig": "bin/editorconfig" - } - }, - "node_modules/editorconfig/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/editorconfig/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/editorconfig/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.7.tgz", - "integrity": "sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.313", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz", - "integrity": "sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/end-of-stream/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/env-ci": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz", - "integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==", - "dependencies": { - "execa": "^5.0.0", - "fromentries": "^1.3.2", - "java-properties": "^1.0.0" - }, - "engines": { - "node": ">=10.17" - } - }, - "node_modules/env-ci/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/env-ci/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/env-ci/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/env-ci/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.1.tgz", - "integrity": "sha512-CyUMbmsjxedx8B0mr79mNOqetvkbij/zrXnFeK2zc3pGRn3/tibjiNAv/3UxFEyfMDjh+ZqTrJrEGBFiGfD5Og==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.12.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" - } - }, - "node_modules/eslint-config-google": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.12.0.tgz", - "integrity": "sha512-SHDM3nIRCJBACjf8c/H6FvCwRmKbphESNl3gJFBNbw4KYDLCONB3ABYLXDGF+iaVP9XSTND/Q5/PuGoFkp4xbg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "eslint": ">=5.4.0" - } - }, - "node_modules/eslint-config-standard": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", - "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0", - "eslint-plugin-import": ">=2.13.0", - "eslint-plugin-node": ">=7.0.0", - "eslint-plugin-promise": ">=4.0.0", - "eslint-plugin-standard": ">=4.0.0" - } - }, - "node_modules/eslint-config-standard-jsx": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz", - "integrity": "sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0", - "eslint-plugin-react": ">=7.11.1" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/eslint-plugin-es": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", - "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", - "dev": true, - "dependencies": { - "eslint-utils": "^1.4.2", - "regexpp": "^2.0.1" - }, - "engines": { - "node": ">=6.5.0" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", - "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", - "dev": true, - "dependencies": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "2.x - 5.x" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/eslint-plugin-import/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/eslint-plugin-import/node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-promise": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", - "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", - "dev": true, - "dependencies": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-standard": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.2.tgz", - "integrity": "sha512-nKptN8l7jksXkwFk++PhJB3cCDTcXOEyhISIN86Ue2feJ1LFyY3PrY3/xT2keXlJSY5bpmbiTG0f885/YKAvTA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peerDependencies": { - "eslint": ">=5.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "dependencies": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/eventuate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventuate/-/eventuate-4.0.0.tgz", - "integrity": "sha512-SysKo5/rgqCaXlO4H4DE62JXCFtDpdm+boWOzaeaYph3Xejy04Cc4/E2HDPnOES0MFb643WgKRlx09W2iVAIBw==", - "dependencies": { - "define-error": "~1.0.0", - "object-assign": "~3.0.0", - "shallow-copy": "0.0.1" - } - }, - "node_modules/eventuate/node_modules/object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/expect-ct": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", - "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dev": true, - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-redact": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-2.1.0.tgz", - "integrity": "sha512-0LkHpTLyadJavq9sRzzyqIoMZemWli77K2/MGOkafrR64B9ItrvZ9aT+jluvNDsv0YEHjSNhlMBtbokuoqii4A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "dependencies": { - "flat-cache": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-replace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-2.0.1.tgz", - "integrity": "sha512-LzDo3Fpa30FLIBsh6DCDnMN1KW2g4QKkqKmejlImgWY67dDFPX/x9Kh/op/GK522DchQXEvDi/wD48HKW49XOQ==", - "dependencies": { - "array-back": "^2.0.0", - "test-value": "^3.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-versions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", - "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", - "dependencies": { - "semver-regex": "^3.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flatstr": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", - "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" - }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/foreground-child/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/foreground-child/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau" - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/frameguard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", - "integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", - "dependencies": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/git-log-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", - "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - } - }, - "node_modules/git-log-parser/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/git-log-parser/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/git-log-parser/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/git-log-parser/node_modules/split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", - "dependencies": { - "through2": "~2.0.0" - } - }, - "node_modules/git-log-parser/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/git-log-parser/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { + "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -8036,381 +4617,299 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "optional": true, "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" }, "bin": { - "handlebars": "bin/handlebars" + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=0.4.7" + "node": ">=6.0" }, "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" + "source-map": "~0.6.1" } }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "node_modules/eslint": { + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", + "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.28.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" + "url": "https://eslint.org/donate" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": ">=5.16.0" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "ms": "^2.1.1" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" + "debug": "^3.2.7" }, "engines": { - "node": ">=8" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/hasha/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } + "node_modules/eslint-module-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, - "node_modules/helmet": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.2.tgz", - "integrity": "sha512-okUo+MeWgg00cKB8Csblu8EXgcIoDyb5ZS/3u0W4spCimeVuCUvVZ6Vj3O2VJ1Sxpyb8jCDvzu0L1KKT11pkIg==", + "node_modules/eslint-plugin-es": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", + "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", + "dev": true, "dependencies": { - "depd": "2.0.0", - "dns-prefetch-control": "0.2.0", - "dont-sniff-mimetype": "1.1.0", - "expect-ct": "0.2.0", - "feature-policy": "0.3.0", - "frameguard": "3.1.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.9.4", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "ienoopen": "1.1.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" + "eslint-utils": "^1.4.2", + "regexpp": "^2.0.1" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==", - "engines": { - "node": ">=4.0.0" + "node": ">=6.5.0" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "node_modules/helmet-csp": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.4.tgz", - "integrity": "sha512-qUgGx8+yk7Xl8XFEGI4MFu1oNmulxhQVTlV8HP8tV3tpfslCs30OZz/9uQqsWPvDISiu/NwrrCowsZBhFADYqg==", + "node_modules/eslint-plugin-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", + "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", + "dev": true, "dependencies": { - "bowser": "^2.7.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" + "eslint-plugin-es": "^1.3.1", + "eslint-utils": "^1.3.1", + "ignore": "^4.0.2", + "minimatch": "^3.0.4", + "resolve": "^1.8.1", + "semver": "^5.5.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "node_modules/hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==", + "node_modules/eslint-plugin-node/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, "engines": { - "node": ">=4.0.0" + "node": ">= 4" } }, - "node_modules/hook-std": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", - "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", - "engines": { - "node": ">=8" + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "node_modules/eslint-plugin-promise": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", + "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "node_modules/eslint-plugin-standard": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.2.tgz", + "integrity": "sha512-nKptN8l7jksXkwFk++PhJB3cCDTcXOEyhISIN86Ue2feJ1LFyY3PrY3/xT2keXlJSY5bpmbiTG0f885/YKAvTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": ">=5.0.0" } }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha512-TaZpC6cO/k3DFsjfzz1LnOobbVSq+J+7WpJxrVtN4L+8+BPQj8iBDRB2Dx49613N+e7/+ZSQ9ra+xZm7Blf4wg==" - }, - "node_modules/hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, "dependencies": { - "depd": "2.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=4.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "optional": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, "engines": { - "node": ">= 6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/eslint/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -8421,630 +4920,726 @@ } } }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/http-reasons": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", - "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" }, "engines": { "node": ">=0.10" } }, - "node_modules/httpntlm": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.7.tgz", - "integrity": "sha512-Pv2Rvrz8H0qv1Dne5mAdZ9JegG1uc6Vu5lwLflIY6s8RKHdZQbW39L4dYswSgqMDT0pkJILUTKjeyU0VPNRZjA==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "dependencies": { - "httpreq": ">=0.4.22", - "underscore": "~1.12.1" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=0.8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/httpntlm/node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "dev": true - }, - "node_modules/httpreq": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.5.2.tgz", - "integrity": "sha512-2Jm+x9WkExDOeFRrdBCBSpLPT5SokTcRHkunV3pjKmX/cx6av8zQ0WtHUMDrYb6O4hBFzNU6sxJEypvRUVYKnw==", + "node_modules/espree/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">= 6.15.1" + "node": ">=0.4.0" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "devOptional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "estraverse": "^5.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "engines": { - "node": ">=10.17.0" + "node": ">=0.10" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "optional": true, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/ienoopen": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", - "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true, "engines": { - "node": ">=4.0.0" + "node": ">=0.10.0" } }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { - "node": ">= 4" + "node": ">= 0.6" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "d": "1", + "es5-ext": "~0.10.14" } }, - "node_modules/import-from": { + "node_modules/eventuate": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", - "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "resolved": "https://registry.npmjs.org/eventuate/-/eventuate-4.0.0.tgz", + "integrity": "sha512-SysKo5/rgqCaXlO4H4DE62JXCFtDpdm+boWOzaeaYph3Xejy04Cc4/E2HDPnOES0MFb643WgKRlx09W2iVAIBw==", + "dependencies": { + "define-error": "~1.0.0", + "object-assign": "~3.0.0", + "shallow-copy": "0.0.1" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "devOptional": true, + "node_modules/eventuate/node_modules/object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", "engines": { - "node": ">=0.8.19" + "node": ">=0.10.0" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true - }, - "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dependencies": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" + "shebang-regex": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=0.10.0" } }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dependencies": { - "ansi-regex": "^4.1.0" + "isexe": "^2.0.0" }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "engines": { "node": ">=6" } }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "node_modules/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "optional": true + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", - "dev": true, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { - "node": ">= 0.10" + "node": ">= 0.6" } }, - "node_modules/is-admin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-admin/-/is-admin-3.0.0.tgz", - "integrity": "sha512-wOa3CXFJAu8BZ2BDtG9xYOOrsq6oiSvc2jFPy4X/HINx5bmJUcW8e+apItVbU2E7GIfBVaFVO7Zit4oAWtTJcw==", + "node_modules/express/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "execa": "^1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, + "node_modules/express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "has-bigints": "^1.0.1" + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { - "binary-extensions": "^2.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "^2.7.2" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.12" } }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "node_modules/external-editor/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", + "dev": true + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "optional": true, "dependencies": { - "has": "^1.0.3" + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/is-elevated": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-3.0.0.tgz", - "integrity": "sha512-wjcp6RkouU9jpg55zERl+BglvV5j4jx5c/EMvQ+d12j/+nIEenNWPu+qc0tCg3JkLodbKZMg1qhJzEwG4qjclg==", + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "optional": true, "dependencies": { - "is-admin": "^3.0.0", - "is-root": "^2.1.0" + "pump": "^3.0.0" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { + "node_modules/extract-zip/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "optional": true, + "dependencies": { + "pend": "~1.2.0" } }, - "node_modules/is-fullwidth-code-point": { + "node_modules/figures": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, "engines": { "node": ">=4" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/is-ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", - "integrity": "sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "ip-regex": "^2.0.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "optional": true - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" } }, - "node_modules/is-number-like": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", - "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dependencies": { - "lodash.isfinite": "^3.3.2" + "balanced-match": "^1.0.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dependencies": { - "has-tostringtag": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "node_modules/filesize": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.4.tgz", + "integrity": "sha512-ryBwPIIeErmxgPnm6cbESAzXjuEFubs+yKYLBZvg3CaiNcmkJChoOGcBSrZ6IwkMwPABwPpVXE6IlNdGJJrvEg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">= 10.4.0" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dependencies": { + "array-back": "^3.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" } }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dev": true }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "engines": { - "node": ">=6" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16" } }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { - "node": ">=0.10.0" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -9053,2283 +5648,2615 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.0.0" } }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dependencies": { - "text-extensions": "^1.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/is-typedarray": { + "node_modules/formidable/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==" + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dependencies": { - "call-bind": "^1.0.2" + "minipass": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "devOptional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "node_modules/ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", + "dependencies": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "engines": { + "node": ">=0.8.0" + } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/issue-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, "dependencies": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { - "node": ">=10.13" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "optional": true, "dependencies": { - "append-transform": "^2.0.0" + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" + "is-property": "^1.0.2" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 0.4" } }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/istanbul-lib-processinfo/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { - "node": ">= 8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/istanbul-lib-processinfo/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { - "glob": "^7.1.3" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-processinfo/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "shebang-regex": "^3.0.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/istanbul-lib-processinfo/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/istanbul-lib-processinfo/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "optional": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/get-uri/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "assert-plus": "^1.0.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "ms": "2.1.2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=10.13.0" } }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=10.0" } }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "devOptional": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/java-properties": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.4.0" } }, - "node_modules/js-beautify": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.7.tgz", - "integrity": "sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==", + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^0.15.3", - "glob": "^8.0.3", - "nopt": "^6.0.0" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" + "ansi-regex": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/js-beautify/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/js-beautify/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "dunder-proto": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/js-sha512": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", - "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "has-symbols": "^1.0.3" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "optional": true }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/hasha/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { - "universalify": "^2.0.0" + "function-bind": "^1.1.2" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">= 0.4" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "engines": [ - "node >= 0.2.0" - ] + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } }, - "node_modules/jsonschema": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.5.tgz", - "integrity": "sha512-kVTF+08x25PQ0CjuVc0gRM9EUPb0Fe9Ln/utFOgcdxEIOHuU7ooBk/UPTd7t1M91pP35m0MU1T8M5P7vP1bRRw==", + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": "*" + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jsprim": { + "node_modules/http-errors/node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "optional": true, "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/jsx-ast-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", - "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", - "dev": true, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, "dependencies": { - "array-includes": "^3.1.1", - "object.assign": "^4.1.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/http-reasons": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", + "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==", "dev": true }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/http-signature": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "node_modules/httpntlm": { + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", + "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", "dev": true, + "funding": [ + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/samdecrock" + } + ], "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "des.js": "^1.0.1", + "httpreq": ">=0.4.22", + "js-md4": "^0.3.2", + "underscore": "~1.12.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10.4.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/liquid-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", - "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==", + "node_modules/httpreq": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", "dev": true, "engines": { - "node": ">=4" + "node": ">= 6.15.1" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "optional": true, "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">=4" + "node": ">= 14" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "ms": "^2.1.3" }, "engines": { - "node": ">=4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { - "p-locate": "^5.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, - "node_modules/lodash.isfinite": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", - "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==" - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/import-in-the-middle": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", "dependencies": { - "color-convert": "^2.0.1" + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/import-in-the-middle/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": { + "acorn": "bin/acorn" }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "devOptional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "devOptional": true, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "devOptional": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.0.0" } }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "dev": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/inquirer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "bin": { - "loose-envify": "cli.js" + "engines": { + "node": ">=4" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/inquirer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "color-name": "1.1.3" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/inquirer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "es5-ext": "~0.10.2" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/inquirer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, "dependencies": { - "semver": "^6.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "optional": true, + "node_modules/inquirer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">= 10" + "node": ">= 0.4" } }, - "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "optional": true, "engines": { - "node": ">= 6" + "node": ">= 12" } }, - "node_modules/make-fetch-happen/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "dev": true, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=4" } }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "optional": true, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-admin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-admin/-/is-admin-3.0.0.tgz", + "integrity": "sha512-wOa3CXFJAu8BZ2BDtG9xYOOrsq6oiSvc2jFPy4X/HINx5bmJUcW8e+apItVbU2E7GIfBVaFVO7Zit4oAWtTJcw==", "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "execa": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "bin": { - "marked": "bin/marked.js" + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" }, "engines": { - "node": ">= 12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/marked-terminal": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.1.1.tgz", - "integrity": "sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { - "ansi-escapes": "^5.0.0", - "cardinal": "^2.1.1", - "chalk": "^5.0.0", - "cli-table3": "^0.6.1", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.2.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + "node": ">=8" } }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, "dependencies": { - "type-fest": "^1.0.2" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/marked-terminal/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "engines": { - "node": ">=10" + "node_modules/is-elevated": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-3.0.0.tgz", + "integrity": "sha512-wjcp6RkouU9jpg55zERl+BglvV5j4jx5c/EMvQ+d12j/+nIEenNWPu+qc0tCg3JkLodbKZMg1qhJzEwG4qjclg==", + "dependencies": { + "is-admin": "^3.0.0", + "is-root": "^2.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" + "node": ">= 0.4" }, - "engines": { - "node": ">=10.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/mime-format": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", - "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "dependencies": { - "charset": "^1.0.0" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "node_modules/is-ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", + "integrity": "sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g==", "dev": true, + "dependencies": { + "ip-regex": "^2.0.0" + }, "engines": { "node": ">=4" } }, - "node_modules/min-indent": { + "node_modules/is-lambda": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { - "node": ">= 6" + "node": ">=0.12.0" } }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" + "lodash.isfinite": "^3.3.2" } }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "optional": true, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, "engines": { "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" } }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "optional": true, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "call-bound": "^1.0.3" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, "dependencies": { - "minimist": "^1.2.6" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha-junit-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.0.0.tgz", - "integrity": "sha512-20HoWh2HEfhqmigfXOKUhZQyX23JImskc37ZOhIjBKoBEsb+4cAFRJpAVhFpnvsztLklW/gFVzsrobjLwmX4lA==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "dependencies": { - "debug": "^2.2.0", - "md5": "^2.1.0", - "mkdirp": "~0.5.1", - "strip-ansi": "^4.0.0", - "xml": "^1.0.0" + "which-typed-array": "^1.1.16" }, - "peerDependencies": { - "mocha": ">=2.2.5" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "optional": true }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bound": "^1.0.3" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/is2": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", + "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "optional": true, "dependencies": { - "argparse": "^2.0.1" + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=v0.10.0" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/is2/node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "optional": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "append-transform": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, - "node_modules/mocha/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "engines": { - "node": ">=0.10.0" + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/moment-timezone": { - "version": "0.5.38", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz", - "integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==", + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, "dependencies": { - "moment": ">= 2.9.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, "dependencies": { - "basic-auth": "~2.0.0", - "debug": "2.6.9", - "depd": "~1.1.2", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "semver": "^7.5.3" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/morgan/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" } }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, - "node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">=8" } }, - "node_modules/multer/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, "bin": { - "nanoid": "bin/nanoid.cjs" + "jake": "bin/cli.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=10" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, - "node_modules/nconf": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.12.0.tgz", - "integrity": "sha512-T3fZPw3c7Dfrz8JBQEbEcZJ2s8f7cUMpKuyBtsGQe0b71pcXx6gNh4oti2xh5dxB+gO9ufNfISBlGvvWtfyMcA==", + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, "dependencies": { - "async": "^3.0.0", - "ini": "^2.0.0", - "secure-keys": "^1.0.0", - "yargs": "^16.1.1" + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" }, "engines": { - "node": ">= 0.4.0" + "node": ">=14" } }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==" + "node_modules/js-beautify/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/newman": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/newman/-/newman-5.3.2.tgz", - "integrity": "sha512-cWy8pV0iwvMOZLTw3hkAHcwo2ZA0GKkXm8oUMn1Ltii3ZI2nKpnrg9QGdIT0hGHChRkX6prY5e3Aar7uykMGNg==", + "node_modules/js-beautify/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "dependencies": { - "async": "3.2.3", - "chardet": "1.4.0", - "cli-progress": "3.10.0", - "cli-table3": "0.6.1", - "colors": "1.4.0", - "commander": "7.2.0", - "csv-parse": "4.16.3", - "eventemitter3": "4.0.7", - "filesize": "8.0.7", - "lodash": "4.17.21", - "mkdirp": "1.0.4", - "postman-collection": "4.1.1", - "postman-collection-transformer": "4.1.6", - "postman-request": "2.88.1-postman.31", - "postman-runtime": "7.29.0", - "pretty-ms": "7.0.1", - "semver": "7.3.5", - "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", - "word-wrap": "1.2.3", - "xmlbuilder": "15.1.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "newman": "bin/newman.js" + "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/newman-reporter-junitfull": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/newman-reporter-junitfull/-/newman-reporter-junitfull-1.1.1.tgz", - "integrity": "sha512-ET5rU1qkeJ5yvFxcKQFkqGxWia50kdnufm1uzyeNYlUg6T+k07AvOS0mfp/Ejr0njnsiPfFLb9kC48F8pafq9A==", + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "lodash": "^4.17.10", - "moment": "^2.22.2", - "xmlbuilder": "^10.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "newman": ">=4" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/newman-reporter-junitfull/node_modules/xmlbuilder": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", - "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/newman/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "node_modules/js-beautify/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", "dev": true }, - "node_modules/newman/node_modules/chardet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", - "integrity": "sha512-NpwMDdSIprbYx1CLnfbxEIarI0Z+s9MssEgggMNheGM+WD68yOhV7IEA/3r6tr0yTRgQD0HuZJDw32s99i6L+A==", + "node_modules/js-sha512": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", + "integrity": "sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg==", "dev": true }, - "node_modules/newman/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dependencies": { - "yallist": "^4.0.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=10" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/newman/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "engines": { - "node": ">=10" + "node": ">= 10.16.0" } }, - "node_modules/newman/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { - "semver": "bin/semver.js" + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/newman/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, - "node_modules/nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "dependencies": { - "isarray": "0.0.1" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==", + "node_modules/jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, "engines": { - "node": ">=4.0.0" + "node": ">=18.0.0" } }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dependencies": { - "lodash": "^4.17.21" + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "engines": { + "node": "*" } }, - "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" } }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "optional": true, + "node_modules/jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dev": true, "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" }, "engines": { - "node": ">= 10.12.0" + "node": ">=4.0" } }, - "node_modules/node-gyp/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true, - "engines": { - "node": ">=8" + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "node_modules/jwk-to-pem": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz", + "integrity": "sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.6.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "optional": true, + "node_modules/keycloak-connect": { + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/keycloak-connect/-/keycloak-connect-26.1.1.tgz", + "integrity": "sha512-2wvNJXldB9Em+mp6liJ+AnftcJovFEvNhUgv3hblNDmVihBoBqn4zFlwLIN41lo0H8CicB2T86xZ5U2MiQ9FFA==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "jwk-to-pem": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=14" + }, + "optionalDependencies": { + "chromedriver": "latest" } }, - "node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "optional": true, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8.0" } }, - "node_modules/node-gyp/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/liquid-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", + "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/node-gyp/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "optional": true, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dev": true, "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "lie": "3.1.1" } }, - "node_modules/node-gyp/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", - "optional": true, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "optional": true, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, "dependencies": { - "glob": "^7.1.3" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "optional": true, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "loose-envify": "cli.js" } }, - "node_modules/node-gyp/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" } }, - "node_modules/node-gyp/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "es5-ext": "~0.10.2" } }, - "node_modules/node-gyp/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "semver": "^6.0.0" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "semver": "bin/semver.js" } }, - "node_modules/node-gyp/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true - }, - "node_modules/node-oauth1": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-oauth1/-/node-oauth1-1.3.0.tgz", - "integrity": "sha512-0yggixNfrA1KcBwvh/Hy2xAS1Wfs9dcg6TdFf2zN7gilcAigMdrtZ4ybrBSXBgLvGDw9V1p2MRnGBMq7XjTWLg==", - "dev": true - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, "dependencies": { - "process-on-spawn": "^1.0.0" + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/nodemailer": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", - "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==", - "engines": { - "node": ">=6.0.0" + "node": ">= 10" } }, - "node_modules/nodemailer-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.3.0.tgz", - "integrity": "sha512-5P5+lR0+sWvk1UZGFoTida33dG0xAqk1Pv0t7cPlrJ09dLdZWh/kenxb0vOoUoNVOci4gmSYOnMHzvheSBeFag==" - }, - "node_modules/nodemailer-shared": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.0.4.tgz", - "integrity": "sha512-tfqdDPbj6L3PywhNqCa988bDxxnffo3Gw0DZkaYwOYVsnMZFgF7LoffS4FAFbiGb1M+mcNBPX/Vy9l0A95fJAg==", + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, "dependencies": { - "nodemailer-fetch": "1.3.0" + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "node_modules/nodemailer-smtp-transport": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.4.2.tgz", - "integrity": "sha512-VD3oQ5Xzgszg+2f0H3qAnXhciF6pZ9j2hHNaJqHEzh+caEs/cU1dDmCbpdN2bJxPYBeCilA2XshhtEnvxJgZGQ==", + "node_modules/make-fetch-happen/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, "dependencies": { - "nodemailer-shared": "1.0.4", - "nodemailer-wellknown": "0.1.8", - "smtp-connection": "2.3.2" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/nodemailer-wellknown": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.8.tgz", - "integrity": "sha512-kxDRGWY6ZYtcKsCr4IMw5B9nST0EKK8Ay/JjgK96lBEdpt6nRl2ds5khTVv/BGLECbLIAzOsmIwP7KUt1C9frA==" - }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "dev": true, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/normalize-package-data/node_modules/lru-cache": { + "node_modules/make-fetch-happen/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -11337,861 +8264,1050 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "node_modules/make-fetch-happen/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, "dependencies": { - "lru-cache": "^6.0.0" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 10" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { - "node": ">=10" + "node": ">= 0.6" + } + }, + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "dev": true, + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dependencies": { - "path-key": "^2.0.0" + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" }, "engines": { "node": ">=4" } }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" } }, - "node_modules/nyc": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", - "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", + "node_modules/mime-format": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", + "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", "dev": true, "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "js-yaml": "^3.13.1", - "make-dir": "^3.0.0", - "node-preload": "^0.2.0", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "uuid": "^3.3.3", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" + "charset": "^1.0.0" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" }, "engines": { - "node": ">=8.9" + "node": ">= 0.6" } }, - "node_modules/nyc/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/nyc/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, - "node_modules/nyc/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=7.0.0" + "node": "*" } }, - "node_modules/nyc/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/nyc/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, "dependencies": { - "p-locate": "^4.1.0" + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" }, "engines": { "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" } }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, "dependencies": { - "p-try": "^2.0.0" + "minipass": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, "dependencies": { - "p-limit": "^2.2.0" + "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/nyc/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dependencies": { - "glob": "^7.1.3" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, "bin": { - "rimraf": "bin.js" + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/nyc/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/mocha": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", + "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" }, "engines": { - "node": ">=8" + "node": ">= 14.0.0" } }, - "node_modules/nyc/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "mocha": ">=2.2.5" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/mocha-junit-reporter/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "node_modules/mocha-junit-reporter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", - "integrity": "sha512-c6legOHWepAbWnp3j5SRUMpxCXBKI4rD7A5Osn9IzZ8w4O/KccXdW0lqdkQKbpk0eHGjNgKihgzY6WuEq99Tfw==", - "engines": { - "node": ">=0.10.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "node_modules/mocha/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/on-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-error/-/on-error-2.1.0.tgz", - "integrity": "sha512-wpKXxCW2wXLI+9DB9DDBVuOCN9C5rjyaP4GWwqhgrSd2ys1Vyc9yGaPmC5HSOdQ30x9zCLozi9mHx3lm01E+LQ==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { - "node": ">= 0.8" + "node": "*" } }, - "node_modules/once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", "dependencies": { - "wrappy": "1" + "moment": "^2.29.4" + }, + "engines": { + "node": "*" } }, - "node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", "dependencies": { - "mimic-fn": "^1.0.0" + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">= 6.0.0" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "minimist": "^1.2.6" }, - "engines": { - "node": ">= 0.8.0" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/os": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", - "integrity": "sha512-jg06S2xr5De63mLjZVJDf3/k37tpjppr2LR7MUOsxv8XuUCVpCnvbCksXCBcB5gQqQf/K0+87WGTRlAj5q7r1A==" - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, + "node_modules/multer/node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, - "node_modules/p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/mysql2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.10.1.tgz", + "integrity": "sha512-6zo1T3GILsXMCex3YEu7hCz2OXLUarxFsxvFcUHWMpkPtmZLeTTWgRdc1gWyNJiYt6AxITmIf9bZDRy/jAfWew==", "dependencies": { - "p-map": "^2.0.0" + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" }, "engines": { - "node": ">=8" + "node": ">= 8.0" } }, - "node_modules/p-filter/node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, "engines": { - "node": ">=4" + "node": ">=12.0.0" } }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nconf": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.12.1.tgz", + "integrity": "sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==", "dependencies": { - "yocto-queue": "^0.1.0" + "async": "^3.0.0", + "ini": "^2.0.0", + "secure-keys": "^1.0.0", + "yargs": "^16.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4.0" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/nconf/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, + "node_modules/nconf/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dependencies": { - "aggregate-error": "^3.0.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "optional": true, "engines": { - "node": ">=6" + "node": ">= 0.4.0" } }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "node_modules/newman": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/newman/-/newman-6.2.1.tgz", + "integrity": "sha512-Zq8Sr5GFF+OXs5yIbyglLMKMh1WNMjYVV0yZaSBZ+DIgQOIWcxT8QTfbrl/YUGrLyT4rjpu+yZ/Z+kozw79GEA==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.5", + "chardet": "2.0.0", + "cli-progress": "3.12.0", + "cli-table3": "0.6.5", + "colors": "1.4.0", + "commander": "11.1.0", + "csv-parse": "4.16.3", + "filesize": "10.1.4", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mkdirp": "3.0.1", + "postman-collection": "4.4.0", + "postman-collection-transformer": "4.1.8", + "postman-request": "2.88.1-postman.34", + "postman-runtime": "7.39.1", + "pretty-ms": "7.0.1", + "semver": "7.6.3", + "serialised-error": "1.1.3", + "word-wrap": "1.2.5", + "xmlbuilder": "15.1.1" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" + "bin": { + "newman": "bin/newman.js" }, "engines": { - "node": ">=6" + "node": ">=16" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/newman-reporter-junitfull": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/newman-reporter-junitfull/-/newman-reporter-junitfull-1.1.1.tgz", + "integrity": "sha512-ET5rU1qkeJ5yvFxcKQFkqGxWia50kdnufm1uzyeNYlUg6T+k07AvOS0mfp/Ejr0njnsiPfFLb9kC48F8pafq9A==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "lodash": "^4.17.10", + "moment": "^2.22.2", + "xmlbuilder": "^10.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "newman": ">=4" } }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "node_modules/newman-reporter-junitfull/node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } + "node_modules/newman/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } + "node_modules/nise/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/node-abi": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", + "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", + "dependencies": { + "semver": "^7.3.5" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/node-fetch-npm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", + "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", + "deprecated": "This module is not used anymore, npm uses minipass-fetch for its fetch implementation now", + "dependencies": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + }, "engines": { "node": ">=4" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/node-forge": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "engines": { - "node": ">=8" + "node": ">= 6.13.0" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, "engines": { - "node": "*" + "node": ">= 10.12.0" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "node_modules/node-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=8.6" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/node-gyp/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "optional": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/pino": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.6.1.tgz", - "integrity": "sha512-DOgm7rn6ctBkBYemHXSLj7+j3o3U1q1FWBXbHcprur8mA93QcJSycEkEqhqKiFB9Mx/3Qld2FGr6+9yfQza0kA==", + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, "dependencies": { - "fast-redact": "^2.0.0", - "fast-safe-stringify": "^2.0.7", - "flatstr": "^1.0.12", - "pino-std-serializers": "^2.4.2", - "quick-format-unescaped": "^4.0.1", - "sonic-boom": "^1.0.2" + "abbrev": "1" }, "bin": { - "pino": "bin.js" + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/pino-std-serializers": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz", - "integrity": "sha512-wXqbqSrIhE58TdrxxlfLwU9eDhrzppQDvGhBEr1gYbzzM4KKo3Y63gSjiDXRKLVS2UOXdPNR2v+KnQgNrs+xUg==" - }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "node_modules/node-gyp/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "optional": true, "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "node_modules/node-oauth1": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-oauth1/-/node-oauth1-1.3.0.tgz", + "integrity": "sha512-0yggixNfrA1KcBwvh/Hy2xAS1Wfs9dcg6TdFf2zN7gilcAigMdrtZ4ybrBSXBgLvGDw9V1p2MRnGBMq7XjTWLg==", + "dev": true + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "process-on-spawn": "^1.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "dependencies": { - "p-try": "^1.0.0" - }, + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dependencies": { - "p-limit": "^1.1.0" + "path-key": "^2.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "engines": { "node": ">=4" } }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, "engines": { - "node": ">=4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "dependencies": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" }, "engines": { - "node": ">=0.10" + "node": ">=8.9" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "node_modules/pkg-dir/node_modules/find-up": { + "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", @@ -12204,7 +9320,28 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/locate-path": { + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", @@ -12216,7 +9353,7 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/p-limit": { + "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", @@ -12231,7 +9368,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/p-locate": { + "node_modules/nyc/node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", @@ -12243,662 +9380,807 @@ "node": ">=8" } }, - "node_modules/pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/portscanner": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", - "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "dependencies": { - "async": "^2.6.0", - "is-number-like": "^1.0.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=0.4", - "npm": ">=1.0.0" + "node": ">=8" } }, - "node_modules/portscanner/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, "dependencies": { - "lodash": "^4.17.14" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" } }, - "node_modules/postman-collection": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.1.1.tgz", - "integrity": "sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg==", + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "dependencies": { - "faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.34", - "postman-url-encoder": "3.0.5", - "semver": "7.3.5", - "uuid": "8.3.2" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/oauth4webapi": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.3.tgz", + "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", + "integrity": "sha512-c6legOHWepAbWnp3j5SRUMpxCXBKI4rD7A5Osn9IzZ8w4O/KccXdW0lqdkQKbpk0eHGjNgKihgzY6WuEq99Tfw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-error/-/on-error-2.1.0.tgz", + "integrity": "sha512-wpKXxCW2wXLI+9DB9DDBVuOCN9C5rjyaP4GWwqhgrSd2ys1Vyc9yGaPmC5HSOdQ30x9zCLozi9mHx3lm01E+LQ==" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, - "node_modules/postman-collection-transformer": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.6.tgz", - "integrity": "sha512-xvdQb6sZoWcG9xZXUPSuxocjcd6WCZlINlGGiuHdSfxhgiwQhj9qhF0JRFbagZ8xB0+pYUairD5MiCENc6DEVA==", - "dev": true, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "commander": "8.3.0", - "inherits": "2.0.4", - "lodash": "4.17.21", - "semver": "7.3.5", - "strip-json-comments": "3.1.1" - }, - "bin": { - "postman-collection-transformer": "bin/transform-collection.js" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/postman-collection-transformer/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "engines": { - "node": ">= 12" + "node": ">= 0.8" } }, - "node_modules/postman-collection-transformer/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "node_modules/once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "wrappy": "1" } }, - "node_modules/postman-collection-transformer/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "mimic-fn": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/postman-collection-transformer/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/openid-client": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.1.tgz", + "integrity": "sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==", + "optional": true, + "dependencies": { + "jose": "^6.1.0", + "oauth4webapi": "^3.8.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/panva" } }, - "node_modules/postman-collection-transformer/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/openid-client/node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } }, - "node_modules/postman-collection/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/postman-collection/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postman-collection/node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/postman-collection/node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postman-collection/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "p-limit": "^3.0.2" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postman-collection/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/postman-collection/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/postman-request": { - "version": "2.88.1-postman.31", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.31.tgz", - "integrity": "sha512-OJbYqP7ItxQ84yHyuNpDywCZB0HYbpHJisMQ9lb1cSL3N5H3Td6a2+3l/a74UMd3u82BiGC5yQyYmdOIETP/nQ==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "optional": true, "dependencies": { - "@postman/form-data": "~3.1.1", - "@postman/tunnel-agent": "^0.6.3", - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "brotli": "~1.3.2", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "har-validator": "~5.1.3", - "http-signature": "~1.3.1", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "stream-length": "^1.0.2", - "tough-cookie": "~2.5.0", - "uuid": "^3.3.2" + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, - "node_modules/postman-request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=0.6" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/postman-request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "optional": true, "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "degenerator": "^5.0.0", + "netmask": "^2.0.2" }, "engines": { - "node": ">=0.8" + "node": ">= 14" } }, - "node_modules/postman-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.29.0.tgz", - "integrity": "sha512-eXxHREE/fUpohkGPRgBY1YccSGx9cyW3mtGiPyIE4zD5fYzasgBHqW6kbEND3Xrd3yf/uht/YI1H8O7J1+A1+w==", + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "dependencies": { - "async": "3.2.3", - "aws4": "1.11.0", - "handlebars": "4.7.7", - "httpntlm": "1.7.7", - "js-sha512": "0.8.0", - "lodash": "4.17.21", - "mime-types": "2.1.34", - "node-oauth1": "1.3.0", - "performance-now": "2.1.0", - "postman-collection": "4.1.1", - "postman-request": "2.88.1-postman.31", - "postman-sandbox": "4.0.6", - "postman-url-encoder": "3.0.5", - "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", - "uuid": "8.3.2" + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/postman-runtime/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, - "node_modules/postman-runtime/node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, - "node_modules/postman-runtime/node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/postman-runtime/node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/postman-runtime/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "engines": { + "node": ">=6" } }, - "node_modules/postman-sandbox": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.0.6.tgz", - "integrity": "sha512-PPRanSNEE4zy3kO7CeSBHmAfJnGdD9ecHY/Mjh26CQuZZarGkNO8c0U/n+xX3+5M1BRNc82UYq6YCtdsSDqcng==", - "dev": true, - "dependencies": { - "lodash": "4.17.21", - "teleport-javascript": "1.0.0", - "uvm": "2.0.2" - }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/postman-url-encoder": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", - "integrity": "sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "parse-ms": "^2.1.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "dev": true, "dependencies": { - "fromentries": "^1.2.0" + "pify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/path-type/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { - "node": ">=0.4.0" + "node": ">=0.10.0" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "optional": true }, - "node_modules/promise-polyfill": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-2.1.4.tgz", - "integrity": "sha512-/DVUJXyaiYr7Pu0q2qPV/OtABpiukAHswJb9VV/tUVFsvC5iZUTyVPxfEr8cIVatGa5/Mxeli8QMyzAMBmoiYg==" + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "optional": true, + "node_modules/pg": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", + "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" }, "engines": { - "node": ">=10" + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } } }, - "node_modules/promise-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "optional": true, - "engines": { - "node": ">= 4" - } + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "optional": true }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==" }, - "node_modules/prop-types/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "peerDependencies": { + "pg": ">=8.0" + } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "split2": "^4.1.0" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=4" } }, - "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "node_modules/pino": { + "version": "9.13.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.13.1.tgz", + "integrity": "sha512-Szuj+ViDTjKPQYiKumGmEn3frdl+ZPSdosHyt9SnUevFosOkMY2b7ipxlEctNKPmMD/VibeBI+ZcZCJK+4DPuw==", "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "slow-redact": "^0.3.0", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "pino": "bin.js" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dependencies": { + "split2": "^4.0.0" + } }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/randombytes": { + "node_modules/pkg-conf/node_modules/find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "dependencies": { - "safe-buffer": "^5.1.0" + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "p-try": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "p-limit": "^1.1.0" }, - "bin": { - "rc": "cli.js" + "engines": { + "node": ">=4" } }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", + "dev": true, "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/find-up": { + "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -12907,10 +10189,11 @@ "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { + "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -12918,10 +10201,11 @@ "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { + "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -12932,10 +10216,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { + "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -12943,407 +10228,638 @@ "node": ">=8" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "node_modules/pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, "engines": { - "node": ">=8" + "node": ">=0.4", + "npm": ">=1.0.0" } }, - "node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "node_modules/portscanner/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "lodash": "^4.17.14" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "dependencies": { - "picomatch": "^2.2.1" + "xtend": "^4.0.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=0.10.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/postman-collection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", + "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", + "dev": true, "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.5", + "semver": "7.5.4", + "uuid": "8.3.2" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "node_modules/postman-collection-transformer": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.8.tgz", + "integrity": "sha512-smJ6X7Z7kbg6hp7JZPFixrSN3J3WkQed7DrWCC5tF7IxOMpFLqhtTtGssY8nD1inP8+mJf+N72Pf2ttUAHgBKw==", + "dev": true, "dependencies": { - "esprima": "~4.0.0" + "commander": "8.3.0", + "inherits": "2.0.4", + "lodash": "4.17.21", + "semver": "7.5.4", + "strip-json-comments": "3.1.1" + }, + "bin": { + "postman-collection-transformer": "bin/transform-collection.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "node_modules/postman-collection-transformer/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 12" } }, - "node_modules/referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==", + "node_modules/postman-collection-transformer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=4.0.0" + "node": ">=10" } }, - "node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "node_modules/postman-collection-transformer/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "node_modules/postman-collection/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "node_modules/postman-collection/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "engines": { - "node": ">=6.5.0" - } - }, - "node_modules/registry-auth-token": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", - "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dependencies": { - "@pnpm/npm-conf": "^1.0.4" + "yallist": "^4.0.0" }, "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "node_modules/postman-collection/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "es6-error": "^4.0.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "node_modules/postman-collection/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/postman-request": { + "version": "2.88.1-postman.34", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.34.tgz", + "integrity": "sha512-GkolJ4cIzgamcwHRDkeZc/taFWO1u2HuGNML47K9ZAsFH2LdEkS5Yy8QanpzhjydzV3WWthl9v60J8E7SjKodQ==", + "dev": true, "dependencies": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.3", "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", + "aws4": "^1.12.0", + "brotli": "^1.3.3", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", + "har-validator": "~5.1.3", + "http-signature": "~1.3.1", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", + "mime-types": "^2.1.35", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.2", + "qs": "~6.5.3", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "stream-length": "^1.0.2", + "uuid": "^8.3.2" }, "engines": { - "node": ">= 4" + "node": ">= 6" } }, - "node_modules/request-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.4.tgz", - "integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==", - "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "dependencies": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, + "node_modules/postman-request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.6" + } + }, + "node_modules/postman-request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/postman-runtime": { + "version": "7.39.1", + "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.39.1.tgz", + "integrity": "sha512-IRNrBE0l1K3ZqQhQVYgF6MPuqOB9HqYncal+a7RpSS+sysKLhJMkC9SfUn1HVuOpokdPkK92ykvPzj8kCOLYAg==", + "dev": true, + "dependencies": { + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.5", + "aws4": "1.12.0", + "handlebars": "4.7.8", + "httpntlm": "1.8.13", + "jose": "4.14.4", + "js-sha512": "0.9.0", + "lodash": "4.17.21", + "mime-types": "2.1.35", + "node-forge": "1.3.1", + "node-oauth1": "1.3.0", + "performance-now": "2.1.0", + "postman-collection": "4.4.0", + "postman-request": "2.88.1-postman.34", + "postman-sandbox": "4.7.1", + "postman-url-encoder": "3.0.5", + "serialised-error": "1.1.3", + "strip-json-comments": "3.1.1", + "uuid": "8.3.2" }, - "peerDependencies": { - "request": "^2.34" + "engines": { + "node": ">=12" } }, - "node_modules/request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "node_modules/postman-runtime/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/postman-runtime/node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "dev": true + }, + "node_modules/postman-runtime/node_modules/jose": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", + "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/postman-runtime/node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/postman-runtime/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/postman-sandbox": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.7.1.tgz", + "integrity": "sha512-H2wYSLK0mB588IaxoLrLoPbpmxsIcwFtgaK2c8gAsAQ+TgYFePwb4qdeVcYDMqmwrLd77/ViXkjasP/sBMz1sQ==", + "dev": true, "dependencies": { - "lodash": "^4.17.11" + "lodash": "4.17.21", + "postman-collection": "4.4.0", + "teleport-javascript": "1.0.0", + "uvm": "2.1.1" }, "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" + "node": ">=10" } }, - "node_modules/request-promise/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/postman-url-encoder": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", + "integrity": "sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==", + "dev": true, "dependencies": { - "psl": "^1.1.28", "punycode": "^2.1.1" }, "engines": { - "node": ">=0.8" + "node": ">=10" } }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" }, "engines": { - "node": ">= 0.12" + "node": ">=10" } }, - "node_modules/request/node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "parse-ms": "^2.1.0" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/request/node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "fromentries": "^1.2.0" }, "engines": { - "node": ">=0.6.0" + "node": ">=8" } }, - "node_modules/request/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, "engines": { - "node": ">=0.6" + "node": ">=0.4.0" } }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-polyfill": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-2.1.4.tgz", + "integrity": "sha512-/DVUJXyaiYr7Pu0q2qPV/OtABpiukAHswJb9VV/tUVFsvC5iZUTyVPxfEr8cIVatGa5/Mxeli8QMyzAMBmoiYg==" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, "dependencies": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": ">=0.8" + "node": ">=10" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, - "node_modules/require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", - "dev": true, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, "dependencies": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/require-uncached/node_modules/resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", - "dev": true, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "optional": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ms": "^2.1.3" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "optional": true, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "dev": true, "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "punycode": "^2.3.1" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" + "funding": { + "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/retry-as-promised": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.1.0.tgz", - "integrity": "sha512-g6T9rr5G4AfRVJZcnCbbmpcTDw8NJSnmVrvrJ9Pm9OWAzigocIcFp4+ItwHGJIr0wx0YzwlCJOvvEKQrZhzPOw==", + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dependencies": { - "any-promise": "^1.3.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dependencies": { - "glob": "^7.1.3" + "side-channel": "^1.0.6" }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -13357,701 +10873,869 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + ] + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" } }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { - "npm": ">=2.0.0" + "node": ">= 0.8" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=4" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/secure-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", - "integrity": "sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==" + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/semantic-release": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-19.0.3.tgz", - "integrity": "sha512-HaFbydST1cDKZHuFZxB8DTrBLJVK/AnDExpK0s3EqLIAAUAHUgnd+VSJCUtTYQKkAkauL8G9CucODrVCc7BuAA==", - "dependencies": { - "@semantic-release/commit-analyzer": "^9.0.2", - "@semantic-release/error": "^3.0.0", - "@semantic-release/github": "^8.0.0", - "@semantic-release/npm": "^9.0.0", - "@semantic-release/release-notes-generator": "^10.0.0", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.0.0", - "env-ci": "^5.0.0", - "execa": "^5.0.0", - "figures": "^3.0.0", - "find-versions": "^4.0.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^4.0.0", - "lodash": "^4.17.21", - "marked": "^4.0.10", - "marked-terminal": "^5.0.0", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^7.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^3.1.1", - "signale": "^1.2.1", - "yargs": "^16.2.0" + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, - "bin": { - "semantic-release": "bin/semantic-release.js" + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" }, "engines": { - "node": ">=16 || ^14.17" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "p-limit": "^1.1.0" }, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/read-pkg/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "error-ex": "^1.2.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/read-pkg/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dependencies": { - "yallist": "^4.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" }, "engines": { - "node": ">=10" + "node": ">=8.10.0" } }, - "node_modules/semantic-release/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", "engines": { - "node": ">=6" + "node": ">= 12.13.0" } }, - "node_modules/semantic-release/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/semantic-release/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, "dependencies": { - "path-key": "^3.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=6.5.0" } }, - "node_modules/semantic-release/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "es6-error": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, + "node_modules/request": { + "resolved": "node_modules/@kubernetes/client-node/@cypress/request@3.0.8", + "link": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, - "node_modules/semantic-release/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "ms": "^2.1.3" }, "engines": { - "node": ">= 8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/semantic-release/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/require-in-the-middle/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "bin": { - "semver": "bin/semver" - } + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "node_modules/require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", + "dev": true, "dependencies": { - "semver": "^6.3.0" + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "node_modules/require-uncached/node_modules/resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semver-regex": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", - "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/send/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, "engines": { - "node": ">= 0.6" + "node": ">= 4" } }, - "node_modules/send/node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==" }, - "node_modules/send/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "node_modules/rfc4648": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz", + "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==" + }, + "node_modules/rhea": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/rhea/-/rhea-3.0.4.tgz", + "integrity": "sha512-n3kw8syCdrsfJ72w3rohpoHHlmv/RZZEP9VY5BVjjo0sEGIt4YSKypBgaiA+OUSgJAzLjOECYecsclG5xbYtZw==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" + "debug": "^4.3.3" } }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" + "node_modules/rhea/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" }, "engines": { - "node": ">=4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/send/node_modules/ms": { + "node_modules/rhea/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/send/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "devOptional": true, "dependencies": { - "ee-first": "1.1.1" + "glob": "^7.1.3" }, - "engines": { - "node": ">= 0.8" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sequelize": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.0.tgz", - "integrity": "sha512-m8Wi90rs3NZP9coXE52c7PL4Q078nwYZXqt1IxPvgki7nOFn0p/F0eKsYDBXCPw9G8/BCEa6zZNk0DQUAT4ypA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "devOptional": true, "dependencies": { - "@types/debug": "^4.1.7", - "@types/validator": "^13.7.1", - "debug": "^4.3.3", - "dottie": "^2.0.2", - "inflection": "^1.13.2", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "moment-timezone": "^0.5.35", - "pg-connection-string": "^2.5.0", - "retry-as-promised": "^7.0.3", - "semver": "^7.3.5", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.7.0", - "wkx": "^0.5.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10.0.0" + "node": "*" }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sequelize-cli": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-5.5.0.tgz", - "integrity": "sha512-twVQ02alCpr2XvxNmpi32C48WZs6xHTH1OFTfTS5Meg3BVqOM8ghiZoml4FITFjlD8sAJSQjlAHTwqTbuolA6Q==", + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "dependencies": { - "bluebird": "^3.5.3", - "cli-color": "^1.4.0", - "fs-extra": "^7.0.1", - "js-beautify": "^1.8.8", - "lodash": "^4.17.5", - "resolve": "^1.5.0", - "umzug": "^2.1.0", - "yargs": "^13.1.0" - }, - "bin": { - "sequelize": "lib/sequelize" + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=8.0" } }, - "node_modules/sequelize-cli/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.12.0" } }, - "node_modules/sequelize-cli/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "queue-microtask": "^1.2.2" } }, - "node_modules/sequelize-cli/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/sequelize-cli/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", "dev": true, "dependencies": { - "locate-path": "^3.0.0" + "symbol-observable": "1.0.1" }, "engines": { - "node": ">=6" + "npm": ">=2.0.0" } }, - "node_modules/sequelize-cli/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sequelize-cli/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, - "node_modules/sequelize-cli/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sequelize-cli/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sequelize-cli/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/secure-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", + "integrity": "sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { - "p-limit": "^2.0.0" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">=6" + "node": ">= 0.8.0" } }, - "node_modules/sequelize-cli/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/sequelize-cli/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, + "node_modules/send/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/sequelize-cli/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], "dependencies": { - "ansi-regex": "^4.1.0" + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/sequelize-cli/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } } }, - "node_modules/sequelize-cli/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/sequelize-cli": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", + "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "cli-color": "^2.0.3", + "fs-extra": "^9.1.0", + "js-beautify": "^1.14.5", + "lodash": "^4.17.21", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" }, "engines": { - "node": ">=6" + "node": ">=10.0.0" } }, - "node_modules/sequelize-cli/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/sequelize-cli/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "node_modules/sequelize-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/sequelize-cli/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "node_modules/sequelize-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, "node_modules/sequelize-pool": { @@ -14063,11 +11747,11 @@ } }, "node_modules/sequelize/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -14078,40 +11762,10 @@ } } }, - "node_modules/sequelize/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/sequelize/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/sequelize/node_modules/retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" - }, - "node_modules/sequelize/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/sequelize/node_modules/uuid": { "version": "8.3.2", @@ -14121,11 +11775,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/sequelize/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/serialised-error": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/serialised-error/-/serialised-error-1.1.3.tgz", @@ -14137,24 +11786,61 @@ "uuid": "^3.0.0" } }, + "node_modules/serialised-error/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -14163,7 +11849,54 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "devOptional": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -14176,117 +11909,201 @@ "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dependencies": { - "shebang-regex": "^1.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", - "dev": true - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" } }, "node_modules/sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, "node_modules/sinon-chai": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.3.0.tgz", - "integrity": "sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", + "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", "dev": true, "peerDependencies": { "chai": "^4.0.0", - "sinon": ">=4.0.0 <8.0.0" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" + "sinon": ">=4.0.0" } }, "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" } }, + "node_modules/slow-redact": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/slow-redact/-/slow-redact-0.3.2.tgz", + "integrity": "sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -14297,19 +12114,16 @@ "npm": ">= 3.0.0" } }, - "node_modules/smtp-connection": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.3.2.tgz", - "integrity": "sha512-ORefDrSgddCYkJE7oH3jB6WRv2Z7laZLCMRYD4GFFB8oaZw/hnil7exDBP4yBYs3OgschOuvVdCauUQAblTWvQ==", - "dependencies": { - "nodemailer-shared": "1.0.4" - } - }, "node_modules/snyk": { - "version": "1.1109.0", - "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.1109.0.tgz", - "integrity": "sha512-sBcibkAfcq6nXr6t0GieDjdc8kQfEf429+M1VKavGfLaJPQNIqSjtOhQJ5FcZqaB/mCWa1szektkHeyAiB4m9A==", + "version": "1.1301.0", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.1301.0.tgz", + "integrity": "sha512-kTb8F9L1PlI3nYWlp60wnSGWGmcRs6bBtSBl9s8YYhAiFZNseIZfXolQXBSCaya5QlcxzfH1pb4aqCNMbi0tgg==", "dev": true, + "hasInstallScript": true, + "dependencies": { + "@sentry/node": "^7.36.0", + "global-agent": "^3.0.0" + }, "bin": { "snyk": "bin/snyk" }, @@ -14318,40 +12132,40 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "optional": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "optional": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "optional": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -14363,33 +12177,28 @@ } }, "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "optional": true }, "node_modules/sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "dependencies": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" + "atomic-sleep": "^1.0.0" } }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==" - }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -14407,117 +12216,61 @@ "node": ">=8" } }, - "node_modules/spawn-wrap/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/spawn-wrap/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true }, "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "engines": { - "node": ">= 6" - } - }, - "node_modules/split2/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">= 10.x" } }, "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true }, "node_modules/sqlite3": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.5.tgz", - "integrity": "sha512-7sP16i4wI+yKnGOO2q2ijze7EjQ9US+Vw7DYYwxfFtqNZDGgBcEw0oeDaDvUTq66uJOzVd/z6MkIg+c9erSJKg==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "node-addon-api": "^4.2.0", + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", "tar": "^6.1.11" }, "optionalDependencies": { @@ -14532,10 +12285,54 @@ } } }, + "node_modules/sqlite3/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sqlite3/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sqlite3/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -14600,61 +12397,119 @@ "node": ">=4" } }, - "node_modules/standard-engine": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-9.0.0.tgz", - "integrity": "sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w==", + "node_modules/standard-engine": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-9.0.0.tgz", + "integrity": "sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w==", + "dev": true, + "dependencies": { + "deglob": "^2.1.0", + "get-stdin": "^6.0.0", + "minimist": "^1.1.0", + "pkg-conf": "^2.0.0" + } + }, + "node_modules/standard/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/standard/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "dependencies": { - "deglob": "^2.1.0", - "get-stdin": "^6.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^2.0.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/standard/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "node_modules/standard/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=4" } }, - "node_modules/standard/node_modules/chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", - "dev": true - }, - "node_modules/standard/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/standard/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "color-name": "1.1.3" } }, - "node_modules/standard/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/standard/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/standard/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.8" + } + }, + "node_modules/standard/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" } }, "node_modules/standard/node_modules/eslint": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "ajv": "^6.5.0", @@ -14703,6 +12558,116 @@ "node": "^6.14.0 || ^8.10.0 || >=9.10.0" } }, + "node_modules/standard/node_modules/eslint-config-standard": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", + "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0", + "eslint-plugin-import": ">=2.13.0", + "eslint-plugin-node": ">=7.0.0", + "eslint-plugin-promise": ">=4.0.0", + "eslint-plugin-standard": ">=4.0.0" + } + }, + "node_modules/standard/node_modules/eslint-config-standard-jsx": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz", + "integrity": "sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0", + "eslint-plugin-react": ">=7.11.1" + } + }, + "node_modules/standard/node_modules/eslint-plugin-import": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", + "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", + "dev": true, + "dependencies": { + "contains-path": "^0.1.0", + "debug": "^2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.2.0", + "has": "^1.0.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0", + "resolve": "^1.6.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "2.x - 5.x" + } + }, + "node_modules/standard/node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standard/node_modules/eslint-plugin-react": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", + "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", + "dev": true, + "dependencies": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.0.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/standard/node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/standard/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/eslint/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/standard/node_modules/espree": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", @@ -14717,18 +12682,13 @@ "node": ">=6.0.0" } }, - "node_modules/standard/node_modules/external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "node_modules/standard/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - }, "engines": { - "node": ">=0.12" + "node": ">=4.0" } }, "node_modules/standard/node_modules/file-entry-cache": { @@ -14737,50 +12697,118 @@ "integrity": "sha512-uXP/zGzxxFvFfcZGgBIwotm+Tdc55ddPAzF7iHshP4YGaXMww7rSF9peD9D1sui5ebONg5UobsZv+FfgEpGv/w==", "dev": true, "dependencies": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standard/node_modules/flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "dependencies": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standard/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/standard/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/standard/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/standard/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/standard/node_modules/flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "node_modules/standard/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "dependencies": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/standard/node_modules/inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "node_modules/standard/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "minimist": "^1.2.6" }, - "engines": { - "node": ">=6.0.0" + "bin": { + "mkdirp": "bin/cmd.js" } }, "node_modules/standard/node_modules/ms": { @@ -14789,114 +12817,174 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/standard/node_modules/rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "node_modules/standard/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "dependencies": { - "symbol-observable": "1.0.1" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" }, "engines": { - "npm": ">=2.0.0" + "node": ">= 0.8.0" } }, - "node_modules/standard/node_modules/slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "node_modules/standard/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0" - }, "engines": { "node": ">=4" } }, - "node_modules/standard/node_modules/table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "node_modules/standard/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/standard/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "glob": "^7.1.3" }, - "engines": { - "node": ">=4.0.0" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/standard/node_modules/write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", + "node_modules/standard/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/standard/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "dependencies": { - "mkdirp": "^0.5.1" + "shebang-regex": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "node_modules/standard/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "node_modules/standard/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/standard/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "node_modules/standard/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/stream-combiner2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "node_modules/standard/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/standard/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/stream-combiner2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/stream-combiner2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", + "engines": { + "node": ">= 0.10.0" } }, "node_modules/stream-length": { @@ -14908,12 +12996,6 @@ "bluebird": "^2.6.2" } }, - "node_modules/stream-length/node_modules/bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", - "dev": true - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -14933,62 +13015,111 @@ "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==" }, "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -15008,94 +13139,82 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", "dev": true, "dependencies": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "engines": { - "node": ">= 4.0" + "node": ">=6.4.0 <13 || >=14" } }, "node_modules/superagent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/superagent/node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "node_modules/superagent/node_modules/formidable": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0", + "qs": "^6.11.0" }, - "engines": { - "node": ">= 0.12" + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/superagent/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/superagent/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=4.0.0" } }, "node_modules/superagent/node_modules/ms": { @@ -15104,68 +13223,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/superagent/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/superagent/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/superagent/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/superagent/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "wrappy": "1" } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -15187,25 +13254,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swagger-ui-dist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.1.tgz", - "integrity": "sha512-n7AT4wzKIPpHy/BGflJOepGMrbY/7Cd5yVd9ptVczaJGAKScbVJrZxFbAE2ZSZa8KmqdQ0+pOs3/5mWY5tSMZQ==" - }, - "node_modules/swagger-ui-express": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.2.tgz", - "integrity": "sha512-MHIOaq9JrTTB3ygUJD+08PbjM5Tt/q7x80yz9VTFIatw8j5uIWKcr90S0h5NLMzFEDC6+eVprtoeA5MDZXCUKQ==", - "dependencies": { - "swagger-ui-dist": ">=4.11.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0" - } - }, "node_modules/symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", @@ -15216,170 +13264,297 @@ } }, "node_modules/table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "dependencies": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=4.0.0" } }, "node_modules/table-layout": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", "dependencies": { - "array-back": "^2.0.0", - "deep-extend": "~0.6.0", - "lodash.padend": "^4.6.1", - "typical": "^2.6.1", - "wordwrapjs": "^3.0.0" + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=12.17" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "engines": { + "node": ">=12.17" } }, "node_modules/table/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/table/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/table/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/table/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/table/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } }, - "node_modules/table/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, "engines": { "node": ">=6" } }, - "node_modules/table/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "ansi-regex": "^4.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">= 6" } }, - "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/tar/node_modules/minipass": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", - "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/tar/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dependencies": { + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/teleport-javascript": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/teleport-javascript/-/teleport-javascript-1.0.0.tgz", - "integrity": "sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg==", - "dev": true - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "node_modules/tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "optional": true, "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "debug": "4.3.1", + "is2": "^2.0.6" } }, - "node_modules/tempy/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" + "node_modules/tcp-port-used/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/tcp-port-used/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/teleport-javascript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/teleport-javascript/-/teleport-javascript-1.0.0.tgz", + "integrity": "sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg==", + "dev": true + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -15394,24 +13569,25 @@ "node": ">=8" } }, - "node_modules/test-value": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "dependencies": { - "array-back": "^2.0.0", - "typical": "^2.6.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "engines": { - "node": ">=0.10" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-table": { @@ -15420,48 +13596,31 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "real-require": "^0.2.0" } }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true }, "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", "dev": true, "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" } }, "node_modules/tmp": { @@ -15476,19 +13635,11 @@ "node": ">=0.6.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -15509,46 +13660,10 @@ "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" }, - "node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "engines": { - "node": ">=8" - } - }, "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -15564,21 +13679,22 @@ "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true }, "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", "dev": true }, "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" @@ -15597,6 +13713,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, "engines": { "node": ">=8" } @@ -15613,15 +13730,75 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-length": { + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15642,14 +13819,18 @@ } }, "node_modules/typical": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "engines": { + "node": ">=8" + } }, "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -15658,37 +13839,63 @@ "node": ">=0.8.0" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/umzug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.2.0.tgz", - "integrity": "sha512-xZLW76ax70pND9bx3wqwb8zqkFGzZIK8dIHD9WdNy/CrNfjWcwQgQkGCuUqcuwEBvUm+g07z+qWvY+pxDmMEEw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dev": true, "dependencies": { - "babel-runtime": "^6.23.0", - "bluebird": "^3.5.3" + "bluebird": "^3.7.2" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/umzug/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "dev": true + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" }, "node_modules/uniq": { "version": "1.0.1", @@ -15714,28 +13921,13 @@ "imurmurhash": "^0.1.4" } }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/unpipe": { @@ -15747,9 +13939,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", "dev": true, "funding": [ { @@ -15759,14 +13951,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -15776,21 +13972,19 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" - }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, "dependencies": { - "inherits": "2.0.3" + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" } }, "node_modules/util-deprecate": { @@ -15798,11 +13992,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -15812,45 +14001,49 @@ } }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { - "uuid": "bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/uvm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.0.2.tgz", - "integrity": "sha512-Ra+aPiS5GXAbwXmyNExqdS42sTqmmx4XWEDF8uJlsTfOkKf9Rd9xNgav1Yckv4HfVEZg4iOFODWHFYuJ+9Fzfg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.1.1.tgz", + "integrity": "sha512-BZ5w8adTpNNr+zczOBRpaX/hH8UPKAf7fmCnidrcsqt3bn8KT9bDIfuS7hgRU9RXgiN01su2pwysBONY6w8W5w==", "dev": true, "dependencies": { - "flatted": "3.1.1" + "flatted": "3.2.6" }, "engines": { "node": ">=10" } }, "node_modules/uvm/node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "node_modules/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", "engines": { "node": ">= 0.10" } @@ -15867,6 +14060,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, "engines": [ "node >=0.6.0" ], @@ -15879,67 +14073,113 @@ "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "devOptional": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -15952,6 +14192,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -15965,9 +14206,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -15976,24 +14217,21 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true }, "node_modules/wordwrapjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", - "dependencies": { - "reduce-flatten": "^1.0.1", - "typical": "^2.6.1" - }, + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", "engines": { - "node": ">=4.0.0" + "node": ">=12.17" } }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -16012,74 +14250,22 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -16088,15 +14274,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", "dev": true, "dependencies": { "mkdirp": "^0.5.1" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/write-file-atomic": { @@ -16111,12 +14297,36 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==", + "node_modules/write/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "engines": { - "node": ">=4.0.0" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/xml": { @@ -16146,6 +14356,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/xss-clean/-/xss-clean-0.1.1.tgz", "integrity": "sha512-On99yydxoAEkHpraR7Wjg9cD6UmKfbtH2HXO1it2djid32osHL7Qr8bIu+dGYoOeKzf3ZszLfz1gwR/D7whZ2A==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dependencies": { "xss-filters": "1.2.6" } @@ -16172,40 +14383,31 @@ } }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "engines": { "node": ">=10" } @@ -16249,53 +14451,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "optional": true, "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index c6d25a162..f3fe3d43f 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,16 @@ { - "name": "@iofog/iofogcontroller", - "version": "3.0.4", - "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", + "name": "@datasance/iofogcontroller", + "version": "3.5.11", + "description": "ioFog Controller project for Datasance PoT @ datasance.com \\nCopyright (c) 2023 Datasance Teknoloji A.S.", "main": "./src/main.js", - "author": "Saeid Baghbidi", + "author": "Emirhan Durmus", "contributors": [ - "Kilton Hopkins ", - "Saeid Rezaei Baghbidi", - "Alexandre de Wergifosse", - "Pavel Kazlou", - "Egor Krylovich", - "Iryna Laryionava", - "Maryna Lipnitskaya", - "Dmitriy Kudasov", - "Dmitry Stolbunov", - "Darya Busel", - "Alexander Shpak", - "Kate Lukashick", - "Eugene Pankov", - "Maksim Chepelev", - "Tetiana Yatsiuk", - "Sergey Valevich" + "Emirhan Durmus ", + "Alpaslan Doğan " ], "license": "EPL-2.0", "bugs": { - "email": "edgemaster@iofog.org" + "email": "support@datasance.com" }, "standard": { "ignore": [ @@ -32,10 +18,13 @@ "src/lib/**/*.js" ] }, - "homepage": "https://www.iofog.org", + "homepage": "https://www.datasance.com", "repository": { "type": "git", - "url": "https://github.com/ioFog/Controller" + "url": "https://github.com/Datasance/Controller" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com/" }, "scripts": { "prestart": "npm run lint", @@ -50,7 +39,6 @@ "lint": "npm run standard", "standard": "./node_modules/.bin/standard", "snyk": "./node_modules/.bin/snyk monitor", - "semantic-release": "semantic-release", "pretest": "npm run lint", "test": "node scripts/run-test.js test", "prepostman_test": "npm run lint", @@ -67,68 +55,73 @@ "iofog-controller": "src/main.js" }, "dependencies": { - "@iofog/ecn-viewer": "3.0.2", - "axios": "1.0.0-alpha.1", - "body-parser": "^1.20.1", - "child_process": "1.0.2", - "command-line-args": "5.0.2", - "command-line-usage": "5.0.5", + "@datasance/ecn-viewer": "1.2.6", + "@kubernetes/client-node": "^0.22.3", + "@msgpack/msgpack": "^3.1.2", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.200.0", + "@opentelemetry/instrumentation-express": "^0.48.1", + "@opentelemetry/instrumentation-http": "^0.200.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-node": "^0.200.0", + "axios": "1.12.2", + "bignumber.js": "^9.3.0", + "body-parser": "^1.20.3", + "command-line-args": "5.2.1", + "command-line-usage": "7.0.3", "concurrent-queue": "7.0.2", - "cookie-parser": "1.4.3", + "cookie-parser": "1.4.7", "cors": "2.8.5", "daemonize2": "0.4.2", - "ejs": "3.1.7", - "express": "4.17.3", - "formidable": "1.2.1", + "dotenv": "^16.5.0", + "ejs": "3.1.10", + "express": "4.21.2", + "express-session": "1.18.2", + "formidable": "3.5.4", "ftp": "0.3.10", - "helmet": "3.21.2", + "helmet": "7.1.0", "is-elevated": "3.0.0", - "js-yaml": "3.14.1", - "jsonschema": "1.2.5", - "minimatch": "3.1.2", - "moment": "2.29.4", - "moment-timezone": "0.5.38", - "morgan": "1.9.1", + "jose": "^4.15.9", + "js-yaml": "4.1.1", + "jsonschema": "1.4.1", + "keycloak-connect": "^26.1.1", + "moment": "2.30.1", "multer": "1.4.5-lts.1", - "nconf": "0.12.0", - "nodemailer": "6.7.3", - "nodemailer-smtp-transport": "2.4.2", - "os": "0.1.1", - "path": "0.12.7", - "pino": "6.6.1", - "pino-std-serializers": "2.5.0", + "mysql2": "3.10.1", + "nconf": "0.12.1", + "node-fetch-npm": "^2.0.4", + "node-forge": "^1.3.3", + "pg": "8.12.0", + "pino": "9.13.1", + "pino-std-serializers": "7.0.0", "portscanner": "2.2.0", - "qs": "6.10.3", - "request": "2.88.0", - "request-promise": "4.2.4", - "retry-as-promised": "3.1.0", - "semantic-release": "19.0.3", - "semver": "5.6.0", - "sequelize": "6.29.0", - "sqlite3": "^5.1.5", + "qs": "6.12.1", + "rhea": "^3.0.4", + "sequelize": "6.37.7", + "sqlite3": "^5.1.7", "string-format": "2.0.0", - "swagger-ui-express": "^4.6.2", - "umzug": "2.2.0", - "underscore": "1.13.1", + "uuid": "11.1.0", + "ws": "^8.18.0", "xss-clean": "0.1.1" }, "devDependencies": { - "acorn": "7.1.1", - "bdd-lazy-var": "2.5.2", - "chai": "4.2.0", - "chai-as-promised": "7.1.1", - "chai-http": "4.2.1", - "eslint": "5.14.1", - "eslint-config-google": "0.12.0", - "mocha": "9.2.2", - "mocha-junit-reporter": "2.0.0", - "newman": "5.3.2", + "acorn": "8.11.3", + "bdd-lazy-var": "2.6.1", + "chai": "5.1.1", + "chai-as-promised": "7.1.2", + "chai-http": "4.4.0", + "eslint": "9.28.0", + "eslint-config-google": "0.14.0", + "js-yaml": "^4.1.1", + "mocha": "10.6.0", + "mocha-junit-reporter": "2.2.1", + "newman": "^6.2.1", "newman-reporter-junitfull": "1.1.1", - "nyc": "15.0.0", - "sequelize-cli": "5.5.0", - "sinon": "7.5.0", - "sinon-chai": "3.3.0", - "snyk": "^1.1064.0", + "nyc": "15.1.0", + "sequelize-cli": "6.6.2", + "sinon": "17.0.1", + "sinon-chai": "3.7.0", + "snyk": "^1.1291.0", "standard": "12.0.1" }, "files": [ @@ -139,5 +132,19 @@ ".eslintrc.js", ".jshintrc", ".snyk" - ] + ], + "type": "commonjs", + "overrides": { + "@kubernetes/client-node": { + "request": "@cypress/request@3.0.8" + }, + "sqlite3": { + "prebuild-install": { + "tar-fs": "^2.1.4" + } + }, + "sequelize": { + "validator": "^13.15.22" + } + } } diff --git a/scripts/cli-tests.js b/scripts/cli-tests.js index 6f55ff610..e64f86f39 100644 --- a/scripts/cli-tests.js +++ b/scripts/cli-tests.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -34,12 +34,8 @@ let testsCounter = 0 let testsFailed = 0 const controllerStatusFields = ['status', 'timestamp'] -const controllerEmailActivationFields = ['isEmailActivationEnabled'] const controllerFogTypesFields = ['fogTypes'] -const userCreateFields = ['id'] -const userAccessTokenFields = ['accessToken'] - const ioFogCreateFields = ['uuid'] const ioFogListFields = ['fogs'] const ioFogProvisioningFields = ['key', 'expirationTime'] @@ -69,9 +65,9 @@ async function seedTestData () { fogType: 1, isSystem: true, routerMode: 'interior', - messagingPort: 5672, - edgeRouterPort: 56722, - interRouterPort: 56721, + messagingPort: 5671, + edgeRouterPort: 45671, + interRouterPort: 55671, host: 'localhost' }, { }, false) const defaultRouter = await RouterService.findOne({ isDefault: true }) @@ -84,24 +80,10 @@ function testControllerSection () { console.log('\n=============================\nStarting controller section..') responseHasFields(testCommand('controller status'), controllerStatusFields) - responseHasFields(testCommand('controller email-activation'), controllerEmailActivationFields) responseHasFields(testCommand('controller fog-types'), controllerFogTypesFields) hasSomeResponse(testCommand('controller version')) } -function testUserSection () { - console.log('\n=============================\nStarting user section..') - - responseHasFields(testCommand('user add -f John -l Doe -e user@domain.com -p \'#Bugs4Fun\''), userCreateFields) - responseEquals(testCommand('user update -f John2 -l Doe2 -e user@domain.com -p \'#Bugs4Fun34\''), - 'User updated successfully.') - responseIsArray(testCommand('user list')) - responseHasFields(testCommand('user generate-token -e user@domain.com'), userAccessTokenFields) - responseEquals(testCommand('user suspend -e user@domain.com'), 'User suspended successfully.') - responseEquals(testCommand('user activate -e user@domain.com'), 'User activated successfully.') - responseEquals(testCommand('user remove -e user@domain.com'), 'User removed successfully.') -} - function testConfigSection () { console.log('\n=============================\nStarting config section..') @@ -110,7 +92,6 @@ function testConfigSection () { // " -h testHomeUrl -a testEmailAddress -w testEmailPassword -s testEmailService -d testLogDir -z 555")); hasSomeResponse(testCommand('config list')) responseEquals(testCommand('config dev-mode -o'), 'Dev mode state updated successfully.') - responseEquals(testCommand('config email-activation -f'), 'Email activation state updated successfully.') } function testTunnelSection () { @@ -124,18 +105,14 @@ function testTunnelSection () { function testIoFogSection () { console.log('\n=============================\nStarting iofog section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e fogUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - const userId = userCreateResponse.id - try { const ioFogCreateResponse = responseHasFields(testCommand('iofog add -n ioFog1 -l testLocation -t 55 -g 65' + ' -d testDescription -D testDockerUrl -M 55 -T testDiskDirectoryString -m 65 -c 24 -G 1 -Y testLogDirectory ' + - ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u ' + userId), ioFogCreateFields) + ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u '), ioFogCreateFields) const ioFogUuid = ioFogCreateResponse.uuid responseEquals(testCommand('iofog update -i ' + ioFogUuid + ' -n ioFog1 -l testLocation -t 55 -g 65 ' + '-d testDescription -D testDockerUrl -M 55 -T testDiskDirectoryString -m 65 -c 24 -G 1 -Y testLogDirectory ' + - ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -L INFO -p 65 -k 95 -u ' + userId), 'ioFog node has been updated successfully.') + ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -L INFO -p 65 -k 95 -u '), 'ioFog node has been updated successfully.') responseHasFields(testCommand('iofog list'), ioFogListFields) responseHasFields(testCommand('iofog info -i ' + ioFogUuid), ioFogCreateFields) responseHasFields(testCommand('iofog provisioning-key -i ' + ioFogUuid), ioFogProvisioningFields) @@ -145,7 +122,7 @@ function testIoFogSection () { 'ioFog version command has been set successfully') hasSomeResponse(testCommand('iofog hal-hw -i ' + ioFogUuid)) hasSomeResponse(testCommand('iofog hal-usb -i ' + ioFogUuid)) - responseEquals(testCommand('iofog remove -i ' + ioFogUuid + ' -u ' + userId), 'ioFog node has been removed successfully') + responseEquals(testCommand('iofog remove -i ' + ioFogUuid + ' -u '), 'ioFog node has been removed successfully') executeCommand('user remove -e fogUser@domain.com') } catch (exception) { executeCommand('user remove -e fogUser@domain.com') @@ -155,18 +132,15 @@ function testIoFogSection () { function testCatalogSection () { console.log('\n=============================\nStarting catalog section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e catalogUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - const userId = userCreateResponse.id const registryCreateResponse = responseHasFields(executeCommand('registry add -U testRegistryUri -b -l testUserName' + - ' -p testPassword -e testEmail@gmail.com -u ' + userId), registryCreateFields) + ' -p testPassword -e testEmail@gmail.com -u '), registryCreateFields) const registryId = registryCreateResponse.id try { const catalogCreateResponse = responseHasFields(testCommand('catalog add -n testCatalogItem1 -d testDescription' + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + - '-X \'{}\' -u ' + userId), catalogCreateFields) + '-X \'{}\' -u '), catalogCreateFields) const catalogId = catalogCreateResponse.id responseEquals(testCommand('catalog update -i ' + catalogId + ' -n testCatalogItem2 -d testDescription' + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + @@ -186,19 +160,15 @@ function testCatalogSection () { function testApplicationSection () { console.log('\n=============================\nStarting application section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e applicationUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - const userId = userCreateResponse.id - try { const applicationCreateResponse = responseHasFields(testCommand('application add -n test-application-1 -d testDescription' + - ' -a -u ' + userId), applicationCreateFields) + ' -a -u '), applicationCreateFields) const name = applicationCreateResponse.name - responseEquals(testCommand('application update -n ' + name + ' -d testDescription -a' + ' -u ' + userId), + responseEquals(testCommand('application update -n ' + name + ' -d testDescription -a' + ' -u '), 'Application updated successfully.') responseHasFields(testCommand('application list'), applicationListFields) responseHasFields(testCommand('application info -n ' + name), applicationCreateFields) - responseEquals(testCommand('application remove -n ' + name + ' -u ' + userId), 'Application removed successfully.') + responseEquals(testCommand('application remove -n ' + name + ' -u '), 'Application removed successfully.') executeCommand('user remove -e applicationUser@domain.com') } catch (exception) { executeCommand('user remove -e applicationUser@domain.com') @@ -208,33 +178,29 @@ function testApplicationSection () { function testMicroserviceSection () { console.log('\n=============================\nStarting microservice section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e microserviceUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - const userId = userCreateResponse.id - const registryCreateResponse = responseHasFields(executeCommand('registry add -U testRegistryUri -b -l testUserName' + - ' -p testPassword -e testEmail@gmail.com -u ' + userId), registryCreateFields) + ' -p testPassword -e testEmail@gmail.com -u '), registryCreateFields) const registryId = registryCreateResponse.id const catalogCreateResponse = responseHasFields(executeCommand('catalog add -n testCatalogItem1 -d testDescription' + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + - '-X \'{}\' -u ' + userId), catalogCreateFields) + '-X \'{}\' -u '), catalogCreateFields) const catalogId = catalogCreateResponse.id const applicationCreateResponse = responseHasFields(executeCommand('application add -n test-application1 -d testDescription' + - ' -a -u ' + userId), applicationCreateFields) + ' -a -u '), applicationCreateFields) const applicationId = applicationCreateResponse.name const ioFogCreateResponse = responseHasFields(executeCommand('iofog add -n ioFog2 -l testLocation -t 55 -g 65 ' + '-d testDescription -D testDockerUrl -M 55 -T testDiskDirectoryString -m 65 -c 24 -G 1 -Y testLogDirectory ' + - ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u ' + userId), ioFogCreateFields) + ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u '), ioFogCreateFields) const ioFogUuid = ioFogCreateResponse.uuid try { const microserviceCreateResponse = responseHasFields(testCommand('microservice add -n microservice-name-1' + ' -c ' + catalogId + ' -F ' + applicationId + ' -I ' + ioFogUuid + ' -g \'{}\' -v /host_src:/container_src:rw -l 15 -R' + - ' -p 80:8080:false -u ' + userId), microserviceCreateFields) + ' -p 80:8080:false -u '), microserviceCreateFields) const microserviceUuid = microserviceCreateResponse.uuid responseEquals(testCommand('microservice update -i ' + microserviceUuid + ' -n microservice-name-2' + ' -I ' + ioFogUuid + ' -g \'{}\' -v /host_src:/container_src:rw -l 15 -R -w'), @@ -274,13 +240,9 @@ function testMicroserviceSection () { function testRegistrySection () { console.log('\n=============================\nStarting registry section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e registryUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - try { - const userId = userCreateResponse.id const registryCreateResponse = responseHasFields(testCommand('registry add -U testRegistryUri -b -l testUserName' + - ' -p testPassword -e testEmail@gmail.com -u ' + userId), registryCreateFields) + ' -p testPassword -e testEmail@gmail.com -u '), registryCreateFields) const registryId = registryCreateResponse.id responseEquals(testCommand('registry update -i ' + registryId + ' -U testRegistryUri -b -l testUserName' + ' -p testPassword -e testEmail@gmail.com'), 'Registry has been updated successfully.') @@ -295,32 +257,28 @@ function testRegistrySection () { function testDiagnosticsSection () { console.log('\n=============================\nStarting diagnostics section..') - const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e diagnosticsUser@domain.com' + - ' -p \'#Bugs4Fun\''), userCreateFields) - const userId = userCreateResponse.id - const registryCreateResponse = responseHasFields(executeCommand('registry add -U testRegistryUri -b -l testUserName' + - ' -p testPassword -e testEmail@gmail.com -u ' + userId), registryCreateFields) + ' -p testPassword -e testEmail@gmail.com -u '), registryCreateFields) const registryId = registryCreateResponse.id const catalogCreateResponse = responseHasFields(executeCommand('catalog add -n testCatalogItem1 -d testDescription' + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + - '-X \'{}\' -u ' + userId), catalogCreateFields) + '-X \'{}\' -u '), catalogCreateFields) const catalogId = catalogCreateResponse.id const applicationCreateResponse = responseHasFields(executeCommand('application add -n test-application1 -d testDescription' + - ' -a -u ' + userId), applicationCreateFields) + ' -a -u '), applicationCreateFields) const applicationId = applicationCreateResponse.name const ioFogCreateResponse = responseHasFields(executeCommand('iofog add -n ioFog3 -l testLocation -t 55 -g 65' + ' -d testDescription -D testDockerUrl -M 55 -T testDiskDirectoryString -m 65 -c 24 -G 1 -Y testLogDirectory ' + - ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u ' + userId), ioFogCreateFields) + ' -s 25 -F 27 -Q 26 -B -W -A -y 1 -u '), ioFogCreateFields) const ioFogUuid = ioFogCreateResponse.uuid const microserviceCreateResponse = responseHasFields(executeCommand('microservice add -n microservice-name-1' + ' -c ' + catalogId + ' -F ' + applicationId + ' -I ' + ioFogUuid + ' -g \'{}\' -v /host_src:/container_src:rw -l 15 -R' + - ' -p 80:8080:false -u ' + userId), microserviceCreateFields) + ' -p 80:8080:false -u '), microserviceCreateFields) const microserviceUuid = microserviceCreateResponse.uuid try { @@ -421,7 +379,6 @@ async function cliTest () { await seedTestData() testControllerSection() - testUserSection() testConfigSection() testTunnelSection() testIoFogSection() diff --git a/scripts/coverage.js b/scripts/coverage.js index ad9793ec1..ee8c0d201 100644 --- a/scripts/coverage.js +++ b/scripts/coverage.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/init.js b/scripts/init.js index 91ef17f02..09cbee6b6 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/postinstall.js b/scripts/postinstall.js index cd734dc71..851c00621 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,7 +14,7 @@ // const sqlite3 = require('sqlite3') // .verbose() //use verbose in dev to get stack traces const execSync = require('child_process').execSync const fs = require('fs') -const semver = require('semver') +const compareVersions = require('compare-versions') const config = require('../src/config') const currentVersion = require('../package').version @@ -34,16 +34,16 @@ function postinstall () { console.log(`previous version - ${prevVersion}`) console.log(`new version - ${currentVersion}`) - if (semver.satisfies(prevVersion, '<=1.0.0')) { + if (compareVersions(prevVersion, '<=1.0.0')) { console.log('upgrading from version <= 1.0.0 :') insertSeeds() } - if (semver.satisfies(prevVersion, '<=1.0.30')) { + if (compareVersions(prevVersion, '<=1.0.30')) { console.log('upgrading from version <= 1.0.30 :') updateEncryptionMethod() } - if (semver.satisfies(prevVersion, '<=1.0.37')) { + if (compareVersions(prevVersion, '<=1.0.37')) { console.log('upgrading from version <= 1.0.37 :') updateLogName() } @@ -56,8 +56,8 @@ function postinstall () { // init db const options = { env: { - 'NODE_ENV': 'production', - 'PATH': process.env.PATH + NODE_ENV: 'production', + PATH: process.env.PATH }, stdio: [process.stdin, process.stdout, process.stderr] } @@ -168,7 +168,7 @@ function updateEncryptionMethod () { function updateLogName () { console.log(' updating log name in ') - const dirname = config.get('Service:LogsDirectory') + const dirname = config.get('log.directory') if (fs.existsSync(dirname)) { fs.readdirSync(dirname).forEach((file) => { @@ -184,5 +184,5 @@ function updateLogName () { } module.exports = { - postinstall: postinstall + postinstall } diff --git a/scripts/postmantest.js b/scripts/postmantest.js index 3d2aa07ed..555254a38 100644 --- a/scripts/postmantest.js +++ b/scripts/postmantest.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/preuninstall.js b/scripts/preuninstall.js index ad9e80d99..b22fd9e27 100644 --- a/scripts/preuninstall.js +++ b/scripts/preuninstall.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/run-test.js b/scripts/run-test.js index 78f0dcc2a..14f1e5e30 100644 --- a/scripts/run-test.js +++ b/scripts/run-test.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/scripts-api.js b/scripts/scripts-api.js index 25bcf32e7..1af3818ca 100644 --- a/scripts/scripts-api.js +++ b/scripts/scripts-api.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/start-dev.js b/scripts/start-dev.js index fa8a309a0..996af7f9a 100644 --- a/scripts/start-dev.js +++ b/scripts/start-dev.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,21 +12,43 @@ */ const execSync = require('child_process').execSync - +const path = require('path') +const fs = require('fs') const { setDbEnvVars } = require('./util') function startDev () { + // Load .env file if it exists + const envPath = path.resolve(process.cwd(), '.env') + let envVars = {} + + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8') + envContent.split('\n').forEach(line => { + line = line.trim() + if (line && !line.startsWith('#')) { + const [key, value] = line.split('=').map(str => str.trim()) + if (key && value) { + envVars[key] = value + } + } + }) + } else { + } + + // Create a new environment object with all variables + const newEnv = { + ...process.env, // Include existing environment variables + ...envVars, // Override with .env variables + 'NODE_ENV': 'development', + 'PATH': process.env.PATH + } + + // Apply database environment variables const options = { - env: { - 'NODE_ENV': 'development', - 'VIEWER_PORT': '8008', - 'PATH': process.env.PATH - }, + env: setDbEnvVars(newEnv), stdio: [process.stdin, process.stdout, process.stderr] } - options.env = setDbEnvVars(options.env) - execSync('node ./src/main.js start', options) } diff --git a/scripts/start.js b/scripts/start.js index c56cabee9..bf021f270 100644 --- a/scripts/start.js +++ b/scripts/start.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/stop.js b/scripts/stop.js index f81ee01a3..bcbc4e8de 100644 --- a/scripts/stop.js +++ b/scripts/stop.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/test.js b/scripts/test.js index 1939537bf..b53c9beff 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/scripts/util.js b/scripts/util.js index eee10e00c..0245dd7d3 100644 --- a/scripts/util.js +++ b/scripts/util.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2018 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -22,7 +22,7 @@ const DEV_DB_BACKUP = `${TEMP_DIR}/dev_database.sqlite` const PROD_DB = `${ROOT_DIR}/src/data/sqlite_files/prod_database.sqlite` const PROD_DB_BACKUP = `${TEMP_DIR}/prod_database.sqlite` -let dbName = process.env.DB_NAME || 'iofogcontroller' +let dbName = process.env.DB_NAME || 'pot-controller' if (!dbName.endsWith('.sqlite')) { dbName += '.sqlite' } diff --git a/src/cli/application.js b/src/cli/application.js index 833467ef4..e5997dd81 100644 --- a/src/cli/application.js +++ b/src/cli/application.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,12 +13,10 @@ const BaseCLIHandler = require('./base-cli-handler') const constants = require('../helpers/constants') -const AuthDecorator = require('../decorators/cli-decorator') const ApplicationService = require('../services/application-service') const AppHelper = require('../helpers/app-helper') const logger = require('../logger') const fs = require('fs') -const CliDataTypes = require('./cli-data-types') const JSON_SCHEMA = AppHelper.stringifyCliJsonSchema({ name: 'string', @@ -71,13 +69,6 @@ class Application extends BaseCLIHandler { type: Boolean, description: 'Deactivate application', group: [constants.CMD_UPDATE, constants.CMD_ADD] - }, - { - name: 'user-id', - alias: 'u', - type: CliDataTypes.Integer, - description: 'User\'s id', - group: [constants.CMD_ADD, constants.CMD_UPDATE, constants.CMD_REMOVE] } ] this.commands = { @@ -99,19 +90,19 @@ class Application extends BaseCLIHandler { switch (command) { case constants.CMD_ADD: - await _executeCase(applicationCommand, constants.CMD_ADD, _createApplication, true) + await _executeCase(applicationCommand, constants.CMD_ADD, _createApplication) break case constants.CMD_UPDATE: - await _executeCase(applicationCommand, constants.CMD_UPDATE, _updateApplication, true) + await _executeCase(applicationCommand, constants.CMD_UPDATE, _updateApplication) break case constants.CMD_REMOVE: - await _executeCase(applicationCommand, constants.CMD_REMOVE, _deleteApplication, true) + await _executeCase(applicationCommand, constants.CMD_REMOVE, _deleteApplication) break case constants.CMD_LIST: - await _executeCase(applicationCommand, constants.CMD_LIST, _getAllApplications, false) + await _executeCase(applicationCommand, constants.CMD_LIST, _getAllApplications) break case constants.CMD_INFO: - await _executeCase(applicationCommand, constants.CMD_INFO, _getApplication, false) + await _executeCase(applicationCommand, constants.CMD_INFO, _getApplication) break case constants.CMD_HELP: default: @@ -135,48 +126,42 @@ class Application extends BaseCLIHandler { } } -const _executeCase = async function (applicationCommand, commandName, f, isUserRequired) { +const _executeCase = async function (applicationCommand, commandName, f) { try { const item = applicationCommand[commandName] - - if (isUserRequired) { - const decoratedFunction = AuthDecorator.prepareUserById(f) - await decoratedFunction(item) - } else { - await f(item) - } + await f(item) } catch (error) { logger.error(error.message) } } -const _createApplication = async function (applicationData, user) { +const _createApplication = async function (applicationData) { const application = applicationData.file ? JSON.parse(fs.readFileSync(applicationData.file, 'utf8')) : _createApplicationObject(applicationData) logger.cliReq('application add', { args: application }) - const createdApplication = await ApplicationService.createApplicationEndPoint(application, user, true) + const createdApplication = await ApplicationService.createApplicationEndPoint(application, true) logger.cliRes(JSON.stringify({ id: createdApplication.id, name: createdApplication.name }, null, 2)) } -const _updateApplication = async function (applicationData, user) { +const _updateApplication = async function (applicationData) { const application = applicationData.file ? JSON.parse(fs.readFileSync(applicationData.file, 'utf8')) : _createApplicationObject(applicationData) const name = applicationData.name logger.cliReq('application update', { args: application }) - await ApplicationService.patchApplicationEndPoint(application, { name }, user, true) + await ApplicationService.patchApplicationEndPoint(application, { name }, true) logger.cliRes('Application updated successfully.') } -const _deleteApplication = async function (applicationData, user) { +const _deleteApplication = async function (applicationData) { const name = applicationData.name logger.cliReq('application remove', { args: { name } }) - await ApplicationService.deleteApplicationEndPoint({ name }, user, true) + await ApplicationService.deleteApplicationEndPoint({ name }, true) logger.cliRes('Application removed successfully.') } diff --git a/src/cli/base-cli-handler.js b/src/cli/base-cli-handler.js index 94254c10c..48b0bdb36 100644 --- a/src/cli/base-cli-handler.js +++ b/src/cli/base-cli-handler.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -62,7 +62,7 @@ class CLIHandler { const usage = [ { header: 'ioFogController', - content: 'Fog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2020 Edgeworx, Inc.' + content: 'Fog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2023 Datasance Teknoloji A.S.' } ].concat(sections) logger.cliRes(commandLineUsage(usage)) @@ -95,7 +95,7 @@ class CLIHandler { const usage = [ { header: 'ioFogController', - content: 'Fog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2020 Edgeworx, Inc.' + content: 'Fog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2023 Datasance Teknoloji A.S.' } ].concat(sections) logger.cliRes(commandLineUsage(usage)) diff --git a/src/cli/catalog.js b/src/cli/catalog.js index cf9662517..9f15b4519 100644 --- a/src/cli/catalog.js +++ b/src/cli/catalog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -17,7 +17,6 @@ const logger = require('../logger') const CatalogItemService = require('../services/catalog-service') const fs = require('fs') const AppHelper = require('../helpers/app-helper') -const AuthDecorator = require('../decorators/cli-decorator') const Errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') const CliDataTypes = require('./cli-data-types') @@ -192,13 +191,6 @@ class Catalog extends BaseCLIHandler { type: String, description: 'Catalog item config example', group: [constants.CMD_UPDATE, constants.CMD_ADD] - }, - { - name: 'user-id', - alias: 'u', - type: CliDataTypes.Integer, - description: 'User\'s id', - group: [constants.CMD_ADD] } ] this.commands = { @@ -220,19 +212,19 @@ class Catalog extends BaseCLIHandler { switch (command) { case constants.CMD_ADD: - await _executeCase(catalogCommand, constants.CMD_ADD, _createCatalogItem, true) + await _executeCase(catalogCommand, constants.CMD_ADD, _createCatalogItem) break case constants.CMD_UPDATE: - await _executeCase(catalogCommand, constants.CMD_UPDATE, _updateCatalogItem, false) + await _executeCase(catalogCommand, constants.CMD_UPDATE, _updateCatalogItem) break case constants.CMD_REMOVE: - await _executeCase(catalogCommand, constants.CMD_REMOVE, _deleteCatalogItem, false) + await _executeCase(catalogCommand, constants.CMD_REMOVE, _deleteCatalogItem) break case constants.CMD_LIST: - await _executeCase(catalogCommand, constants.CMD_LIST, _listCatalogItems, false) + await _executeCase(catalogCommand, constants.CMD_LIST, _listCatalogItems) break case constants.CMD_INFO: - await _executeCase(catalogCommand, constants.CMD_INFO, _getCatalogItem, false) + await _executeCase(catalogCommand, constants.CMD_INFO, _getCatalogItem) break case constants.CMD_HELP: default: @@ -256,28 +248,22 @@ class Catalog extends BaseCLIHandler { } } -const _executeCase = async function (catalogCommand, commandName, f, isUserRequired) { +const _executeCase = async function (catalogCommand, commandName, f) { try { const item = catalogCommand[commandName] - - if (isUserRequired) { - const decoratedFunction = AuthDecorator.prepareUserById(f) - await decoratedFunction(item) - } else { - await f(item) - } + await f(item) } catch (error) { logger.error(error.message) } } -const _createCatalogItem = async function (obj, user) { +const _createCatalogItem = async function (obj) { const item = obj.file ? JSON.parse(fs.readFileSync(obj.file, 'utf8')) : _createCatalogItemObject(obj) logger.cliReq('catalog add', { args: item }) - const catalogItemIdObject = await CatalogItemService.createCatalogItemEndPoint(item, user) + const catalogItemIdObject = await CatalogItemService.createCatalogItemEndPoint(item) logger.cliRes(JSON.stringify({ id: catalogItemIdObject.id }, null, 2)) diff --git a/src/cli/cli-data-types.js b/src/cli/cli-data-types.js index 8c8d6f8c0..909e3c115 100644 --- a/src/cli/cli-data-types.js +++ b/src/cli/cli-data-types.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/cli/config.js b/src/cli/config.js index 6d111077b..6492c4b09 100644 --- a/src/cli/config.js +++ b/src/cli/config.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -59,34 +59,6 @@ class Config extends BaseCLIHandler { description: 'Path to SSL intermediate certificate file', group: constants.CMD_ADD }, - { - name: 'home-url', - alias: 'h', - type: String, - description: 'Home page url for email activation links', - group: constants.CMD_ADD - }, - { - name: 'email-address', - alias: 'a', - type: String, - description: 'Email address to send activations from', - group: constants.CMD_ADD - }, - { - name: 'email-password', - alias: 'w', - type: String, - description: 'Email password to send activations from', - group: constants.CMD_ADD - }, - { - name: 'email-service', - alias: 's', - type: String, - description: 'Email service to send activations', - group: constants.CMD_ADD - }, { name: 'log-dir', alias: 'd', @@ -113,28 +85,20 @@ class Config extends BaseCLIHandler { alias: 'o', type: Boolean, description: 'Enable', - group: [constants.CMD_DEV_MODE, constants.CMD_EMAIL_ACTIVATION] + group: [constants.CMD_DEV_MODE] }, { name: 'off', alias: 'f', type: Boolean, description: 'Disable', - group: [constants.CMD_DEV_MODE, constants.CMD_EMAIL_ACTIVATION] - }, - { - name: 'kubelet', - alias: 't', - type: String, - description: 'iofog-kubelet url', - group: constants.CMD_ADD + group: [constants.CMD_DEV_MODE] } ] this.commands = { [constants.CMD_ADD]: 'Add a new config value.', [constants.CMD_LIST]: 'Display current config.', - [constants.CMD_DEV_MODE]: 'Dev mode config.', - [constants.CMD_EMAIL_ACTIVATION]: 'Email activation config.' + [constants.CMD_DEV_MODE]: 'Dev mode config.' } } @@ -156,9 +120,6 @@ class Config extends BaseCLIHandler { case constants.CMD_DEV_MODE: await _executeCase(configCommand, constants.CMD_DEV_MODE, _changeDevModeState) break - case constants.CMD_EMAIL_ACTIVATION: - await _executeCase(configCommand, constants.CMD_EMAIL_ACTIVATION, _changeEmailActivationState) - break case constants.CMD_HELP: default: return this.help([], true, false) @@ -181,84 +142,59 @@ const _executeCase = async function (catalogCommand, commandName, f) { const _addConfigOption = async function (options) { await Validator.validate(options, Validator.schemas.configUpdate) - await updateConfig(options.port, 'port', 'Server:Port', async (onSuccess) => { + await updateConfig(options.port, 'port', 'server.port', async (onSuccess) => { const port = options.port const status = await AppHelper.checkPortAvailability(port) if (status === 'closed') { - config.set('Server:Port', port) + config.set('server.port', port) onSuccess() } else { logger.error(AppHelper.formatMessage(ErrorMessages.PORT_NOT_AVAILABLE, port)) } }) - await updateConfig(options.sslCert, 'ssl-cert', 'Server:SslCert', (onSuccess) => { + await updateConfig(options.sslCert, 'ssl-cert', 'server.ssl.path.cert', (onSuccess) => { const sslCert = options.sslCert if (!AppHelper.isFileExists(sslCert)) { logger.error(ErrorMessages.INVALID_FILE_PATH) return } - config.set('Server:SslCert', sslCert) + config.set('server.ssl.path.cert', sslCert) onSuccess() }) - await updateConfig(options.sslKey, 'ssl-key', 'Server:SslKey', (onSuccess) => { + await updateConfig(options.sslKey, 'ssl-key', 'server.ssl.path.key', (onSuccess) => { const sslKey = options.sslKey if (!AppHelper.isFileExists(sslKey)) { logger.error(ErrorMessages.INVALID_FILE_PATH) return } - config.set('Server:SslKey', sslKey) + config.set('server.ssl.path.key', sslKey) onSuccess() }) - await updateConfig(options.intermediateCert, 'intermediate-cert', 'Server:IntermediateCert', (onSuccess) => { + await updateConfig(options.intermediateCert, 'intermediate-cert', 'server.ssl.path.intermediateCert', (onSuccess) => { const intermediateCert = options.intermediateCert if (!AppHelper.isFileExists(intermediateCert)) { logger.error(ErrorMessages.INVALID_FILE_PATH) return } - config.set('Server:IntermediateCert', intermediateCert) - onSuccess() - }) - - await updateConfig(options.homeUrl, 'home-url', 'Email:HomeUrl', (onSuccess) => { - config.set('Email:HomeUrl', options.homeUrl) - onSuccess() - }) - - await updateConfig(options.emailAddress, 'email-address', 'Email:Address', (onSuccess) => { - config.set('Email:Address', options.emailAddress) - onSuccess() - }) - - if (options.emailPassword) { - config.set('Email:Password', AppHelper.encryptText(options.emailPassword, config.get('Email:Address'))) - logger.cliRes('Config option email-password has been updated.') - } - - await updateConfig(options.emailService, 'email-service', 'Email:Service', (onSuccess) => { - config.set('Email:Service', options.emailService) + config.set('server.ssl.path.intermediateCert', intermediateCert) onSuccess() }) - await updateConfig(options.logDir, 'log-dir', 'Service:LogsDirectory', (onSuccess) => { - config.set('Service:LogsDirectory', options.logDir) + await updateConfig(options.logDir, 'log-dir', 'log.directory', (onSuccess) => { + config.set('log.directory', options.logDir) onSuccess() }) - await updateConfig(options.logSize, 'log-size', 'Service:LogsFileSize', (onSuccess) => { - config.set('Service:LogsFileSize', options.logSize * 1024) + await updateConfig(options.logSize, 'log-size', 'log.fileSize', (onSuccess) => { + config.set('log.fileSize', options.logSize * 1024) onSuccess() }) - await updateConfig(options.logSize, 'log-file-counr', 'Service:LogsFileCount', (onSuccess) => { - config.set('Service:LogsFileCount', options.logFileCount) - onSuccess() - }) - - await updateConfig(options.kubelet, 'kubelet', 'Kubelet:Uri', (onSuccess) => { - config.set('Kubelet:Uri', options.kubelet) + await updateConfig(options.logFileCount, 'log-file-count', 'log.fileCount', (onSuccess) => { + config.set('log.fileCount', options.logFileCount) onSuccess() }) } @@ -279,20 +215,14 @@ const updateConfig = async function (newConfigValue, cliConfigName, configName, const _listConfigOptions = function () { const configuration = { - 'Port': config.get('Server:Port'), - 'SSL key directory': config.get('Server:SslKey'), - 'SSL certificate directory': config.get('Server:SslCert'), - 'Intermediate key directory': config.get('Server:IntermediateCert'), - 'Home url': config.get('Email:HomeUrl'), - 'Email activation': config.get('Email:ActivationEnabled'), - 'Email address': config.get('Email:Address'), - 'Email password': config.get('Email:Password'), - 'Email service': config.get('Email:Service'), - 'Log files directory': config.get('Service:LogsDirectory'), - 'Log files size': config.get('Service:LogsFileSize'), - 'Log files count': config.get('Service:LogsFileCount'), - 'Dev mode': config.get('Server:DevMode'), - 'Kubelet Url': config.get('Kubelet:Uri') + 'Port': config.get('server.port'), + 'SSL key directory': config.get('server.ssl.path.key'), + 'SSL certificate directory': config.get('server.ssl.path.cert'), + 'Intermediate key directory': config.get('server.ssl.path.intermediateCert'), + 'Log files directory': config.get('log.directory'), + 'Log files size': config.get('log.fileSize'), + 'Log files count': config.get('log.fileCount'), + 'Dev mode': config.get('server.devMode') } const result = Object.keys(configuration) @@ -304,14 +234,8 @@ const _listConfigOptions = function () { const _changeDevModeState = async function (options) { const enableDevMode = AppHelper.validateBooleanCliOptions(options.on, options.off) - config.set('Server:DevMode', enableDevMode) + config.set('server.devMode', enableDevMode) logger.cliRes('Dev mode state updated successfully.') } -const _changeEmailActivationState = function (options) { - const enableEmailActivation = AppHelper.validateBooleanCliOptions(options.on, options.off) - config.set('Email:ActivationEnabled', enableEmailActivation) - logger.cliRes('Email activation state updated successfully.') -} - module.exports = new Config() diff --git a/src/cli/controller.js b/src/cli/controller.js index 4e6b6a85a..9ae8561ca 100644 --- a/src/cli/controller.js +++ b/src/cli/controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,7 +15,6 @@ const BaseCLIHandler = require('./base-cli-handler') const constants = require('../helpers/constants') const ControllerService = require('../services/controller-service') const logger = require('../logger') -const AuthDecorator = require('../decorators/cli-decorator') class Controller extends BaseCLIHandler { constructor () { @@ -26,13 +25,12 @@ class Controller extends BaseCLIHandler { { name: 'command', defaultOption: true, - description: 'status, email-activation, fog-types, version', + description: 'status, fog-types, version', group: constants.CMD } ] this.commands = { [constants.CMD_STATUS]: 'Display iofog-controller service status.', - [constants.CMD_EMAIL_ACTIVATION]: 'Is email activation.', [constants.CMD_FOG_TYPES]: 'List all Fog-types.', [constants.CMD_VERSION]: 'Display iofog-controller service version.' } @@ -48,16 +46,13 @@ class Controller extends BaseCLIHandler { switch (command) { case constants.CMD_STATUS: - await _executeCase(controllerCommand, constants.CMD_STATUS, _getStatus, false) - break - case constants.CMD_EMAIL_ACTIVATION: - await _executeCase(controllerCommand, constants.CMD_EMAIL_ACTIVATION, _emailActivation, false) + await _executeCase(controllerCommand, constants.CMD_STATUS, _getStatus) break case constants.CMD_FOG_TYPES: - await _executeCase(controllerCommand, constants.CMD_FOG_TYPES, _getFogTypes, false) + await _executeCase(controllerCommand, constants.CMD_FOG_TYPES, _getFogTypes) break case constants.CMD_VERSION: - await _executeCase(controllerCommand, constants.CMD_VERSION, _getVersion, false) + await _executeCase(controllerCommand, constants.CMD_VERSION, _getVersion) break case constants.CMD_HELP: default: @@ -69,16 +64,10 @@ class Controller extends BaseCLIHandler { } } -const _executeCase = async function (userCommand, commandName, f, isUserRequired) { +const _executeCase = async function (userCommand, commandName, f) { try { const item = userCommand[commandName] - - if (isUserRequired) { - const decoratedFunction = AuthDecorator.prepareUserByEmail(f) - await decoratedFunction(item) - } else { - await f(item) - } + await f(item) } catch (error) { logger.error(error.message) } @@ -89,12 +78,6 @@ const _getStatus = async function () { logger.cliRes(JSON.stringify(response, null, 2)) } -const _emailActivation = async function () { - logger.cliReq('controller email-activation') - const response = await ControllerService.emailActivation(true) - logger.cliRes(JSON.stringify(response, null, 2)) -} - const _getFogTypes = async function () { logger.cliReq('controller fog-types') const response = await ControllerService.getFogTypes(true) diff --git a/src/cli/diagnostics.js b/src/cli/diagnostics.js index 24fa9cd8c..165c55ed6 100644 --- a/src/cli/diagnostics.js +++ b/src/cli/diagnostics.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -16,7 +16,6 @@ const constants = require('../helpers/constants') const logger = require('../logger') const DiagnosticService = require('../services/diagnostic-service') const AppHelper = require('../helpers/app-helper') -const AuthDecorator = require('../decorators/cli-decorator') const CliDataTypes = require('./cli-data-types') class Diagnostics extends BaseCLIHandler { @@ -114,19 +113,19 @@ class Diagnostics extends BaseCLIHandler { switch (command) { case constants.CMD_STRACE_UPDATE: - await _executeCase(diagnosticCommand, constants.CMD_STRACE_UPDATE, _changeMicroserviceStraceState, false) + await _executeCase(diagnosticCommand, constants.CMD_STRACE_UPDATE, _changeMicroserviceStraceState) break case constants.CMD_STRACE_INFO: - await _executeCase(diagnosticCommand, constants.CMD_STRACE_INFO, _getMicroserviceStraceData, false) + await _executeCase(diagnosticCommand, constants.CMD_STRACE_INFO, _getMicroserviceStraceData) break case constants.CMD_STRACE_FTP_POST: - await _executeCase(diagnosticCommand, constants.CMD_STRACE_FTP_POST, _postMicroserviceStraceDataToFtp, false) + await _executeCase(diagnosticCommand, constants.CMD_STRACE_FTP_POST, _postMicroserviceStraceDataToFtp) break case constants.CMD_IMAGE_SNAPSHOT_CREATE: - await _executeCase(diagnosticCommand, constants.CMD_IMAGE_SNAPSHOT_CREATE, _postMicroserviceImageSnapshotCreate, false) + await _executeCase(diagnosticCommand, constants.CMD_IMAGE_SNAPSHOT_CREATE, _postMicroserviceImageSnapshotCreate) break case constants.CMD_IMAGE_SNAPSHOT_GET: - await _executeCase(diagnosticCommand, constants.CMD_IMAGE_SNAPSHOT_GET, _getMicroserviceImageSnapshot, false) + await _executeCase(diagnosticCommand, constants.CMD_IMAGE_SNAPSHOT_GET, _getMicroserviceImageSnapshot) break case constants.CMD_HELP: default: @@ -138,16 +137,10 @@ class Diagnostics extends BaseCLIHandler { } } -const _executeCase = async function (diagnosticCommand, commandName, f, isUserRequired) { +const _executeCase = async function (diagnosticCommand, commandName, f) { try { const item = diagnosticCommand[commandName] - - if (isUserRequired) { - const decoratedFunction = AuthDecorator.prepareUserById(f) - await decoratedFunction(item) - } else { - await f(item) - } + await f(item) } catch (error) { logger.error(error.message) } diff --git a/src/cli/index.js b/src/cli/index.js index 7e16cfdba..a905bf1dd 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,7 +13,6 @@ const BaseCLIHandler = require('./base-cli-handler') const Start = require('./start') -const User = require('./user') const Config = require('./config') const Tunnel = require('./tunnel') const IOFog = require('./iofog') @@ -38,7 +37,6 @@ class Cli extends BaseCLIHandler { // [constants.CMD_INIT_DB]: 'Init sqlite db for iofog-controller.', [constants.CMD_CONTROLLER]: 'Display iofog-controller service information.', [constants.CMD_HELP]: 'Display usage information.', - [constants.CMD_USER]: 'User operations.', [constants.CMD_CONFIG]: 'Set/Display iofog-controller service config.', [constants.CMD_TUNNEL]: 'Tunnel operations.', [constants.CMD_IOFOG]: 'ioFog nodes operations.', @@ -65,8 +63,6 @@ class Cli extends BaseCLIHandler { return case constants.CMD_CONTROLLER: return Controller.run({ argv }) - case constants.CMD_USER: - return User.run({ argv }) case constants.CMD_CONFIG: return Config.run({ argv }) case constants.CMD_TUNNEL: diff --git a/src/cli/iofog.js b/src/cli/iofog.js index 7d2533018..8ea90aae2 100644 --- a/src/cli/iofog.js +++ b/src/cli/iofog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,7 +15,6 @@ const BaseCLIHandler = require('./base-cli-handler') const constants = require('../helpers/constants') const logger = require('../logger') const fs = require('fs') -const CliDecorator = require('../decorators/cli-decorator') const AppHelper = require('../helpers/app-helper') const FogService = require('../services/iofog-service') const CliDataTypes = require('./cli-data-types') @@ -248,13 +247,6 @@ class IOFog extends BaseCLIHandler { description: 'ioFog version command ', group: [constants.CMD_VERSION] }, - { - name: 'user-id', - alias: 'u', - type: CliDataTypes.Integer, - description: 'User\'s id', - group: [constants.CMD_ADD, constants.CMD_UPDATE, constants.CMD_REMOVE] - }, { name: 'log-level', alias: 'L', @@ -309,37 +301,37 @@ class IOFog extends BaseCLIHandler { switch (command) { case constants.CMD_ADD: - await _executeCase(iofogCommand, constants.CMD_ADD, _createFog, true) + await _executeCase(iofogCommand, constants.CMD_ADD, _createFog) break case constants.CMD_UPDATE: - await _executeCase(iofogCommand, constants.CMD_UPDATE, _updateFog, true) + await _executeCase(iofogCommand, constants.CMD_UPDATE, _updateFog) break case constants.CMD_REMOVE: - await _executeCase(iofogCommand, constants.CMD_REMOVE, _deleteFog, true) + await _executeCase(iofogCommand, constants.CMD_REMOVE, _deleteFog) break case constants.CMD_LIST: - await _executeCase(iofogCommand, constants.CMD_LIST, _getFogList, false) + await _executeCase(iofogCommand, constants.CMD_LIST, _getFogList) break case constants.CMD_INFO: - await _executeCase(iofogCommand, constants.CMD_INFO, _getFog, false) + await _executeCase(iofogCommand, constants.CMD_INFO, _getFog) break case constants.CMD_PROVISIONING_KEY: - await _executeCase(iofogCommand, constants.CMD_PROVISIONING_KEY, _generateProvision, false) + await _executeCase(iofogCommand, constants.CMD_PROVISIONING_KEY, _generateProvision) break case constants.CMD_IOFOG_REBOOT: - await _executeCase(iofogCommand, constants.CMD_IOFOG_REBOOT, _setFogRebootCommand, false) + await _executeCase(iofogCommand, constants.CMD_IOFOG_REBOOT, _setFogRebootCommand) break case constants.CMD_VERSION: - await _executeCase(iofogCommand, constants.CMD_VERSION, _setFogVersionCommand, false) + await _executeCase(iofogCommand, constants.CMD_VERSION, _setFogVersionCommand) break case constants.CMD_HAL_HW: - await _executeCase(iofogCommand, constants.CMD_HAL_HW, _getHalHardwareInfo, false) + await _executeCase(iofogCommand, constants.CMD_HAL_HW, _getHalHardwareInfo) break case constants.CMD_HAL_USB: - await _executeCase(iofogCommand, constants.CMD_HAL_USB, _getHalUsbInfo, false) + await _executeCase(iofogCommand, constants.CMD_HAL_USB, _getHalUsbInfo) break case constants.CMD_IOFOG_PRUNE: - await _executeCase(iofogCommand, constants.CMD_IOFOG_PRUNE, _setFogPruneCommand, false) + await _executeCase(iofogCommand, constants.CMD_IOFOG_PRUNE, _setFogPruneCommand) break case constants.CMD_HELP: default: @@ -362,34 +354,28 @@ class IOFog extends BaseCLIHandler { } } -async function _executeCase (commands, commandName, f, isUserRequired) { +async function _executeCase (commands, commandName, f) { try { const obj = commands[commandName] - - if (isUserRequired) { - const decoratedFunction = CliDecorator.prepareUserById(f) - await decoratedFunction(obj) - } else { - await f(obj) - } + await f(obj) } catch (error) { logger.error(error.message) } } -async function _createFog (obj, user) { +async function _createFog (obj) { const fog = obj.file ? JSON.parse(fs.readFileSync(obj.file, 'utf8')) : _createFogObject(obj) logger.cliReq('fog add', { args: fog }) - const result = await FogService.createFogEndPoint(fog, user, true) + const result = await FogService.createFogEndPoint(fog, true) logger.cliRes(JSON.stringify({ uuid: result.uuid }, null, 2)) } -async function _updateFog (obj, user) { +async function _updateFog (obj) { const fog = obj.file ? JSON.parse(fs.readFileSync(obj.file, 'utf8')) : _createFogObject(obj) @@ -397,52 +383,52 @@ async function _updateFog (obj, user) { fog.uuid = obj.iofogUuid logger.cliReq('fog update', { args: fog }) - await FogService.updateFogEndPoint(fog, user, true) + await FogService.updateFogEndPoint(fog, true) logger.cliRes('ioFog node has been updated successfully.') } -async function _deleteFog (obj, user) { +async function _deleteFog (obj) { const fog = _createFogObject(obj) logger.cliReq('fog remove', { args: fog }) - await FogService.deleteFogEndPoint(fog, user, true) + await FogService.deleteFogEndPoint(fog, true) logger.cliRes('ioFog node has been removed successfully') } -async function _getFogList (obj, user) { +async function _getFogList (obj) { logger.cliReq('fog list') const emptyFilters = [] - const list = await FogService.getFogListEndPoint(emptyFilters, user, true, false) + const list = await FogService.getFogListEndPoint(emptyFilters, true, false) logger.cliRes(JSON.stringify(list, null, 2)) } -async function _getFog (obj, user) { +async function _getFog (obj) { const fog = _createFogObject(obj) logger.cliReq('fog info', { args: fog }) - const res = await FogService.getFogEndPoint(fog, user, true) + const res = await FogService.getFogEndPoint(fog, true) logger.cliRes(JSON.stringify(res, null, 2)) } -async function _generateProvision (obj, user) { +async function _generateProvision (obj) { const fog = _createFogObject(obj) logger.cliReq('fog provisioning-key', { args: fog }) - const response = await FogService.generateProvisioningKeyEndPoint(fog, user, true) + const response = await FogService.generateProvisioningKeyEndPoint(fog, true) logger.cliRes(JSON.stringify(response), null, 2) } -async function _setFogRebootCommand (obj, user) { +async function _setFogRebootCommand (obj) { const fog = _createFogObject(obj) logger.cliReq('fog reboot', { args: fog }) - await FogService.setFogRebootCommandEndPoint(fog, user, true) + await FogService.setFogRebootCommandEndPoint(fog, true) logger.cliRes('ioFog reboot command has been set successfully') } -async function _setFogVersionCommand (obj, user) { +async function _setFogVersionCommand (obj) { const fog = { uuid: obj.iofogUuid, versionCommand: obj.versionCommand } logger.cliReq('fog version', { args: fog }) - await FogService.setFogVersionCommandEndPoint(fog, user, true) + await FogService.setFogVersionCommandEndPoint(fog, true) logger.cliRes('ioFog version command has been set successfully') } @@ -476,10 +462,10 @@ async function _getHalUsbInfo (obj) { } } -async function _setFogPruneCommand (obj, user) { +async function _setFogPruneCommand (obj) { const fog = _createFogObject(obj) logger.cliReq('fog prune', { args: fog }) - await FogService.setFogPruneCommandEndPoint(fog, user, true) + await FogService.setFogPruneCommandEndPoint(fog, true) logger.cliRes('ioFog prune command has been set successfully') } @@ -507,7 +493,6 @@ function _createFogObject (cliData) { abstractedHardwareEnabled: AppHelper.validateBooleanCliOptions(cliData.absHwEnable, cliData.absHwDisable), fogType: cliData.fogType, - userId: cliData.userId, dockerPruningFrequency: cliData.dockerPruningFrequency, availableDiskThreshold: cliData.availableDiskThreshold, logLevel: cliData.logLevel, diff --git a/src/cli/microservice.js b/src/cli/microservice.js index b9cbe2ea2..589b0cde8 100644 --- a/src/cli/microservice.js +++ b/src/cli/microservice.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -18,13 +18,13 @@ const logger = require('../logger') const MicroserviceService = require('../services/microservices-service') const fs = require('fs') const AppHelper = require('../helpers/app-helper') -const CliDecorator = require('../decorators/cli-decorator') const CliDataTypes = require('./cli-data-types') const JSON_SCHEMA_ADD = AppHelper.stringifyCliJsonSchema( { name: 'string', config: 'string', + annotations: 'string', catalogItemId: 0, images: [ { @@ -35,7 +35,8 @@ const JSON_SCHEMA_ADD = AppHelper.stringifyCliJsonSchema( registryId: 1, application: 'string', iofogUuid: 'string', - rootHostAccess: true, + hostNetworkMode: true, + isPrivileged: true, logSize: 0, volumeMappings: [ { @@ -63,7 +64,19 @@ const JSON_SCHEMA_ADD = AppHelper.stringifyCliJsonSchema( ], cmd: [ 'string' - ] + ], + cdiDevices: [ + 'string' + ], + capAdd: [ + 'string' + ], + capDrop: [ + 'string' + ], + runAsUser: 'string', + platform: 'string', + runtime: 'string' } ) @@ -71,9 +84,11 @@ const JSON_SCHEMA_UPDATE = AppHelper.stringifyCliJsonSchema( { name: 'string', config: 'string', + annotations: 'string', rebuild: true, iofogUuid: 'string', - rootHostAccess: true, + hostNetworkMode: true, + isPrivileged: true, logSize: 0, catalogItemId: 0, images: [ @@ -99,7 +114,19 @@ const JSON_SCHEMA_UPDATE = AppHelper.stringifyCliJsonSchema( ], cmd: [ 'string' - ] + ], + cdiDevices: [ + 'string' + ], + capAdd: [ + 'string' + ], + capDrop: [ + 'string' + ], + runAsUser: 'string', + platform: 'string', + runtime: 'string' } ) @@ -186,6 +213,13 @@ class Microservice extends BaseCLIHandler { description: 'Microservice config', group: [constants.CMD_UPDATE, constants.CMD_ADD] }, + { + name: 'annotations', + alias: 'A', + type: String, + description: 'Microservice annotations', + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, { name: 'volumes', alias: 'v', @@ -202,17 +236,17 @@ class Microservice extends BaseCLIHandler { group: [constants.CMD_UPDATE, constants.CMD_ADD] }, { - name: 'root-enable', - alias: 'r', + name: 'host-network-mode', + alias: 'hN', type: Boolean, - description: 'Enable root access', + description: 'Enable host network mode', group: [constants.CMD_UPDATE, constants.CMD_ADD] }, { - name: 'root-disable', - alias: 'R', + name: 'is-privileged', + alias: 'iP', type: Boolean, - description: 'Disable root access', + description: 'Enable privileged mode', group: [constants.CMD_UPDATE, constants.CMD_ADD] }, { @@ -266,13 +300,6 @@ class Microservice extends BaseCLIHandler { description: 'Delete microservice with cleanup', group: [constants.CMD_REMOVE] }, - { - name: 'user-id', - alias: 'u', - type: CliDataTypes.Integer, - description: 'User\'s id', - group: [constants.CMD_ADD] - }, { name: 'mapping-id', alias: 'm', @@ -295,6 +322,54 @@ class Microservice extends BaseCLIHandler { description: 'Microservice container command and argument(s)', multiple: true, group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'cdiDevices', + alias: 'D', + type: String, + description: 'Map CDI devices to microservice container', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'capAdd', + alias: 'cA', + type: String, + description: 'A list of kernel capabilities to add to the container.', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'capDrop', + alias: 'cD', + type: String, + description: 'A list of kernel capabilities to drop to the container.', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'user', + alias: 'U', + type: String, + description: 'Run Microservice as a user)', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'platform', + alias: 'L', + type: String, + description: 'Microservice image platform to be used', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] + }, + { + name: 'runtime', + alias: 'y', + type: String, + description: 'Microservice container runtime definition', + multiple: true, + group: [constants.CMD_UPDATE, constants.CMD_ADD] } ] this.commands = { @@ -324,43 +399,43 @@ class Microservice extends BaseCLIHandler { switch (command) { case constants.CMD_ADD: - await _executeCase(microserviceCommand, constants.CMD_ADD, _createMicroservice, true) + await _executeCase(microserviceCommand, constants.CMD_ADD, _createMicroservice) break case constants.CMD_UPDATE: - await _executeCase(microserviceCommand, constants.CMD_UPDATE, _updateMicroservice, false) + await _executeCase(microserviceCommand, constants.CMD_UPDATE, _updateMicroservice) break case constants.CMD_REMOVE: - await _executeCase(microserviceCommand, constants.CMD_REMOVE, _removeMicroservice, false) + await _executeCase(microserviceCommand, constants.CMD_REMOVE, _removeMicroservice) break case constants.CMD_LIST: - await _executeCase(microserviceCommand, constants.CMD_LIST, _listMicroservices, false) + await _executeCase(microserviceCommand, constants.CMD_LIST, _listMicroservices) break case constants.CMD_INFO: - await _executeCase(microserviceCommand, constants.CMD_INFO, _getMicroservice, false) + await _executeCase(microserviceCommand, constants.CMD_INFO, _getMicroservice) break case constants.CMD_ROUTE_CREATE: - await _executeCase(microserviceCommand, constants.CMD_ROUTE_CREATE, _createRoute, false) + await _executeCase(microserviceCommand, constants.CMD_ROUTE_CREATE, _createRoute) break case constants.CMD_ROUTE_REMOVE: - await _executeCase(microserviceCommand, constants.CMD_ROUTE_REMOVE, _removeRoute, false) + await _executeCase(microserviceCommand, constants.CMD_ROUTE_REMOVE, _removeRoute) break case constants.CMD_PORT_MAPPING_CREATE: - await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_CREATE, _createPortMapping, false) + await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_CREATE, _createPortMapping) break case constants.CMD_PORT_MAPPING_REMOVE: - await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_REMOVE, _removePortMapping, false) + await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_REMOVE, _removePortMapping) break case constants.CMD_PORT_MAPPING_LIST: - await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_LIST, _listPortMappings, false) + await _executeCase(microserviceCommand, constants.CMD_PORT_MAPPING_LIST, _listPortMappings) break case constants.CMD_VOLUME_MAPPING_CREATE: - await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_CREATE, _createVolumeMapping, false) + await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_CREATE, _createVolumeMapping) break case constants.CMD_VOLUME_MAPPING_REMOVE: - await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_REMOVE, _removeVolumeMapping, false) + await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_REMOVE, _removeVolumeMapping) break case constants.CMD_VOLUME_MAPPING_LIST: - await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_LIST, _listVolumeMappings, false) + await _executeCase(microserviceCommand, constants.CMD_VOLUME_MAPPING_LIST, _listVolumeMappings) break case constants.CMD_HELP: default: @@ -437,28 +512,22 @@ class Microservice extends BaseCLIHandler { } } -async function _executeCase (commands, commandName, f, isUserRequired) { +async function _executeCase (commands, commandName, f) { try { const obj = commands[commandName] - - if (isUserRequired) { - const decoratedFunction = CliDecorator.prepareUserById(f) - await decoratedFunction(obj) - } else { - await f(obj) - } + await f(obj) } catch (error) { logger.error(error.message) } } -const _createRoute = async function (obj, user) { +const _createRoute = async function (obj) { try { const arr = obj.route.split(':') const sourceMicroserviceUuid = arr[0] const destMicroserviceUuid = arr[1] logger.cliReq('microservice route-create', { args: { source: sourceMicroserviceUuid, dest: destMicroserviceUuid } }) - await MicroserviceService.createRouteEndPoint(sourceMicroserviceUuid, destMicroserviceUuid, user, true) + await MicroserviceService.createRouteEndPoint(sourceMicroserviceUuid, destMicroserviceUuid, true) logger.cliRes(`Microservice route with source microservice ${sourceMicroserviceUuid} and dest microservice ${destMicroserviceUuid} has been created successfully.`) } catch (e) { @@ -466,13 +535,13 @@ const _createRoute = async function (obj, user) { } } -const _removeRoute = async function (obj, user) { +const _removeRoute = async function (obj) { try { const arr = obj.route.split(':') const sourceMicroserviceUuid = arr[0] const destMicroserviceUuid = arr[1] logger.cliReq('microservice route-remove', { args: { source: sourceMicroserviceUuid, dest: destMicroserviceUuid } }) - await MicroserviceService.deleteRouteEndPoint(sourceMicroserviceUuid, destMicroserviceUuid, user, true) + await MicroserviceService.deleteRouteEndPoint(sourceMicroserviceUuid, destMicroserviceUuid, true) logger.cliRes('Microservice route with source microservice ' + sourceMicroserviceUuid + ' and dest microservice ' + destMicroserviceUuid + 'has been removed successfully.') } catch (e) { @@ -480,83 +549,83 @@ const _removeRoute = async function (obj, user) { } } -const _createPortMapping = async function (obj, user) { +const _createPortMapping = async function (obj) { const mapping = parsePortMappingObject(obj.mapping, ErrorMessages.CLI.INVALID_PORT_MAPPING) logger.cliReq('microservice port-mapping-create', { args: mapping }) - await MicroserviceService.createPortMappingEndPoint(obj.microserviceUuid, mapping, user, true) + await MicroserviceService.createPortMappingEndPoint(obj.microserviceUuid, mapping, true) logger.cliRes('Port mapping has been created successfully.') } -const _createVolumeMapping = async function (obj, user) { +const _createVolumeMapping = async function (obj) { const mapping = parseVolumeMappingObject(obj.mapping, ErrorMessages.CLI.INVALID_VOLUME_MAPPING) logger.cliReq('microservice volume-mapping-create', { args: mapping }) - const result = await MicroserviceService.createVolumeMappingEndPoint(obj.microserviceUuid, mapping, user, true) + const result = await MicroserviceService.createVolumeMappingEndPoint(obj.microserviceUuid, mapping, true) logger.cliRes(JSON.stringify({ id: result.id }, null, 2)) } -const _removePortMapping = async function (obj, user) { +const _removePortMapping = async function (obj) { try { logger.cliReq('microservice port-mapping-remove', { args: obj }) - await MicroserviceService.deletePortMappingEndPoint(obj.microserviceUuid, obj.internalPort, user, true) + await MicroserviceService.deletePortMappingEndPoint(obj.microserviceUuid, obj.internalPort, true) logger.cliRes('Port mapping has been removed successfully.') } catch (e) { logger.error(e.message) } } -const _removeVolumeMapping = async function (obj, user) { +const _removeVolumeMapping = async function (obj) { try { logger.cliReq('microservice volume-mapping-remove', { args: obj }) - await MicroserviceService.deleteVolumeMappingEndPoint(obj.microserviceUuid, obj.mappingId, user, true) + await MicroserviceService.deleteVolumeMappingEndPoint(obj.microserviceUuid, obj.mappingId, true) logger.cliRes('Volume mapping has been deleted successfully.') } catch (e) { logger.error(e.message) } } -const _listPortMappings = async function (obj, user) { +const _listPortMappings = async function (obj) { logger.cliReq('microservice port-mapping-list', { args: { microserviceUuid: obj.microserviceUuid } }) - const result = await MicroserviceService.listMicroservicePortMappingsEndPoint(obj.microserviceUuid, user, true) + const result = await MicroserviceService.listMicroservicePortMappingsEndPoint(obj.microserviceUuid, true) logger.cliRes(JSON.stringify(result, null, 2)) } -const _listVolumeMappings = async function (obj, user) { +const _listVolumeMappings = async function (obj) { logger.cliReq('microservice volume-mapping-list', { args: { microserviceUuid: obj.microserviceUuid } }) - const result = await MicroserviceService.listVolumeMappingsEndPoint(obj.microserviceUuid, user, true) + const result = await MicroserviceService.listVolumeMappingsEndPoint(obj.microserviceUuid, true) logger.cliRes(JSON.stringify(result, null, 2)) } -const _removeMicroservice = async function (obj, user) { +const _removeMicroservice = async function (obj) { const microserviceData = { withCleanup: obj.cleanup } logger.cliReq('microservice remove', { args: { microserviceUuid: obj.microserviceUuid, withCleanup: obj.cleanup } }) - await MicroserviceService.deleteMicroserviceEndPoint(obj.microserviceUuid, microserviceData, user, true) + await MicroserviceService.deleteMicroserviceEndPoint(obj.microserviceUuid, microserviceData, true) logger.cliRes('Microservice has been removed successfully.') } const _listMicroservices = async function () { logger.cliReq('microservice list') - const result = await MicroserviceService.listMicroservicesEndPoint('', {}, true) + const result = await MicroserviceService.listMicroservicesEndPoint('', true) logger.cliRes(JSON.stringify(result, null, 2)) } -const _getMicroservice = async function (obj, user) { +const _getMicroservice = async function (obj) { logger.cliReq('microservice info', { args: { microserviceUuid: obj.microserviceUuid } }) - const result = await MicroserviceService.getMicroserviceEndPoint(obj.microserviceUuid, user, true) + const result = await MicroserviceService.getMicroserviceEndPoint(obj.microserviceUuid, true) logger.cliRes(JSON.stringify(result, null, 2)) } -const _createMicroservice = async function (obj, user) { +const _createMicroservice = async function (obj) { const microservice = obj.file ? JSON.parse(fs.readFileSync(obj.file, 'utf8')) : _createMicroserviceObject(obj) logger.cliReq('microservice add', { args: microservice }) - const result = await MicroserviceService.createMicroserviceEndPoint(microservice, user, true) + const result = await MicroserviceService.createMicroserviceEndPoint(microservice, true) const output = { uuid: result.uuid } @@ -567,13 +636,13 @@ const _createMicroservice = async function (obj, user) { logger.cliRes(JSON.stringify(output, null, 2)) } -const _updateMicroservice = async function (obj, user) { +const _updateMicroservice = async function (obj) { const microservice = obj.file ? JSON.parse(fs.readFileSync(obj.file, 'utf8')) : _updateMicroserviceObject(obj) logger.cliReq('microservice update', { args: microservice }) - await MicroserviceService.updateMicroserviceEndPoint(obj.microserviceUuid, microservice, user, true) + await MicroserviceService.updateMicroserviceEndPoint(obj.microserviceUuid, microservice, true) logger.cliRes('Microservice has been updated successfully.') } @@ -594,11 +663,19 @@ const _updateMicroserviceObject = function (obj) { const microserviceObj = { name: obj.name, config: obj.config, + annotations: obj.annotations, iofogUuid: obj.iofogUuid, - rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), + hostNetworkMode: obj.hostNetworkMode, + isPrivileged: obj.isPrivileged, logSize: (obj.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1, rebuild: obj.rebuild, cmd: obj.cmd, + cdiDevices: obj.cdiDevices, + capAdd: obj.capAdd, + capDrop: obj.capDrop, + runAsUser: obj.runAsUser, + platform: obj.platform, + runtime: obj.runtime, env, images: obj.images, catalogItemId: parseInt(obj.catalogItemId) || undefined, @@ -652,14 +729,22 @@ const _createMicroserviceObject = function (obj) { const microserviceObj = { name: obj.name, config: obj.config, + annotations: obj.annotations, catalogItemId: parseInt(obj.catalogId) || undefined, application: obj.applicationName, registryId: parseInt(obj.registryId) || undefined, iofogUuid: obj.iofogUuid, - rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), + hostNetworkMode: obj.hostNetworkMode, + isPrivileged: obj.isPrivileged, logSize: (obj.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1, routes: obj.routes, cmd: obj.cmd, + cdiDevices: obj.cdiDevices, + capAdd: obj.capAdd, + capDrop: obj.capDrop, + runAsUser: obj.runAsUser, + platform: obj.platform, + runtime: obj.runtime, env, images: [] } diff --git a/src/cli/registry.js b/src/cli/registry.js index dd7311af0..1efee349a 100644 --- a/src/cli/registry.js +++ b/src/cli/registry.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,7 +14,6 @@ const BaseCLIHandler = require('./base-cli-handler') const constants = require('../helpers/constants') const logger = require('../logger') -const CliDecorator = require('../decorators/cli-decorator') const RegistryService = require('../services/registry-service') const AppHelper = require('../helpers/app-helper') const CliDataTypes = require('./cli-data-types') @@ -65,20 +64,6 @@ class Registry extends BaseCLIHandler { description: 'Password', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, - { - name: 'requires-certificate', - alias: 'c', - type: Boolean, - description: 'Requires certificate', - group: [constants.CMD_ADD, constants.CMD_UPDATE] - }, - { - name: 'certificate', - alias: 'C', - type: String, - description: 'Certificate', - group: [constants.CMD_ADD, constants.CMD_UPDATE] - }, { name: 'email', alias: 'e', @@ -86,13 +71,6 @@ class Registry extends BaseCLIHandler { description: 'Email address', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, - { - name: 'user-id', - alias: 'u', - type: CliDataTypes.Integer, - description: 'User\'s id', - group: [constants.CMD_ADD] - }, { name: 'item-id', alias: 'i', @@ -119,16 +97,16 @@ class Registry extends BaseCLIHandler { switch (command) { case constants.CMD_ADD: - await _executeCase(registryCommand, constants.CMD_ADD, _createRegistry, true) + await _executeCase(registryCommand, constants.CMD_ADD, _createRegistry) break case constants.CMD_REMOVE: - await _executeCase(registryCommand, constants.CMD_REMOVE, _deleteRegistry, false) + await _executeCase(registryCommand, constants.CMD_REMOVE, _deleteRegistry) break case constants.CMD_UPDATE: - await _executeCase(registryCommand, constants.CMD_UPDATE, _updateRegistry, false) + await _executeCase(registryCommand, constants.CMD_UPDATE, _updateRegistry) break case constants.CMD_LIST: - await _executeCase(registryCommand, constants.CMD_LIST, _getRegistries, false) + await _executeCase(registryCommand, constants.CMD_LIST, _getRegistries) break case constants.CMD_HELP: default: @@ -140,28 +118,28 @@ class Registry extends BaseCLIHandler { } } -async function _createRegistry (obj, user) { +async function _createRegistry (obj) { const registry = _createRegistryObject(obj) const logRegistry = Object.assign({}, registry) delete logRegistry.password logger.cliReq('registry add', { args: logRegistry }) - const response = await RegistryService.createRegistry(registry, user) + const response = await RegistryService.createRegistry(registry) logger.cliRes(JSON.stringify({ id: response.id }, null, 2)) } -async function _getRegistries (obj, user) { +async function _getRegistries (obj) { logger.cliReq('registry list') - const result = await RegistryService.findRegistries(user, true) + const result = await RegistryService.findRegistries(true) logger.cliRes(JSON.stringify(result, null, 2)) } -async function _deleteRegistry (obj, user) { +async function _deleteRegistry (obj) { logger.cliReq('registry remove', { args: { id: obj.itemId } }) - await RegistryService.deleteRegistry({ id: obj.itemId }, user, true) + await RegistryService.deleteRegistry({ id: obj.itemId }, true) logger.cliRes('Registry has been removed successfully.') } @@ -176,16 +154,10 @@ async function _updateRegistry (obj) { logger.cliRes('Registry has been updated successfully.') } -async function _executeCase (commands, commandName, f, isUserRequired) { +async function _executeCase (commands, commandName, f) { try { const obj = commands[commandName] - - if (isUserRequired) { - const decoratedFunction = CliDecorator.prepareUserById(f) - await decoratedFunction(obj) - } else { - await f(obj) - } + await f(obj) } catch (error) { logger.error(error.message) } @@ -197,9 +169,7 @@ function _createRegistryObject (cliData) { username: cliData.username, password: cliData.password, isPublic: AppHelper.validateBooleanCliOptions(cliData.public, cliData.private), - email: cliData.email, - requiresCert: cliData.requiresCertificate, - certificate: cliData.certificate + email: cliData.email } } diff --git a/src/cli/start.js b/src/cli/start.js index 5088626a2..0b169502f 100644 --- a/src/cli/start.js +++ b/src/cli/start.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -43,11 +43,11 @@ class Start extends BaseCLIHandler { daemon._options.silent = false } const configuration = { - devMode: config.get('Server:DevMode'), - port: config.get('Server:Port'), - sslKey: config.get('Server:SslKey'), - sslCert: config.get('Server:SslCert'), - intermedKey: config.get('Server:IntermediateCert') + devMode: config.get('server.devMode'), + port: config.get('server.port'), + sslKey: config.get('server.ssl.path.key'), + sslCert: config.get('server.ssl.path.cert'), + intermedKey: config.get('server.ssl.path.intermediateCert') } const pid = daemon.status() @@ -76,8 +76,7 @@ function checkDaemon (daemon, configuration) { iterationsCount++ const pid = daemon.status() if (pid === 0) { - logger.error('Error: port is probably allocated, or ssl_key or ssl_cert or intermediate_cert ' + - 'is either missing or invalid.') + logger.error('Error: port is probably allocated, or ssl_key or ssl_cert is either missing or invalid.') return reject(new Error('Error starting ioFog-Controller')) } @@ -95,8 +94,8 @@ function checkDaemon (daemon, configuration) { } function checkServerProtocol (configuration) { - const { devMode, port, sslKey, sslCert, intermedKey } = configuration - if (!devMode && sslKey && sslCert && intermedKey) { + const { devMode, port, sslKey, sslCert } = configuration + if (!devMode && sslKey && sslCert) { logger.cliRes(`==> 🌎 HTTPS server listening on port ${port}. Open up https://localhost:${port}/ in your browser.`) } else { logger.cliRes(`==> 🌎 Listening on port ${port}. Open up http://localhost:${port}/ in your browser.`) diff --git a/src/cli/tunnel.js b/src/cli/tunnel.js index 413ae98f8..75efe9ff8 100644 --- a/src/cli/tunnel.js +++ b/src/cli/tunnel.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -16,7 +16,6 @@ const constants = require('../helpers/constants') const fs = require('fs') const logger = require('../logger') const TunnelService = require('../services/tunnel-service') -const CliDecorator = require('../decorators/cli-decorator') const Errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') const CliDataTypes = require('./cli-data-types') @@ -99,10 +98,10 @@ class Tunnel extends BaseCLIHandler { switch (command) { case constants.CMD_UPDATE: - await _executeCase(tunnelCommand, constants.CMD_UPDATE, _updateTunnel, false) + await _executeCase(tunnelCommand, constants.CMD_UPDATE, _updateTunnel) break case constants.CMD_LIST: - await _executeCase(tunnelCommand, constants.CMD_LIST, _tunnelList, false) + await _executeCase(tunnelCommand, constants.CMD_LIST, _tunnelList) break default: return this.help([]) @@ -113,7 +112,7 @@ class Tunnel extends BaseCLIHandler { } } -async function _updateTunnel (obj, user) { +async function _updateTunnel (obj) { const action = obj.action const tunnel = _createTunnelObject(obj) @@ -127,10 +126,10 @@ async function _updateTunnel (obj, user) { switch (action) { case 'open': - await TunnelService.openTunnel(tunnel, user, true) + await TunnelService.openTunnel(tunnel, true) break case 'close': - await TunnelService.closeTunnel({ iofogUuid: tunnel.iofogUuid }, user) + await TunnelService.closeTunnel({ iofogUuid: tunnel.iofogUuid }) break default: throw new Errors.ValidationError(ErrorMessages.INVALID_ACTION_PROPERTY) @@ -144,16 +143,10 @@ async function _tunnelList () { logger.cliRes(JSON.stringify(tunnels, null, 2)) } -async function _executeCase (commands, commandName, f, isUserRequired) { +async function _executeCase (commands, commandName, f) { try { const obj = commands[commandName] - - if (isUserRequired) { - const decoratedFunction = CliDecorator.prepareUserById(f) - await decoratedFunction(obj) - } else { - await f(obj) - } + await f(obj) } catch (error) { logger.error(error.message) } diff --git a/src/cli/user.js b/src/cli/user.js deleted file mode 100644 index 9f94bddde..000000000 --- a/src/cli/user.js +++ /dev/null @@ -1,188 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseCLIHandler = require('./base-cli-handler') -const constants = require('../helpers/constants') -const UserService = require('../services/user-service') -const logger = require('../logger') -const AppHelper = require('../helpers/app-helper') -const AuthDecorator = require('../decorators/cli-decorator') -const Validator = require('../schemas') - -class User extends BaseCLIHandler { - constructor () { - super() - - this.name = constants.CMD_USER - this.commandDefinitions = [ - { - name: 'command', - defaultOption: true, - description: 'add, remove, update, list, generate-token', - group: constants.CMD - }, - { - name: 'first-name', - alias: 'f', - type: String, - description: 'User\'s first name', - group: [constants.CMD_ADD, constants.CMD_UPDATE] - }, - { - name: 'last-name', - alias: 'l', - type: String, - description: 'User\'s last name', - group: [constants.CMD_ADD, constants.CMD_UPDATE] - }, - { - name: 'email', - alias: 'e', - type: String, - description: 'User\'s email address', - group: [constants.CMD_ADD, constants.CMD_GENERATE_TOKEN, constants.CMD_REMOVE, - constants.CMD_UPDATE, constants.CMD_ACTIVATE, constants.CMD_SUSPEND] - }, - { - name: 'password', - alias: 'p', - type: String, - description: 'User\'s password', - group: [constants.CMD_ADD, constants.CMD_UPDATE] - }, - { - name: 'force', - alias: 'F', - type: Boolean, - description: 'User\'s force delete', - group: [constants.CMD_REMOVE] - } - ] - this.commands = { - [constants.CMD_ADD]: 'Add a new user.', - [constants.CMD_UPDATE]: 'Update existing user.', - [constants.CMD_REMOVE]: 'Delete a user.', - [constants.CMD_LIST]: 'List all users.', - [constants.CMD_GENERATE_TOKEN]: 'Generate token for a user.', - [constants.CMD_ACTIVATE]: 'Activate a user.', - [constants.CMD_SUSPEND]: 'Suspend a user.' - } - } - - async run (args) { - try { - const userCommand = this.parseCommandLineArgs(this.commandDefinitions, { argv: args.argv, partial: false }) - - const command = userCommand.command.command - - this.validateParameters(command, this.commandDefinitions, args.argv) - - switch (command) { - case constants.CMD_ADD: - await _executeCase(userCommand, constants.CMD_ADD, _createUser, false) - break - case constants.CMD_UPDATE: - await _executeCase(userCommand, constants.CMD_UPDATE, _updateUserDetails, true) - break - case constants.CMD_REMOVE: - await _executeCase(userCommand, constants.CMD_REMOVE, _deleteUser, true) - break - case constants.CMD_LIST: - await _executeCase(userCommand, constants.CMD_LIST, _getAllUsers, false) - break - case constants.CMD_GENERATE_TOKEN: - await _executeCase(userCommand, constants.CMD_GENERATE_TOKEN, _generateToken, true) - break - case constants.CMD_ACTIVATE: - await _executeCase(userCommand, constants.CMD_ACTIVATE, _activateUser, true) - break - case constants.CMD_SUSPEND: - await _executeCase(userCommand, constants.CMD_SUSPEND, _suspendUser, true) - break - case constants.CMD_HELP: - default: - return this.help([]) - } - } catch (error) { - this.handleCLIError(error, args.argv) - } - } -} - -const _executeCase = async function (userCommand, commandName, f, isUserRequired) { - try { - const item = userCommand[commandName] - - if (isUserRequired) { - const decoratedFunction = AuthDecorator.prepareUserByEmail(f) - await decoratedFunction(item) - } else { - await f(item) - } - } catch (error) { - logger.error(error.message) - } -} - -const _createUser = async function (user) { - logger.cliReq('user add', { args: user }) - await Validator.validate(user, Validator.schemas.signUp) - - user.password = AppHelper.encryptText(user.password, user.email) - - const response = await UserService.signUp(user, true) - logger.cliRes(JSON.stringify({ - id: response.userId - }), null, 2) -} - -const _updateUserDetails = async function (userDetails, user) { - logger.cliReq('user update', { args: userDetails }) - await UserService.updateUserDetails(user, userDetails, true) - logger.cliRes('User updated successfully.') -} - -const _deleteUser = async function (obj, user) { - logger.cliReq('user remove', { args: { user: user.dataValues, force: obj.force } }) - await UserService.deleteUser(obj.force, user, true) - logger.cliRes('User removed successfully.') -} - -const _getAllUsers = async function () { - logger.cliReq('user list') - const users = await UserService.list(true) - logger.cliRes(JSON.stringify(users, null, 2)) -} - -const _generateToken = async function (emailObj, user) { - logger.cliReq('user generate-token', { args: user.dataValues }) - const response = await UserService.login(user, true) - logger.cliRes(JSON.stringify(response, null, 2)) -} - -const _activateUser = async function (emailObj, user) { - const codeData = { - userId: user.id - } - logger.cliReq('user activate', { args: codeData }) - await UserService.activateUser(codeData, true) - logger.cliRes('User activated successfully.') -} - -const _suspendUser = async function (emailObj, user) { - logger.cliReq('user suspend', { args: user.dataValues }) - await UserService.suspendUser(user, true) - logger.cliRes('User suspended successfully.') -} - -module.exports = new User() diff --git a/src/config/constants.js b/src/config/constants.js deleted file mode 100644 index 99f0fcf92..000000000 --- a/src/config/constants.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -module.exports = { - 'App:Name': 'iofog-controller', - 'Viewer:Port': 80, - - 'Server:Port': 51121, - 'Server:DevMode': false, - - 'Email:ActivationEnabled': false, - 'Email:HomeUrl': 'https://iofog.org', - - 'Service:LogsDirectory': '/var/log/iofog-controller', - 'Service:LogsFileSize': 10485760, - 'Service:LogsFileCount': 10, - - 'Settings:DefaultJobIntervalSeconds': 120, - 'Settings:UserTokenExpirationIntervalSeconds': 3600, - 'Settings:FogTokenExpirationIntervalSeconds': 3600, - 'Settings:KubeletTokenExpirationIntervalSeconds': 3600, - 'Settings:SchedulerTokenExpirationIntervalSeconds': 3600, - 'Settings:FogStatusUpdateIntervalSeconds': 30, - 'Settings:FogStatusUpdateTolerance': 3, - - 'Diagnostics:DiagnosticDir': 'diagnostic' -} diff --git a/src/config/controller.yaml b/src/config/controller.yaml new file mode 100644 index 000000000..1e842b7c2 --- /dev/null +++ b/src/config/controller.yaml @@ -0,0 +1,136 @@ +# Application Configuration +app: + name: pot-controller # Application name + controlPlane: Remote # Control plane type: Remote or Kubernetes or Local + +# Server Configuration +server: + port: 51121 # Server port number + devMode: true + webSocket: + perMessageDeflate: false + allowExtensions: false # Disable all extensions + pingInterval: 30000 # Ping interval in milliseconds (30 seconds) + pongTimeout: 10000 # Pong timeout in milliseconds (10 seconds) + handshakeTimeout: 10000 # 10 seconds + maxPayload: 1048576 # 1MB + maxFrameSize: 65536 # 64KB + session: + timeout: 3600000 # Session timeout in milliseconds (1 hour) + maxConnections: 100 # Maximum connections per session + cleanupInterval: 30000 # Session cleanup interval (30 seconds) + security: + maxConnectionsPerIp: 10 + maxRequestsPerMinute: 60 + maxPayload: 1048576 # 1MB + # ssl: + # path: + # key: "" # SSL key file path + # cert: "" # SSL certificate file path + # intermediateCert: "" # Intermediate certificate file path + # base64: + # key: # SSL key in base64 format + # cert: # SSL certificate in base64 format + # intermediateCert: # Intermediate certificate in base64 format + +# Viewer Configuration +viewer: + port: 8008 # Viewer port number + url: "" # Viewer URL + +# Logging Configuration +log: + level: info + directory: /var/log/iofog-controller # Log directory + fileSize: 1073741824 # Maximum log file size in bytes (1GB) + fileCount: 10 # Maximum number of log files + +# Settings Configuration +settings: + # defaultJobInterval: 120 # Default job interval in seconds + fogStatusUpdateInterval: 30 # Fog status update interval in seconds + fogStatusUpdateTolerance: 3 # Fog status update tolerance + fogExpiredTokenCleanupInterval: 300 # Fog expired token cleanup interval in seconds + eventRetentionDays: 7 # Days to retain events (default: 7) + eventCleanupInterval: 86400 # Cleanup job interval in seconds (default: 24 hours) + eventAuditEnabled: true # Enable/disable event auditing + eventCaptureIpAddress: true # Capture IP address (default: true, set false for privacy compliance) + +# Database Configuration +database: + provider: sqlite # Database provider (sqlite/mysql/postgres) + # mysql: + # host: "" # MySQL host + # port: 3306 # MySQL port + # username: "" # MySQL username + # password: "" # MySQL password + # databaseName: "" # MySQL database name + # useSSL: false # Use SSL for MySQL connection + # sslCA: "" # MySQL SSL CA in base64 encoded string + # postgres: + # host: "" # PostgreSQL host + # port: 5432 # PostgreSQL port + # username: "" # PostgreSQL username + # password: "" # PostgreSQL password + # databaseName: "" # PostgreSQL database name + # useSSL: false # Use SSL for PostgreSQL connection + # sslCA: "" # PostgreSQL SSL CA in base64 encoded string + sqlite: + databaseName: dev_database.sqlite # SQLite database file name + logging: false # Enable SQLite query logging + transactionType: IMMEDIATE # SQLite transaction type + pool: + maxActive: 1 # Maximum active connections + max: 1 # Maximum total connections + min: 0 # Minimum connections + idle: 20000 # Idle timeout in milliseconds + +# Auth Configuration +# auth: +# realm: # Keycloak realm +# realmKey: # Realm public key +# url: # Keycloak authentication server URL +# sslRequired: # SSL requirement level +# client: +# id: # ControllerClient ID +# secret: # ControllerClient Client secret +# viewerClient: # Viewer client ID + +# Bridge Ports Configuration for Services +bridgePorts: + range: "10024-65535" # Bridge ports range + +# System Images Configuration +systemImages: + router: + "1": "ghcr.io/datasance/router:latest" + "2": "ghcr.io/datasance/router:latest" + debug: + "1": "ghcr.io/datasance/node-debugger:latest" + "2": "ghcr.io/datasance/node-debugger:latest" + +# Diagnostics Configuration +diagnostics: + directory: "diagnostic" # Diagnostics directory + + +# OpenTelemetry Configuration +# otel: +# enabled: false # true/disable OpenTelemetry +# serviceName: "pot-controller" # Service name for traces +# endpoint: "http://localhost:4318/v1/traces" # OTel endpoint +# protocol: http/protobuf # Exporter OTLP Protocol (grpc or http/protobuf) +# headers: "" # A list of headers to apply to all outgoing data (traces, metrics, and logs). +# resourceAttributes: "service.version=3.5.0,deployment.environment=production,team=devops" # Resource attributes +# metrics: +# exporter: otlp # Otel metrics exporter +# interval: 1000 # Metrics collection interval in ms +# logs: +# level: info # Log level +# propagators: "tracecontext,baggage" # Context propagation +# traces: +# sampler: "parentbased_traceidratio" # Sampler to be used for traces +# samplerArg: 0.1 +# batch: # Batch size and timeout for telemetry data +# size: 512 # Maximum batch size +# delay: 1000 # Delay interval (in milliseconds) between two consecutive exports diff --git a/src/config/default.json b/src/config/default.json deleted file mode 100644 index df913aa36..000000000 --- a/src/config/default.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "App": { - "Name": "iofog-controller" - }, - "Viewer": { - "Port": 80 - }, - "Kubelet": { - "Uri": "http://localhost:1234" - }, - "Server": { - "Port": 51121, - "DevMode": false - }, - "Email": { - "ActivationEnabled": false, - "HomeUrl": "https://google.com" - }, - "Service": { - "LogsDirectory": "/var/log/iofog-controller", - "LogsFileSize": 10485760, - "LogsFileCount": 10 - }, - "Settings": { - "DefaultJobIntervalSeconds": 120, - "UserTokenExpirationIntervalSeconds": 3600, - "FogTokenExpirationIntervalSeconds": 3600, - "FogStatusUpdateIntervalSeconds": 30, - "FogStatusUpdateTolerance": 3 - }, - "Diagnostics": { - "DiagnosticDir": "diagnostic" - }, - "PublicPorts": { - "Range": "6000-7000", - "ProxyBrokerUrl": "http://localhost:3000", - "ProxyBrokerToken": "" - }, - "SystemImages": { - "Router": { - "1": "iofog/router:latest", - "2": "iofog/router:latest" - }, - "Proxy": { - "1": "iofog/proxy:latest", - "2": "iofog/proxy-arm:latest" - }, - "PortRouter": { - "1": "iofog/port-router:latest", - "2": "iofog/port-router:latest" - } - } -} diff --git a/src/config/development.json b/src/config/development.json deleted file mode 100644 index 08c72584d..000000000 --- a/src/config/development.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "App": { - "Name": "iofog-controller-dev" - }, - "Viewer": { - "Port": 80 - }, - "Kubelet": { - "Uri": "http://localhost:1234" - }, - "Server": { - "Port": 51121, - "DevMode": true - }, - "Email": { - "ActivationEnabled": false, - "HomeUrl": "https://google.com" - }, - "Service": { - "LogsDirectory": "/var/log/iofog-controller", - "LogsFileSize": 10485760, - "LogsFileCount": 10 - }, - "Settings": { - "UserTokenExpirationIntervalSeconds": 360000, - "FogTokenExpirationIntervalSeconds": 3600000, - "FogStatusUpdateIntervalSeconds": 30, - "FogStatusUpdateTolerance": 3 - }, - "Tunnel": { - "Username": "username", - "Password": "password", - "Host": "23.253.111.231", - "RsaKey": "rsa", - "Lport": 22, - "PortRange": "2000-10000" - }, - "Diagnostics": { - "DiagnosticDir": "diagnostic" - }, - "Database": { - "Provider": "sqlite", - "Config": { - "databaseName": "dev_database.sqlite", - "logging": false, - "transactionType": "IMMEDIATE", - "pool": { - "maxactive": 1, - "max": 1, - "min": 0, - "idle": 20000 - } - } - } -} \ No newline at end of file diff --git a/src/config/env-mapping.js b/src/config/env-mapping.js new file mode 100644 index 000000000..4a097817d --- /dev/null +++ b/src/config/env-mapping.js @@ -0,0 +1,108 @@ +module.exports = { + // Application Configuration + 'APP_NAME': 'app.name', + 'CONTROL_PLANE': 'app.controlPlane', + + // Server Configuration + 'SERVER_PORT': 'server.port', + 'SERVER_DEV_MODE': 'server.devMode', + + 'WS_PING_INTERVAL': 'server.webSocket.pingInterval', + 'WS_PONG_TIMEOUT': 'server.webSocket.pongTimeout', + 'WS_MAX_PAYLOAD': 'server.webSocket.maxPayload', + 'WS_SESSION_TIMEOUT': 'server.webSocket.session.timeout', + 'WS_SESSION_MAX_CONNECTIONS': 'server.webSocket.session.maxConnections', + 'WS_CLEANUP_INTERVAL': 'server.webSocket.session.cleanupInterval', + 'WS_SECURITY_MAX_CONNECTIONS_PER_IP': 'server.webSocket.security.maxConnectionsPerIp', + 'WS_SECURITY_MAX_REQUESTS_PER_MINUTE': 'server.webSocket.security.maxRequestsPerMinute', + 'WS_SECURITY_MAX_PAYLOAD': 'server.webSocket.security.maxPayload', + + // SSL Configuration + 'SSL_PATH_KEY': 'server.ssl.path.key', + 'SSL_PATH_CERT': 'server.ssl.path.cert', + 'SSL_PATH_INTERMEDIATE_CERT': 'server.ssl.path.intermediateCert', + 'SSL_BASE64_KEY': 'server.ssl.base64.key', + 'SSL_BASE64_CERT': 'server.ssl.base64.cert', + 'SSL_BASE64_INTERMEDIATE_CERT': 'server.ssl.base64.intermediateCert', + + // Viewer Configuration + 'VIEWER_PORT': 'viewer.port', + 'VIEWER_URL': 'viewer.url', + + // Logging Configuration + 'LOG_LEVEL': 'log.level', + 'LOG_DIRECTORY': 'log.directory', + 'LOG_FILE_SIZE': 'log.fileSize', + 'LOG_FILE_COUNT': 'log.fileCount', + + // Settings Configuration + 'FOG_STATUS_UPDATE_INTERVAL': 'settings.fogStatusUpdateInterval', + 'FOG_STATUS_UPDATE_TOLERANCE': 'settings.fogStatusUpdateTolerance', + 'FOG_EXPIRED_TOKEN_CLEANUP_INTERVAL': 'settings.fogExpiredTokenCleanupInterval', + 'EVENT_RETENTION_DAYS': 'settings.eventRetentionDays', + 'EVENT_CLEANUP_INTERVAL': 'settings.eventCleanupInterval', + 'EVENT_AUDIT_ENABLED': 'settings.eventAuditEnabled', + 'EVENT_CAPTURE_IP_ADDRESS': 'settings.eventCaptureIpAddress', + + // Database Configuration + 'DB_PROVIDER': 'database.provider', + // These will map to the appropriate provider based on DB_PROVIDER + 'DB_HOST': { + path: (provider) => `database.${provider}.host` + }, + 'DB_PORT': { + path: (provider) => `database.${provider}.port` + }, + 'DB_USERNAME': { + path: (provider) => `database.${provider}.username` + }, + 'DB_PASSWORD': { + path: (provider) => `database.${provider}.password` + }, + 'DB_NAME': { + path: (provider) => `database.${provider}.databaseName` + }, + 'DB_USE_SSL': { + path: (provider) => `database.${provider}.useSSL` + }, + 'DB_SSL_CA': { + path: (provider) => `database.${provider}.sslCA` + }, + + // Auth Configuration + 'KC_REALM': 'auth.realm', + 'KC_REALM_KEY': 'auth.realmKey', + 'KC_URL': 'auth.url', + 'KC_SSL_REQ': 'auth.sslRequired', + 'KC_CLIENT': 'auth.client.id', + 'KC_CLIENT_SECRET': 'auth.client.secret', + 'KC_VIEWER_CLIENT': 'auth.viewerClient', + + // Bridge Ports Configuration + 'BRIDGE_PORTS_RANGE': 'bridgePorts.range', + + // System Images Configuration + 'ROUTER_IMAGE_1': 'systemImages.router.1', + 'ROUTER_IMAGE_2': 'systemImages.router.2', + 'DEBUG_IMAGE_1': 'systemImages.debug.1', + 'DEBUG_IMAGE_2': 'systemImages.debug.2', + + // Diagnostics Configuration + 'DIAGNOSTICS_DIRECTORY': 'diagnostics.directory', + + // OpenTelemetry Configuration + 'ENABLE_TELEMETRY': 'otel.enabled', + 'OTEL_SERVICE_NAME': 'otel.serviceName', + 'OTEL_EXPORTER_OTLP_ENDPOINT': 'otel.endpoint', + 'OTEL_EXPORTER_OTLP_PROTOCOL': 'otel.protocol', + 'OTEL_EXPORTER_OTLP_HEADERS': 'otel.headers', + 'OTEL_RESOURCE_ATTRIBUTES': 'otel.resourceAttributes', + 'OTEL_METRICS_EXPORTER': 'otel.metrics.exporter', + 'OTEL_METRICS_INTERVAL': 'otel.metrics.interval', + 'OTEL_LOG_LEVEL': 'otel.logs.level', + 'OTEL_PROPAGATORS': 'otel.propagators', + 'OTEL_TRACES_SAMPLER': 'otel.traces.sampler', + 'OTEL_TRACES_SAMPLER_ARG': 'otel.traces.samplerArg', + 'OTEL_BATCH_SIZE': 'otel.batch.size', + 'OTEL_BATCH_DELAY': 'otel.batch.delay' +} diff --git a/src/config/index.js b/src/config/index.js index 066246992..c6e7da05e 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,39 +13,148 @@ const nconf = require('nconf') const path = require('path') -const constants = require('./constants') +const fs = require('fs') +const yaml = require('js-yaml') class Config { constructor () { - nconf.env({ separator: '_' }) - const environment = nconf.get('NODE:ENV') || 'production' - this.load(environment) + this.envMapping = require('./env-mapping') + this.configPath = process.env.CONFIG_PATH || path.join(__dirname, 'controller.yaml') + this.config = null + this.load() } - get (key, defaultValue) { - let value = nconf.get(key) + load () { + // 1. Load YAML config file + this.loadYamlConfig() + + // 2. Set OTEL environment variables from config + this.setOtelEnvVars() + } + + loadYamlConfig () { + try { + console.log('Loading config from:', this.configPath) + const configContent = fs.readFileSync(this.configPath, 'utf8') + this.config = yaml.load(configContent) + + // Clear any existing configuration + nconf.reset() + + // First set environment variables + nconf.env({ + separator: '_', + parseValues: true, + transform: (obj) => { + // Skip OTEL environment variables as they are handled separately + if (obj.key.startsWith('OTEL_') || obj.key === 'ENABLE_TELEMETRY') { + return null + } + + const mapping = this.envMapping[obj.key] + if (!mapping) { + return null + } + + // Handle database configuration + if (typeof mapping === 'object' && mapping.path) { + const provider = this.get('database.provider', 'sqlite') + return { + key: mapping.path(provider), + value: this.parseEnvValue(obj.value) + } + } + + return { + key: mapping, + value: this.parseEnvValue(obj.value) + } + } + }) + + // Get environment overrides first + const envOverrides = nconf.get() + + // Create a deep copy of the base config + const finalConfig = JSON.parse(JSON.stringify(this.config)) + + // Merge environment overrides into the final config + Object.entries(envOverrides).forEach(([key, value]) => { + if (key.includes('.')) { + const keys = key.split('.') + let current = finalConfig + for (let i = 0; i < keys.length - 1; i++) { + if (!current[keys[i]]) { + current[keys[i]] = {} + } + current = current[keys[i]] + } + current[keys[keys.length - 1]] = value + } else if (!key.includes(':') && key !== 'type') { + finalConfig[key] = value + } + }) - if (value === undefined || value === null) { - value = constants[key] + // Reset nconf and set the final merged config + nconf.reset() + nconf.defaults(finalConfig) + // Log the final merged config + } catch (error) { + console.error(`Error loading config file: ${error.message}`) + throw error } + } - if (value === undefined || value === null) { - value = defaultValue + setOtelEnvVars () { + console.log('Setting OTEL environment variables from config...') + // Only set OTEL env vars if they're not already set + for (const [envVar, configPath] of Object.entries(this.envMapping)) { + if (envVar.startsWith('OTEL_') || envVar === 'ENABLE_TELEMETRY') { + const value = this.get(configPath) + if (value !== undefined && !process.env[envVar]) { + const formattedValue = this.formatValue(value) + process.env[envVar] = formattedValue + } + } } + } + parseEnvValue (value) { + // Handle different types + if (value === 'true') return true + if (value === 'false') return false + if (!isNaN(value) && value !== '') return Number(value) return value } - set (key, value) { - const environment = nconf.get('NODE:ENV') || 'production' + formatValue (value) { + if (typeof value === 'boolean') { + return value.toString() + } + if (Array.isArray(value)) { + return value.join(',') + } + if (typeof value === 'object') { + return Object.entries(value) + .map(([key, val]) => `${key}=${val}`) + .join(',') + } + return value.toString() + } - nconf.stores[environment].set(key, value) - nconf.stores[environment].saveSync() + get (key, defaultValue) { + // Replace dots with colons for nconf compatibility + const nconfKey = key.replace(/\./g, ':') + let value = nconf.get(nconfKey) + return value !== undefined ? value : defaultValue + } + + set (key, value) { + nconf.set(key, value) } - load (environment) { - nconf.file(environment, path.join(__dirname, environment.toLowerCase() + '.json')) - nconf.file('default', path.join(__dirname, 'default.json')) + getAll () { + return nconf.get() } } diff --git a/src/config/keycloak.js b/src/config/keycloak.js new file mode 100644 index 000000000..0f510064a --- /dev/null +++ b/src/config/keycloak.js @@ -0,0 +1,119 @@ +const session = require('express-session') +const Keycloak = require('keycloak-connect') +const config = require('./index') +const logger = require('../logger') + +// Mock Keycloak implementation for development mode +class MockKeycloak { + constructor () { + this.protect = (roles) => { + return async (req, res, next) => { + // In dev mode, we just add mock user info to the request + req.kauth = { + grant: { + access_token: { + content: { + preferred_username: 'dev-user', + realm_access: { + roles: ['SRE', 'Developer', 'Viewer'] + } + } + } + } + } + return next() + } + } + + // Add middleware method to match real Keycloak interface + this.middleware = () => { + return (req, res, next) => { + // In dev mode, we just pass through the middleware + return next() + } + } + } +} + +const keycloakConfig = { + realm: process.env.KC_REALM || config.get('auth.realm'), + 'realm-public-key': process.env.KC_REALM_KEY || config.get('auth.realmKey'), + 'auth-server-url': process.env.KC_URL || config.get('auth.url'), + 'ssl-required': process.env.KC_SSL_REQ || config.get('auth.sslRequired'), + resource: process.env.KC_CLIENT || config.get('auth.client.id'), + 'bearer-only': true, + 'verify-token-audience': true, + credentials: { + secret: process.env.KC_CLIENT_SECRET || config.get('auth.client.secret') + }, + 'use-resource-role-mappings': true, + 'confidential-port': 0 +} + +let keycloak +let memoryStore + +function isAuthConfigured () { + const requiredConfigs = [ + 'auth.realm', + 'auth.realmKey', + 'auth.url', + 'auth.client.id', + 'auth.client.secret' + ] + return requiredConfigs.every(configKey => { + const value = config.get(configKey) + return value !== undefined && value !== null && value !== '' + }) +} + +function initKeycloak () { + if (keycloak) { + return keycloak + } + + const isDevMode = config.get('server.devMode', true) + const hasAuthConfig = isAuthConfigured() + + if (!hasAuthConfig && isDevMode) { + // Initialize mock Keycloak for development + keycloak = new MockKeycloak() + logger.warn('Keycloak initialized in development mode (no auth configuration)') + logger.warn('WARNING: All routes are unprotected in this mode') + } else if (!hasAuthConfig) { + // Throw error in production if auth not configured + const error = new Error('Auth configuration required in production mode') + logger.error('Failed to initialize Keycloak:', error) + throw error + } else { + // Initialize real Keycloak + try { + memoryStore = new session.MemoryStore() + keycloak = new Keycloak({ store: memoryStore }, keycloakConfig) + logger.info('Keycloak initialized successfully with auth configuration') + } catch (error) { + logger.error('Error initializing Keycloak:', error) + throw error + } + } + + return keycloak +} + +function getKeycloak () { + if (keycloak) { + return keycloak + } +} + +function getMemoryStore () { + if (memoryStore) { + return memoryStore + } +} + +module.exports = { + initKeycloak, + getMemoryStore, + getKeycloak +} diff --git a/src/config/production.json b/src/config/production.json deleted file mode 100644 index 725d8abb2..000000000 --- a/src/config/production.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "App": { - "Name": "iofog-controller" - }, - "Viewer": { - "Port": 80 - }, - "Server": { - "Port": 51121, - "DevMode": true - }, - "Email": { - "ActivationEnabled": false - }, - "Service": { - "LogsDirectory": "/var/log/iofog-controller", - "LogsFileSize": 10485760, - "LogsFileCount": 10 - }, - "Settings": { - "UserTokenExpirationIntervalSeconds": 3600, - "FogTokenExpirationIntervalSeconds": 3600, - "FogStatusUpdateIntervalSeconds": 30, - "FogStatusUpdateTolerance": 3 - }, - "PublicPorts": { - "Provider": "default" - }, - "Database": { - "Provider": "sqlite", - "Config": { - "databaseName": "prod_database.sqlite", - "logging": false, - "transactionType": "IMMEDIATE", - "pool": { - "maxactive": 1, - "max": 1, - "min": 0, - "idle": 20000 - } - } - } -} \ No newline at end of file diff --git a/src/config/telemetry.js b/src/config/telemetry.js new file mode 100644 index 000000000..dd969184c --- /dev/null +++ b/src/config/telemetry.js @@ -0,0 +1,78 @@ +const { NodeSDK } = require('@opentelemetry/sdk-node') +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http') +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http') +const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express') +const { + Resource, + envDetectorSync, + hostDetectorSync, + processDetectorSync +} = require('@opentelemetry/resources') +const logger = require('../logger') + +// Workaround for async attributes +function awaitAttributes (detector) { + return { + async detect (config) { + const resource = detector.detect(config) + if (resource.waitForAsyncAttributes) { + await resource.waitForAsyncAttributes() + } + return resource + } + } +} + +// Initialize OpenTelemetry +const sdk = new NodeSDK({ + serviceName: process.env.OTEL_SERVICE_NAME || 'pot-controller', + resource: new Resource({}), + resourceDetectors: [ + awaitAttributes(envDetectorSync), + awaitAttributes(processDetectorSync), + awaitAttributes(hostDetectorSync) + ], + traceExporter: new OTLPTraceExporter({ + url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces', + headers: {} + }), + instrumentations: [ + new HttpInstrumentation(), + new ExpressInstrumentation() + ] +}) + +// Start the SDK +async function startTelemetry () { + const isTelemetryEnabled = process.env.ENABLE_TELEMETRY === 'true' + if (!isTelemetryEnabled) { + logger.info('Telemetry is disabled via ENABLE_TELEMETRY environment variable') + return + } + + try { + await sdk.start() + logger.info('OpenTelemetry initialized successfully') + } catch (error) { + logger.error('Error initializing OpenTelemetry:', error) + process.exit(1) + } +} + +// Handle process termination +process.on('SIGTERM', () => { + if (process.env.ENABLE_TELEMETRY !== 'true') return + + try { + sdk.shutdown() + } catch (error) { + logger.error('Error terminating OpenTelemetry:', error) + } finally { + process.exit(0) + } +}) + +module.exports = { + sdk, + startTelemetry +} diff --git a/src/config/test.json b/src/config/test.json deleted file mode 100644 index d372cd118..000000000 --- a/src/config/test.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "Server": { - "DevMode": true - }, - "Email": { - "ActivationEnabled": false - }, - "Database": { - "Provider": "sqlite", - "Config": { - "databaseName": "test_database.sqlite", - "logging": false, - "transactionType": "IMMEDIATE", - "pool": { - "maxactive": 1, - "max": 1, - "min": 0, - "idle": 20000 - } - } - } -} \ No newline at end of file diff --git a/src/controllers/agent-controller.js b/src/controllers/agent-controller.js index 04e86e09c..404be92ce 100644 --- a/src/controllers/agent-controller.js +++ b/src/controllers/agent-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -36,6 +36,12 @@ const updateAgentConfigEndPoint = async function (req, fog) { return AgentService.updateAgentConfig(updateData, fog) } +const updateAgentGpsEndPoint = async function (req, fog) { + const updateData = req.body + + return AgentService.updateAgentGpsEndPoint(updateData, fog) +} + const getAgentConfigChangesEndPoint = async function (req, fog) { return AgentService.getAgentConfigChanges(fog) } @@ -58,6 +64,10 @@ const getAgentLinkedEdgeResourcesEndpoint = async function (req, fog) { return { edgeResources: await AgentService.getAgentLinkedEdgeResources(fog) } } +const getAgentLinkedVolumeMountsEndpoint = async function (req, fog) { + return { volumeMounts: await AgentService.getAgentLinkedVolumeMounts(fog) } +} + const getAgentMicroserviceEndPoint = async function (req, fog) { const microserviceUuid = req.params.microserviceUuid @@ -110,11 +120,16 @@ const putImageSnapshotEndPoint = async function (req, fog) { return AgentService.putImageSnapshot(req, fog) } +const getControllerCAEndPoint = async function (req, fog) { + return AgentService.getControllerCA(fog) +} + module.exports = { agentProvisionEndPoint: agentProvisionEndPoint, agentDeprovisionEndPoint: AuthDecorator.checkFogToken(agentDeprovisionEndPoint), getAgentConfigEndPoint: AuthDecorator.checkFogToken(getAgentConfigEndPoint), updateAgentConfigEndPoint: AuthDecorator.checkFogToken(updateAgentConfigEndPoint), + updateAgentGpsEndPoint: AuthDecorator.checkFogToken(updateAgentGpsEndPoint), getAgentConfigChangesEndPoint: AuthDecorator.checkFogToken(getAgentConfigChangesEndPoint), updateAgentStatusEndPoint: AuthDecorator.checkFogToken(updateAgentStatusEndPoint), getAgentMicroservicesEndPoint: AuthDecorator.checkFogToken(getAgentMicroservicesEndPoint), @@ -130,5 +145,7 @@ module.exports = { getImageSnapshotEndPoint: AuthDecorator.checkFogToken(getImageSnapshotEndPoint), putImageSnapshotEndPoint: AuthDecorator.checkFogToken(putImageSnapshotEndPoint), resetAgentConfigChangesEndPoint: AuthDecorator.checkFogToken(resetAgentConfigChangesEndPoint), - getAgentLinkedEdgeResourcesEndpoint: AuthDecorator.checkFogToken(getAgentLinkedEdgeResourcesEndpoint) + getAgentLinkedEdgeResourcesEndpoint: AuthDecorator.checkFogToken(getAgentLinkedEdgeResourcesEndpoint), + getAgentLinkedVolumeMountsEndpoint: AuthDecorator.checkFogToken(getAgentLinkedVolumeMountsEndpoint), + getControllerCAEndPoint: AuthDecorator.checkFogToken(getControllerCAEndPoint) } diff --git a/src/controllers/application-controller.js b/src/controllers/application-controller.js index ced617b87..dc3623143 100644 --- a/src/controllers/application-controller.js +++ b/src/controllers/application-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,105 +11,123 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const ApplicationService = require('../services/application-service') const YAMLParserService = require('../services/yaml-parser-service') const errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') const { rvaluesVarSubstition } = require('../helpers/template-helper') -const createApplicationEndPoint = async function (req, user) { +const createApplicationEndPoint = async function (req) { const application = req.body - return ApplicationService.createApplicationEndPoint(application, user, false) + return ApplicationService.createApplicationEndPoint(application, false) } -const createApplicationYAMLEndPoint = async function (req, user) { +const createApplicationYAMLEndPoint = async function (req) { if (!req.file) { throw new errors.ValidationError(ErrorMessages.APPLICATION_FILE_NOT_FOUND) } const fileContent = req.file.buffer.toString() const application = await YAMLParserService.parseAppFile(fileContent) - await rvaluesVarSubstition(application, { self: application }, user) + await rvaluesVarSubstition(application, { self: application }) - return ApplicationService.createApplicationEndPoint(application, user, false) + return ApplicationService.createApplicationEndPoint(application, false) } -const getApplicationsByUserEndPoint = async function (req, user) { - return ApplicationService.getUserApplicationsEndPoint(user, false) +const getApplicationsByUserEndPoint = async function (req) { + return ApplicationService.getUserApplicationsEndPoint(false) } -const getApplicationEndPoint = async function (req, user) { +const getApplicationsBySystemEndPoint = async function (req) { + return ApplicationService.getSystemApplicationsEndPoint(false) +} + +const getApplicationEndPoint = async function (req) { const name = req.params.name - const application = await ApplicationService.getApplicationEndPoint({ name }, user, false) + const application = await ApplicationService.getApplicationEndPoint({ name }, false) + return application +} + +const getSystemApplicationEndPoint = async function (req) { + const name = req.params.name + const application = await ApplicationService.getSystemApplicationEndPoint({ name }, false) return application } -const patchApplicationEndPoint = async function (req, user) { +const patchApplicationEndPoint = async function (req) { const application = req.body const name = req.params.name - return ApplicationService.patchApplicationEndPoint(application, { name }, user, false) + return ApplicationService.patchApplicationEndPoint(application, { name }, false) } -const updateApplicationEndPoint = async function (req, user) { +const updateApplicationEndPoint = async function (req) { const application = req.body const name = req.params.name - return ApplicationService.updateApplicationEndPoint(application, name, user, false) + return ApplicationService.updateApplicationEndPoint(application, name, false) } -const updateApplicationYAMLEndPoint = async function (req, user) { +const updateApplicationYAMLEndPoint = async function (req) { if (!req.file) { throw new errors.ValidationError(ErrorMessages.APPLICATION_FILE_NOT_FOUND) } const name = req.params.name const fileContent = req.file.buffer.toString() const application = await YAMLParserService.parseAppFile(fileContent) - await rvaluesVarSubstition(application, { self: application }, user) + await rvaluesVarSubstition(application, { self: application }) + + return ApplicationService.updateApplicationEndPoint(application, name, false) +} + +const deleteApplicationEndPoint = async function (req) { + const name = req.params.name - return ApplicationService.updateApplicationEndPoint(application, name, user, false) + return ApplicationService.deleteApplicationEndPoint({ name }, false) } -const deleteApplicationEndPoint = async function (req, user) { +const deleteSystemApplicationEndPoint = async function (req) { const name = req.params.name - return ApplicationService.deleteApplicationEndPoint({ name }, user, false) + return ApplicationService.deleteSystemApplicationEndPoint({ name }, false) } // Legacy -const deleteApplicationByIdEndPoint = async function (req, user) { +const deleteApplicationByIdEndPoint = async function (req) { const id = req.params.id - return ApplicationService.deleteApplicationEndPoint({ id }, user, false) + return ApplicationService.deleteApplicationEndPoint({ id }, false) } -const patchApplicationByIdEndPoint = async function (req, user) { +const patchApplicationByIdEndPoint = async function (req) { const application = req.body const id = req.params.id - return ApplicationService.patchApplicationEndPoint(application, { id }, user, false) + return ApplicationService.patchApplicationEndPoint(application, { id }, false) } -const getApplicationByIdEndPoint = async function (req, user) { +const getApplicationByIdEndPoint = async function (req) { const id = req.params.id - const application = await ApplicationService.getApplicationEndPoint({ id }, user, false) + const application = await ApplicationService.getApplicationEndPoint({ id }, false) return application } module.exports = { - createApplicationEndPoint: AuthDecorator.checkAuthToken(createApplicationEndPoint), - createApplicationYAMLEndPoint: AuthDecorator.checkAuthToken(createApplicationYAMLEndPoint), - getApplicationsByUserEndPoint: AuthDecorator.checkAuthToken(getApplicationsByUserEndPoint), - getApplicationEndPoint: AuthDecorator.checkAuthToken(getApplicationEndPoint), - getApplicationByIdEndPoint: AuthDecorator.checkAuthToken(getApplicationByIdEndPoint), - updateApplicationEndPoint: AuthDecorator.checkAuthToken(updateApplicationEndPoint), - updateApplicationYAMLEndPoint: AuthDecorator.checkAuthToken(updateApplicationYAMLEndPoint), - patchApplicationEndPoint: AuthDecorator.checkAuthToken(patchApplicationEndPoint), - patchApplicationByIdEndPoint: AuthDecorator.checkAuthToken(patchApplicationByIdEndPoint), - deleteApplicationEndPoint: AuthDecorator.checkAuthToken(deleteApplicationEndPoint), - deleteApplicationByIdEndPoint: AuthDecorator.checkAuthToken(deleteApplicationByIdEndPoint) + createApplicationEndPoint: (createApplicationEndPoint), + createApplicationYAMLEndPoint: (createApplicationYAMLEndPoint), + getApplicationsByUserEndPoint: (getApplicationsByUserEndPoint), + getApplicationsBySystemEndPoint: (getApplicationsBySystemEndPoint), + getApplicationEndPoint: (getApplicationEndPoint), + getSystemApplicationEndPoint: (getSystemApplicationEndPoint), + getApplicationByIdEndPoint: (getApplicationByIdEndPoint), + updateApplicationEndPoint: (updateApplicationEndPoint), + updateApplicationYAMLEndPoint: (updateApplicationYAMLEndPoint), + patchApplicationEndPoint: (patchApplicationEndPoint), + patchApplicationByIdEndPoint: (patchApplicationByIdEndPoint), + deleteApplicationEndPoint: (deleteApplicationEndPoint), + deleteSystemApplicationEndPoint: (deleteSystemApplicationEndPoint), + deleteApplicationByIdEndPoint: (deleteApplicationByIdEndPoint) } diff --git a/src/controllers/application-template-controller.js b/src/controllers/application-template-controller.js index 1022d1c94..61f281112 100644 --- a/src/controllers/application-template-controller.js +++ b/src/controllers/application-template-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,79 +11,78 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const ApplicationTemplateService = require('../services/application-template-service') const YAMLParserService = require('../services/yaml-parser-service') const errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') const { rvaluesVarSubstition } = require('../helpers/template-helper') -const createApplicationTemplateEndPoint = async function (req, user) { +const createApplicationTemplateEndPoint = async function (req) { const application = req.body - return ApplicationTemplateService.createApplicationTemplateEndPoint(application, user, false) + return ApplicationTemplateService.createApplicationTemplateEndPoint(application, false) } -const createApplicationTemplateYAMLEndPoint = async function (req, user) { +const createApplicationTemplateYAMLEndPoint = async function (req) { if (!req.file) { throw new errors.ValidationError(ErrorMessages.APPLICATION_FILE_NOT_FOUND) } const fileContent = req.file.buffer.toString() const application = await YAMLParserService.parseAppTemplateFile(fileContent) - await rvaluesVarSubstition(application.variables, { self: application.variables }, user) + await rvaluesVarSubstition(application.variables, { self: application.variables }) - return ApplicationTemplateService.createApplicationTemplateEndPoint(application, user, false) + return ApplicationTemplateService.createApplicationTemplateEndPoint(application, false) } -const getApplicationTemplatesByUserEndPoint = async function (req, user) { - return ApplicationTemplateService.getUserApplicationTemplatesEndPoint(user, false) +const getApplicationTemplatesByUserEndPoint = async function (req) { + return ApplicationTemplateService.getUserApplicationTemplatesEndPoint(false) } -const getApplicationTemplateEndPoint = async function (req, user) { +const getApplicationTemplateEndPoint = async function (req) { const name = req.params.name - return ApplicationTemplateService.getApplicationTemplateEndPoint({ name }, user, false) + return ApplicationTemplateService.getApplicationTemplateEndPoint({ name }, false) } -const patchApplicationTemplateEndPoint = async function (req, user) { +const patchApplicationTemplateEndPoint = async function (req) { const application = req.body const name = req.params.name - return ApplicationTemplateService.patchApplicationTemplateEndPoint(application, { name }, user, false) + return ApplicationTemplateService.patchApplicationTemplateEndPoint(application, { name }, false) } -const updateApplicationTemplateEndPoint = async function (req, user) { +const updateApplicationTemplateEndPoint = async function (req) { const application = req.body const name = req.params.name - return ApplicationTemplateService.updateApplicationTemplateEndPoint(application, name, user, false) + return ApplicationTemplateService.updateApplicationTemplateEndPoint(application, name, false) } -const updateApplicationTemplateYAMLEndPoint = async function (req, user) { +const updateApplicationTemplateYAMLEndPoint = async function (req) { if (!req.file) { throw new errors.ValidationError(ErrorMessages.APPLICATION_FILE_NOT_FOUND) } const name = req.params.name const fileContent = req.file.buffer.toString() const application = await YAMLParserService.parseAppTemplateFile(fileContent) - await rvaluesVarSubstition(application.variables, { self: application.variables }, user) + await rvaluesVarSubstition(application.variables, { self: application.variables }) - return ApplicationTemplateService.updateApplicationTemplateEndPoint(application, name, user, false) + return ApplicationTemplateService.updateApplicationTemplateEndPoint(application, name, false) } -const deleteApplicationTemplateEndPoint = async function (req, user) { +const deleteApplicationTemplateEndPoint = async function (req) { const name = req.params.name - return ApplicationTemplateService.deleteApplicationTemplateEndPoint({ name }, user, false) + return ApplicationTemplateService.deleteApplicationTemplateEndPoint({ name }, false) } module.exports = { - createApplicationTemplateEndPoint: AuthDecorator.checkAuthToken(createApplicationTemplateEndPoint), - getApplicationTemplatesByUserEndPoint: AuthDecorator.checkAuthToken(getApplicationTemplatesByUserEndPoint), - getApplicationTemplateEndPoint: AuthDecorator.checkAuthToken(getApplicationTemplateEndPoint), - updateApplicationTemplateEndPoint: AuthDecorator.checkAuthToken(updateApplicationTemplateEndPoint), - updateApplicationTemplateYAMLEndPoint: AuthDecorator.checkAuthToken(updateApplicationTemplateYAMLEndPoint), - patchApplicationTemplateEndPoint: AuthDecorator.checkAuthToken(patchApplicationTemplateEndPoint), - deleteApplicationTemplateEndPoint: AuthDecorator.checkAuthToken(deleteApplicationTemplateEndPoint), - createApplicationTemplateYAMLEndPoint: AuthDecorator.checkAuthToken(createApplicationTemplateYAMLEndPoint) + createApplicationTemplateEndPoint: (createApplicationTemplateEndPoint), + getApplicationTemplatesByUserEndPoint: (getApplicationTemplatesByUserEndPoint), + getApplicationTemplateEndPoint: (getApplicationTemplateEndPoint), + updateApplicationTemplateEndPoint: (updateApplicationTemplateEndPoint), + updateApplicationTemplateYAMLEndPoint: (updateApplicationTemplateYAMLEndPoint), + patchApplicationTemplateEndPoint: (patchApplicationTemplateEndPoint), + deleteApplicationTemplateEndPoint: (deleteApplicationTemplateEndPoint), + createApplicationTemplateYAMLEndPoint: (createApplicationTemplateYAMLEndPoint) } diff --git a/src/controllers/catalog-controller.js b/src/controllers/catalog-controller.js index c92302db6..dcd35cd57 100644 --- a/src/controllers/catalog-controller.js +++ b/src/controllers/catalog-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,32 +12,31 @@ */ const CatalogService = require('../services/catalog-service') -const AuthDecorator = require('./../decorators/authorization-decorator') -const createCatalogItemEndPoint = async function (req, user) { - return CatalogService.createCatalogItemEndPoint(req.body, user) +const createCatalogItemEndPoint = async function (req) { + return CatalogService.createCatalogItemEndPoint(req.body) } -const listCatalogItemsEndPoint = async function (req, user) { - return CatalogService.listCatalogItemsEndPoint(user, false) +const listCatalogItemsEndPoint = async function (req) { + return CatalogService.listCatalogItemsEndPoint(false) } -const listCatalogItemEndPoint = async function (req, user) { - return CatalogService.getCatalogItemEndPoint(req.params.id, user, false) +const listCatalogItemEndPoint = async function (req) { + return CatalogService.getCatalogItemEndPoint(req.params.id, false) } -const deleteCatalogItemEndPoint = async function (req, user) { - await CatalogService.deleteCatalogItemEndPoint(req.params.id, user, false) +const deleteCatalogItemEndPoint = async function (req) { + await CatalogService.deleteCatalogItemEndPoint(req.params.id, false) } -const updateCatalogItemEndPoint = async function (req, user) { - await CatalogService.updateCatalogItemEndPoint(req.params.id, req.body, user, false) +const updateCatalogItemEndPoint = async function (req) { + await CatalogService.updateCatalogItemEndPoint(req.params.id, req.body, false) } module.exports = { - createCatalogItemEndPoint: AuthDecorator.checkAuthToken(createCatalogItemEndPoint), - listCatalogItemsEndPoint: AuthDecorator.checkAuthToken(listCatalogItemsEndPoint), - listCatalogItemEndPoint: AuthDecorator.checkAuthToken(listCatalogItemEndPoint), - deleteCatalogItemEndPoint: AuthDecorator.checkAuthToken(deleteCatalogItemEndPoint), - updateCatalogItemEndPoint: AuthDecorator.checkAuthToken(updateCatalogItemEndPoint) + createCatalogItemEndPoint: (createCatalogItemEndPoint), + listCatalogItemsEndPoint: (listCatalogItemsEndPoint), + listCatalogItemEndPoint: (listCatalogItemEndPoint), + deleteCatalogItemEndPoint: (deleteCatalogItemEndPoint), + updateCatalogItemEndPoint: (updateCatalogItemEndPoint) } diff --git a/src/controllers/certificate-controller.js b/src/controllers/certificate-controller.js new file mode 100644 index 000000000..3271c5c60 --- /dev/null +++ b/src/controllers/certificate-controller.js @@ -0,0 +1,87 @@ +const CertificateService = require('../services/certificate-service') +const YamlParserService = require('../services/yaml-parser-service') + +// CA Management +const createCAEndpoint = async function (req) { + const ca = req.body + return CertificateService.createCAEndpoint(ca) +} + +const getCAEndpoint = async function (req) { + const name = req.params.name + return CertificateService.getCAEndpoint(name) +} + +const listCAEndpoint = async function (req) { + return CertificateService.listCAEndpoint() +} + +const deleteCAEndpoint = async function (req) { + const name = req.params.name + return CertificateService.deleteCAEndpoint(name) +} + +// Certificate Management +const createCertificateEndpoint = async function (req) { + const cert = req.body + return CertificateService.createCertificateEndpoint(cert) +} + +const getCertificateEndpoint = async function (req) { + const name = req.params.name + return CertificateService.getCertificateEndpoint(name) +} + +const listCertificatesEndpoint = async function (req) { + return CertificateService.listCertificatesEndpoint() +} + +const deleteCertificateEndpoint = async function (req) { + const name = req.params.name + return CertificateService.deleteCertificateEndpoint(name) +} + +// Certificate Renewal +const renewCertificateEndpoint = async function (req) { + const name = req.params.name + return CertificateService.renewCertificateEndpoint(name) +} + +// List Expiring Certificates +const listExpiringCertificatesEndpoint = async function (req) { + const days = req.query.days ? parseInt(req.query.days) : 30 + return CertificateService.listExpiringCertificatesEndpoint(days) +} + +// YAML Endpoint +const createCertificateFromYamlEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const certData = await YamlParserService.parseCertificateFile(fileContent) + + if (certData.isCA) { + delete certData.isCA + return CertificateService.createCAEndpoint(certData) + } else { + return CertificateService.createCertificateEndpoint(certData) + } +} + +module.exports = { + // CA endpoints + createCAEndpoint, + getCAEndpoint, + listCAEndpoint, + deleteCAEndpoint, + + // Certificate endpoints + createCertificateEndpoint, + getCertificateEndpoint, + listCertificatesEndpoint, + deleteCertificateEndpoint, + // Certificate renewal endpoints + renewCertificateEndpoint, + listExpiringCertificatesEndpoint, + + // YAML endpoints + createCertificateFromYamlEndpoint +} diff --git a/src/controllers/config-controller.js b/src/controllers/config-controller.js index 7873e6317..2b5efd729 100644 --- a/src/controllers/config-controller.js +++ b/src/controllers/config-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,25 +11,24 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const ConfigService = require('../services/config-service') -const upsertConfigElementEndpoint = async function (req, user) { +const upsertConfigElementEndpoint = async function (req) { const configData = req.body return ConfigService.upsertConfigElement(configData) } -const listConfigEndpoint = async function (user) { +const listConfigEndpoint = async function () { return ConfigService.listConfig() } -const getConfigEndpoint = async function (req, user) { +const getConfigEndpoint = async function (req) { const key = req.params.key return ConfigService.getConfigElement(key) } module.exports = { - upsertConfigElementEndpoint: AuthDecorator.checkAuthToken(upsertConfigElementEndpoint), - listConfigEndpoint: AuthDecorator.checkAuthToken(listConfigEndpoint), - getConfigEndpoint: AuthDecorator.checkAuthToken(getConfigEndpoint) + upsertConfigElementEndpoint: (upsertConfigElementEndpoint), + listConfigEndpoint: (listConfigEndpoint), + getConfigEndpoint: (getConfigEndpoint) } diff --git a/src/controllers/config-map-controller.js b/src/controllers/config-map-controller.js new file mode 100644 index 000000000..ec79358e5 --- /dev/null +++ b/src/controllers/config-map-controller.js @@ -0,0 +1,66 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const ConfigMapService = require('../services/config-map-service') +const YamlParserService = require('../services/yaml-parser-service') + +const createConfigMapEndpoint = async function (req) { + const configMap = req.body + return ConfigMapService.createConfigMapEndpoint(configMap) +} + +const updateConfigMapEndpoint = async function (req) { + const configMap = req.body + const configMapName = req.params.name + return ConfigMapService.updateConfigMapEndpoint(configMapName, configMap) +} + +const getConfigMapEndpoint = async function (req) { + const configMapName = req.params.name + return ConfigMapService.getConfigMapEndpoint(configMapName) +} + +const listConfigMapsEndpoint = async function (req) { + return ConfigMapService.listConfigMapsEndpoint() +} + +const deleteConfigMapEndpoint = async function (req) { + const configMapName = req.params.name + return ConfigMapService.deleteConfigMapEndpoint(configMapName) +} + +const createConfigMapFromYamlEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const configMapData = await YamlParserService.parseConfigMapFile(fileContent) + return ConfigMapService.createConfigMapEndpoint(configMapData) +} + +const updateConfigMapFromYamlEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const configMapName = req.params.name + const configMapData = await YamlParserService.parseConfigMapFile(fileContent, { + isUpdate: true, + configMapName: configMapName + }) + return ConfigMapService.updateConfigMapEndpoint(configMapName, configMapData) +} + +module.exports = { + createConfigMapEndpoint, + updateConfigMapEndpoint, + getConfigMapEndpoint, + listConfigMapsEndpoint, + deleteConfigMapEndpoint, + createConfigMapFromYamlEndpoint, + updateConfigMapFromYamlEndpoint +} diff --git a/src/controllers/controller.js b/src/controllers/controller.js index 8834f830d..abd4650e4 100644 --- a/src/controllers/controller.js +++ b/src/controllers/controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -17,16 +17,11 @@ const statusControllerEndPoint = async function (req) { return ControllerService.statusController(false) } -const emailActivationEndPoint = async function (req) { - return ControllerService.emailActivation(false) -} - const fogTypesEndPoint = async function (req) { return ControllerService.getFogTypes(false) } module.exports = { statusControllerEndPoint: statusControllerEndPoint, - emailActivationEndPoint: emailActivationEndPoint, fogTypesEndPoint: fogTypesEndPoint } diff --git a/src/controllers/diagnostic-controller.js b/src/controllers/diagnostic-controller.js index 0ede7bc28..35d053beb 100644 --- a/src/controllers/diagnostic-controller.js +++ b/src/controllers/diagnostic-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,32 +12,31 @@ */ const DiagnosticService = require('../services/diagnostic-service') -const AuthDecorator = require('./../decorators/authorization-decorator') -const changeMicroserviceStraceStateEndPoint = async function (req, user) { - return DiagnosticService.changeMicroserviceStraceState(req.params.uuid, req.body, user, false) +const changeMicroserviceStraceStateEndPoint = async function (req) { + return DiagnosticService.changeMicroserviceStraceState(req.params.uuid, req.body, false) } -const getMicroserviceStraceDataEndPoint = async function (req, user) { - return DiagnosticService.getMicroserviceStraceData(req.params.uuid, req.query, user, false) +const getMicroserviceStraceDataEndPoint = async function (req) { + return DiagnosticService.getMicroserviceStraceData(req.params.uuid, req.query, false) } -const postMicroserviceStraceDataToFtpEndPoint = async function (req, user) { - return DiagnosticService.postMicroserviceStraceDatatoFtp(req.params.uuid, req.body, user, false) +const postMicroserviceStraceDataToFtpEndPoint = async function (req) { + return DiagnosticService.postMicroserviceStraceDatatoFtp(req.params.uuid, req.body, false) } -const createMicroserviceImageSnapshotEndPoint = async function (req, user) { - return DiagnosticService.postMicroserviceImageSnapshotCreate(req.params.uuid, user, false) +const createMicroserviceImageSnapshotEndPoint = async function (req) { + return DiagnosticService.postMicroserviceImageSnapshotCreate(req.params.uuid, false) } -const getMicroserviceImageSnapshotEndPoint = async function (req, user) { - return DiagnosticService.getMicroserviceImageSnapshot(req.params.uuid, user, false) +const getMicroserviceImageSnapshotEndPoint = async function (req) { + return DiagnosticService.getMicroserviceImageSnapshot(req.params.uuid, false) } module.exports = { - changeMicroserviceStraceStateEndPoint: AuthDecorator.checkAuthToken(changeMicroserviceStraceStateEndPoint), - getMicroserviceStraceDataEndPoint: AuthDecorator.checkAuthToken(getMicroserviceStraceDataEndPoint), - postMicroserviceStraceDataToFtpEndPoint: AuthDecorator.checkAuthToken(postMicroserviceStraceDataToFtpEndPoint), - createMicroserviceImageSnapshotEndPoint: AuthDecorator.checkAuthToken(createMicroserviceImageSnapshotEndPoint), - getMicroserviceImageSnapshotEndPoint: AuthDecorator.checkAuthToken(getMicroserviceImageSnapshotEndPoint) + changeMicroserviceStraceStateEndPoint: (changeMicroserviceStraceStateEndPoint), + getMicroserviceStraceDataEndPoint: (getMicroserviceStraceDataEndPoint), + postMicroserviceStraceDataToFtpEndPoint: (postMicroserviceStraceDataToFtpEndPoint), + createMicroserviceImageSnapshotEndPoint: (createMicroserviceImageSnapshotEndPoint), + getMicroserviceImageSnapshotEndPoint: (getMicroserviceImageSnapshotEndPoint) } diff --git a/src/controllers/edge-resource-controller.js b/src/controllers/edge-resource-controller.js index 79abed87b..68ffb5601 100644 --- a/src/controllers/edge-resource-controller.js +++ b/src/controllers/edge-resource-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,27 +11,26 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const EdgeResourceService = require('../services/edge-resource-service') -const createEdgeResourceEndpoint = async function (req, user) { +const createEdgeResourceEndpoint = async function (req) { const edgeResourceData = req.body - return EdgeResourceService.createEdgeResource(edgeResourceData, user) + return EdgeResourceService.createEdgeResource(edgeResourceData) } -const updateEdgeResourceEndpoint = async function (req, user) { +const updateEdgeResourceEndpoint = async function (req) { const edgeResourceData = req.body const { version, name } = req.params - return EdgeResourceService.updateEdgeResourceEndpoint(edgeResourceData, { name, version }, user) + return EdgeResourceService.updateEdgeResourceEndpoint(edgeResourceData, { name, version }) } -const listEdgeResourcesEndpoint = async function (req, user) { - return { edgeResources: await EdgeResourceService.listEdgeResources(user) } +const listEdgeResourcesEndpoint = async function () { + return { edgeResources: await EdgeResourceService.listEdgeResources() } } -const getEdgeResourceEndpoint = async function (req, user) { +const getEdgeResourceEndpoint = async function (req) { const { version, name } = req.params - const result = await EdgeResourceService.getEdgeResource({ name, version }, user) + const result = await EdgeResourceService.getEdgeResource({ name, version }) if (version) { return result } else { @@ -39,36 +38,36 @@ const getEdgeResourceEndpoint = async function (req, user) { } } -const getEdgeResourceAllVersionsEndpoint = async function (req, user) { +const getEdgeResourceAllVersionsEndpoint = async function (req) { const { name } = req.params - const result = await EdgeResourceService.getEdgeResource({ name }, user) + const result = await EdgeResourceService.getEdgeResource({ name }) return { edgeResources: result } } -const deleteEdgeResourceEndpoint = async function (req, user) { +const deleteEdgeResourceEndpoint = async function (req) { const { version, name } = req.params - return EdgeResourceService.deleteEdgeResource({ name, version }, user) + return EdgeResourceService.deleteEdgeResource({ name, version }) } -const linkEdgeResourceEndpoint = async function (req, user) { +const linkEdgeResourceEndpoint = async function (req) { const { name, version } = req.params const { uuid } = req.body - return EdgeResourceService.linkEdgeResource({ name, version }, uuid, user) + return EdgeResourceService.linkEdgeResource({ name, version }, uuid) } -const unlinkEdgeResourceEndpoint = async function (req, user) { +const unlinkEdgeResourceEndpoint = async function (req) { const { name, version } = req.params const { uuid } = req.body - return EdgeResourceService.unlinkEdgeResource({ name, version }, uuid, user) + return EdgeResourceService.unlinkEdgeResource({ name, version }, uuid) } module.exports = { - createEdgeResourceEndpoint: AuthDecorator.checkAuthToken(createEdgeResourceEndpoint), - updateEdgeResourceEndpoint: AuthDecorator.checkAuthToken(updateEdgeResourceEndpoint), - listEdgeResourcesEndpoint: AuthDecorator.checkAuthToken(listEdgeResourcesEndpoint), - getEdgeResourceEndpoint: AuthDecorator.checkAuthToken(getEdgeResourceEndpoint), - deleteEdgeResourceEndpoint: AuthDecorator.checkAuthToken(deleteEdgeResourceEndpoint), - linkEdgeResourceEndpoint: AuthDecorator.checkAuthToken(linkEdgeResourceEndpoint), - unlinkEdgeResourceEndpoint: AuthDecorator.checkAuthToken(unlinkEdgeResourceEndpoint), - getEdgeResourceAllVersionsEndpoint: AuthDecorator.checkAuthToken(getEdgeResourceAllVersionsEndpoint) + createEdgeResourceEndpoint: (createEdgeResourceEndpoint), + updateEdgeResourceEndpoint: (updateEdgeResourceEndpoint), + listEdgeResourcesEndpoint: (listEdgeResourcesEndpoint), + getEdgeResourceEndpoint: (getEdgeResourceEndpoint), + deleteEdgeResourceEndpoint: (deleteEdgeResourceEndpoint), + linkEdgeResourceEndpoint: (linkEdgeResourceEndpoint), + unlinkEdgeResourceEndpoint: (unlinkEdgeResourceEndpoint), + getEdgeResourceAllVersionsEndpoint: (getEdgeResourceAllVersionsEndpoint) } diff --git a/src/controllers/event-controller.js b/src/controllers/event-controller.js new file mode 100644 index 000000000..62f491073 --- /dev/null +++ b/src/controllers/event-controller.js @@ -0,0 +1,37 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const EventService = require('../services/event-service') + +/** + * List events with query filters and pagination + * @param {object} req - Express request object + * @returns {Promise} Events list with pagination info + */ +async function listEventsEndpoint (req) { + return EventService.listEvents({ query: req.query }, { req }) +} + +/** + * Delete old events manually + * @param {object} req - Express request object + * @returns {Promise} Deletion result + */ +async function deleteOldEventsEndpoint (req) { + return EventService.deleteEvents({ body: req.body }, { req }) +} + +module.exports = { + listEventsEndpoint, + deleteOldEventsEndpoint +} diff --git a/src/controllers/iofog-controller.js b/src/controllers/iofog-controller.js index d9db42345..a89950f29 100644 --- a/src/controllers/iofog-controller.js +++ b/src/controllers/iofog-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,99 +11,118 @@ * */ -const AuthDecorator = require('../decorators/authorization-decorator') const FogService = require('../services/iofog-service') const qs = require('qs') -async function createFogEndPoint (req, user) { +async function createFogEndPoint (req) { const newFog = req.body - return FogService.createFogEndPoint(newFog, user, false) + return FogService.createFogEndPoint(newFog, false) } -async function updateFogEndPoint (req, user) { +async function updateFogEndPoint (req) { const updateFog = req.body updateFog.uuid = req.params.uuid - return FogService.updateFogEndPoint(updateFog, user, false) + return FogService.updateFogEndPoint(updateFog, false) } -async function deleteFogEndPoint (req, user) { +async function deleteFogEndPoint (req) { const deleteFog = { uuid: req.params.uuid } - return FogService.deleteFogEndPoint(deleteFog, user, false) + return FogService.deleteFogEndPoint(deleteFog, false) } -async function getFogEndPoint (req, user) { +async function getFogEndPoint (req) { const getFog = { uuid: req.params.uuid } - return FogService.getFogEndPoint(getFog, user, false) + return FogService.getFogEndPoint(getFog, false) } -async function getFogListEndPoint (req, user) { - const isSystem = req.query && req.query.system ? req.query.system === 'true' : false +async function getFogListEndPoint (req) { + // const isSystem = req.query && req.query.system ? req.query.system === 'true' : false const query = qs.parse(req.query) - return FogService.getFogListEndPoint(query.filters, user, false, isSystem) + // return FogService.getFogListEndPoint(query.filters, false, isSystem) + return FogService.getFogListEndPoint(query.filters, false) } -async function generateProvisionKeyEndPoint (req, user) { +async function generateProvisionKeyEndPoint (req) { const fog = { uuid: req.params.uuid } - return FogService.generateProvisioningKeyEndPoint(fog, user, false) + return FogService.generateProvisioningKeyEndPoint(fog, false) } -async function setFogVersionCommandEndPoint (req, user) { +async function setFogVersionCommandEndPoint (req) { const fogVersionCommand = { uuid: req.params.uuid, versionCommand: req.params.versionCommand } - return FogService.setFogVersionCommandEndPoint(fogVersionCommand, user, false) + return FogService.setFogVersionCommandEndPoint(fogVersionCommand, false) } -async function setFogRebootCommandEndPoint (req, user) { +async function setFogRebootCommandEndPoint (req) { const fog = { uuid: req.params.uuid } - return FogService.setFogRebootCommandEndPoint(fog, user, false) + return FogService.setFogRebootCommandEndPoint(fog, false) } -async function getHalHardwareInfoEndPoint (req, user) { +async function getHalHardwareInfoEndPoint (req) { const uuidObj = { uuid: req.params.uuid } - return FogService.getHalHardwareInfoEndPoint(uuidObj, user, false) + return FogService.getHalHardwareInfoEndPoint(uuidObj, false) } -async function getHalUsbInfoEndPoint (req, user) { +async function getHalUsbInfoEndPoint (req) { const uuidObj = { uuid: req.params.uuid } - return FogService.getHalUsbInfoEndPoint(uuidObj, user, false) + return FogService.getHalUsbInfoEndPoint(uuidObj, false) } -async function setFogPruneCommandEndPoint (req, user) { +async function setFogPruneCommandEndPoint (req) { const fog = { uuid: req.params.uuid } - return FogService.setFogPruneCommandEndPoint(fog, user, false) + return FogService.setFogPruneCommandEndPoint(fog, false) +} + +async function enableNodeExecEndPoint (req) { + const execData = { + uuid: req.params.uuid, + image: req.body.image + } + + return FogService.enableNodeExecEndPoint(execData, false) +} + +async function disableNodeExecEndPoint (req) { + const fogData = { + uuid: req.params.uuid + } + + return FogService.disableNodeExecEndPoint(fogData, false) } module.exports = { - createFogEndPoint: AuthDecorator.checkAuthToken(createFogEndPoint), - updateFogEndPoint: AuthDecorator.checkAuthToken(updateFogEndPoint), - deleteFogEndPoint: AuthDecorator.checkAuthToken(deleteFogEndPoint), - getFogEndPoint: AuthDecorator.checkAuthToken(getFogEndPoint), - getFogListEndPoint: AuthDecorator.checkAuthToken(getFogListEndPoint), - generateProvisioningKeyEndPoint: AuthDecorator.checkAuthToken(generateProvisionKeyEndPoint), - setFogVersionCommandEndPoint: AuthDecorator.checkAuthToken(setFogVersionCommandEndPoint), - setFogRebootCommandEndPoint: AuthDecorator.checkAuthToken(setFogRebootCommandEndPoint), - getHalHardwareInfoEndPoint: AuthDecorator.checkAuthToken(getHalHardwareInfoEndPoint), - getHalUsbInfoEndPoint: AuthDecorator.checkAuthToken(getHalUsbInfoEndPoint), - setFogPruneCommandEndPoint: AuthDecorator.checkAuthToken(setFogPruneCommandEndPoint) + createFogEndPoint: (createFogEndPoint), + updateFogEndPoint: (updateFogEndPoint), + deleteFogEndPoint: (deleteFogEndPoint), + getFogEndPoint: (getFogEndPoint), + getFogListEndPoint: (getFogListEndPoint), + generateProvisioningKeyEndPoint: (generateProvisionKeyEndPoint), + setFogVersionCommandEndPoint: (setFogVersionCommandEndPoint), + setFogRebootCommandEndPoint: (setFogRebootCommandEndPoint), + getHalHardwareInfoEndPoint: (getHalHardwareInfoEndPoint), + getHalUsbInfoEndPoint: (getHalUsbInfoEndPoint), + setFogPruneCommandEndPoint: (setFogPruneCommandEndPoint), + enableNodeExecEndPoint: (enableNodeExecEndPoint), + disableNodeExecEndPoint: (disableNodeExecEndPoint) } diff --git a/src/controllers/kubelet-controller.js b/src/controllers/kubelet-controller.js deleted file mode 100644 index c1fb68622..000000000 --- a/src/controllers/kubelet-controller.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const KubeletService = require('../services/kubelet-service') -const AuthDecorator = require('../decorators/authorization-decorator') - -const kubeletCreatePodEndPoint = async function (req, user) { - const createPodData = req.body - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletCreatePod(createPodData, fogNodeUuid, user) -} - -const kubeletUpdatePodEndPoint = async function (req, user) { - const uploadPodData = req.body - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletUpdatePod(uploadPodData, fogNodeUuid, user) -} - -const kubeletDeletePodEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - const podData = req.body - - return KubeletService.kubeletDeletePod(podData, fogNodeUuid, user) -} - -const kubeletGetPodEndPoint = async function (req, user) { - const namespace = req.query.namespace - const name = req.query.name - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetPod(namespace, name, fogNodeUuid, user) -} - -const kubeletGetContainerLogsEndPoint = async function (req, user) { - const namespace = req.query.namespace - const podName = req.query.podName - const containerName = req.query.containerName - const tail = req.query.tail - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetContainerLogs(namespace, podName, containerName, tail, fogNodeUuid) -} - -const kubeletGetPodStatusEndPoint = async function (req, user) { - const namespace = req.query.namespace - const name = req.query.name - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetPodStatus(namespace, name, fogNodeUuid, user) -} - -const kubeletGetPodsEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetPods(fogNodeUuid, user) -} - -const kubeletGetCapacityEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetCapacity(fogNodeUuid, user) -} - -const kubeletGetAllocatableEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetAllocatable(fogNodeUuid, user) -} - -const kubeletGetNodeConditionsEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetNodeConditions(fogNodeUuid, user) -} - -const kubeletGetNodeAddressesEndPoint = async function (req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetNodeAddresses(fogNodeUuid, user) -} - -const kubeletGetVkTokenEndPoint = async function (req, user) { - const userId = user.id - - return KubeletService.kubeletGetVkToken(userId) -} - -const kubeletGetSchedulerTokenEndPoint = async function (req, user) { - const userId = user.id - - return KubeletService.kubeletGetSchedulerToken(userId) -} - -module.exports = { - kubeletCreatePodEndPoint: AuthDecorator.checkAuthToken(kubeletCreatePodEndPoint), - kubeletUpdatePodEndPoint: AuthDecorator.checkAuthToken(kubeletUpdatePodEndPoint), - kubeletDeletePodEndPoint: AuthDecorator.checkAuthToken(kubeletDeletePodEndPoint), - kubeletGetPodEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodEndPoint), - kubeletGetContainerLogsEndPoint: AuthDecorator.checkAuthToken(kubeletGetContainerLogsEndPoint), - kubeletGetPodStatusEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodStatusEndPoint), - kubeletGetPodsEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodsEndPoint), - kubeletGetCapacityEndPoint: AuthDecorator.checkAuthToken(kubeletGetCapacityEndPoint), - kubeletGetAllocatableEndPoint: AuthDecorator.checkAuthToken(kubeletGetAllocatableEndPoint), - kubeletGetNodeConditionsEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeConditionsEndPoint), - kubeletGetNodeAddressesEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeAddressesEndPoint), - kubeletGetVkTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetVkTokenEndPoint), - kubeletGetSchedulerTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetSchedulerTokenEndPoint) -} diff --git a/src/controllers/microservices-controller.js b/src/controllers/microservices-controller.js index 5590de72d..7fa9b677c 100644 --- a/src/controllers/microservices-controller.js +++ b/src/controllers/microservices-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,130 +11,284 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const MicroservicesService = require('../services/microservices-service') const YAMLParserService = require('../services/yaml-parser-service') const { rvaluesVarSubstition } = require('../helpers/template-helper') -const createMicroserviceOnFogEndPoint = async function (req, user) { +const createMicroserviceOnFogEndPoint = async function (req) { const microservice = req.body - return MicroservicesService.createMicroserviceEndPoint(microservice, user, false) + return MicroservicesService.createMicroserviceEndPoint(microservice, false) } -const createMicroserviceYAMLEndPoint = async function (req, user) { +const createMicroserviceYAMLEndPoint = async function (req) { const fileContent = req.file.buffer.toString() const microservice = await YAMLParserService.parseMicroserviceFile(fileContent) - await rvaluesVarSubstition(microservice, { self: microservice }, user) - return MicroservicesService.createMicroserviceEndPoint(microservice, user, false) + await rvaluesVarSubstition(microservice, { self: microservice }) + return MicroservicesService.createMicroserviceEndPoint(microservice, false) } -const getMicroserviceEndPoint = async function (req, user) { +const getMicroserviceEndPoint = async function (req) { const microserviceUuid = req.params.uuid - return MicroservicesService.getMicroserviceEndPoint(microserviceUuid, user, false) + return MicroservicesService.getMicroserviceEndPoint(microserviceUuid, false) } -const updateMicroserviceEndPoint = async function (req, user) { +const getSystemMicroserviceEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.getSystemMicroserviceEndPoint(microserviceUuid, false) +} + +const listMicroserviceByPubTagEndPoint = async function (req) { + const pubTag = req.params.tag + return MicroservicesService.listMicroserviceByPubTagEndPoint(pubTag) +} + +const listMicroserviceBySubTagEndPoint = async function (req) { + const subTag = req.params.tag + return MicroservicesService.listMicroserviceBySubTagEndPoint(subTag) +} + +const updateMicroserviceEndPoint = async function (req) { + const microservice = req.body + const microserviceUuid = req.params.uuid + return MicroservicesService.updateMicroserviceEndPoint(microserviceUuid, microservice, false) +} + +const updateSystemMicroserviceEndPoint = async function (req) { const microservice = req.body const microserviceUuid = req.params.uuid - return MicroservicesService.updateMicroserviceEndPoint(microserviceUuid, microservice, user, false) + return MicroservicesService.updateSystemMicroserviceEndPoint(microserviceUuid, microservice, false) } -const updateMicroserviceYAMLEndPoint = async function (req, user) { +const rebuildMicroserviceEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.rebuildMicroserviceEndPoint(microserviceUuid, false) +} + +const rebuildSystemMicroserviceEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.rebuildSystemMicroserviceEndPoint(microserviceUuid, false) +} + +const updateMicroserviceYAMLEndPoint = async function (req) { const microserviceUuid = req.params.uuid const fileContent = req.file.buffer.toString() const microservice = await YAMLParserService.parseMicroserviceFile(fileContent) - await rvaluesVarSubstition(microservice, { self: microservice }, user) - return MicroservicesService.updateMicroserviceEndPoint(microserviceUuid, microservice, user, false) + await rvaluesVarSubstition(microservice, { self: microservice }) + return MicroservicesService.updateMicroserviceEndPoint(microserviceUuid, microservice, false) } -const deleteMicroserviceEndPoint = async function (req, user) { +const updateSystemMicroserviceYAMLEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + const fileContent = req.file.buffer.toString() + const microservice = await YAMLParserService.parseMicroserviceFile(fileContent) + await rvaluesVarSubstition(microservice, { self: microservice }) + return MicroservicesService.updateSystemMicroserviceEndPoint(microserviceUuid, microservice, false) +} + +const updateMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + const config = req.body + return MicroservicesService.updateMicroserviceConfigEndPoint(microserviceUuid, config, false) +} + +const getMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.getMicroserviceConfigEndPoint(microserviceUuid, false) +} + +const deleteMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.deleteMicroserviceConfigEndPoint(microserviceUuid, false) +} + +const updateSystemMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + const config = req.body + return MicroservicesService.updateSystemMicroserviceConfigEndPoint(microserviceUuid, config, false) +} + +const getSystemMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.getSystemMicroserviceConfigEndPoint(microserviceUuid, false) +} + +const deleteSystemMicroserviceConfigEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + return MicroservicesService.deleteSystemMicroserviceConfigEndPoint(microserviceUuid, false) +} + +const deleteMicroserviceEndPoint = async function (req) { const microserviceUuid = req.params.uuid const microserviceData = req.body || {} - return MicroservicesService.deleteMicroserviceEndPoint(microserviceUuid, microserviceData, user, false) + return MicroservicesService.deleteMicroserviceEndPoint(microserviceUuid, microserviceData, false) } -const getMicroservicesByApplicationEndPoint = async function (req, user) { +const getMicroservicesByApplicationEndPoint = async function (req) { // API Retro compatibility const flowId = req.query.flowId const applicationName = req.query.application - return MicroservicesService.listMicroservicesEndPoint({ applicationName, flowId }, user, false) + return MicroservicesService.listMicroservicesEndPoint({ applicationName, flowId }, false) } -const createMicroserviceRouteEndPoint = async function (req, user) { +const getSystemMicroservicesByApplicationEndPoint = async function (req) { + // API Retro compatibility + const flowId = req.query.flowId + + const applicationName = req.query.application + return MicroservicesService.listSystemMicroservicesEndPoint({ applicationName, flowId }, false) +} + +const createMicroserviceRouteEndPoint = async function (req) { const sourceUuid = req.params.uuid const destUuid = req.params.receiverUuid - return MicroservicesService.createRouteEndPoint(sourceUuid, destUuid, user, false) + return MicroservicesService.createRouteEndPoint(sourceUuid, destUuid, false) } -const deleteMicroserviceRouteEndPoint = async function (req, user) { +const deleteMicroserviceRouteEndPoint = async function (req) { const sourceUuid = req.params.uuid const destUuid = req.params.receiverUuid - return MicroservicesService.deleteRouteEndPoint(sourceUuid, destUuid, user, false) + return MicroservicesService.deleteRouteEndPoint(sourceUuid, destUuid, false) } -const createMicroservicePortMappingEndPoint = async function (req, user) { +const createMicroservicePortMappingEndPoint = async function (req) { const uuid = req.params.uuid const portMappingData = req.body - return MicroservicesService.createPortMappingEndPoint(uuid, portMappingData, user, false) + return MicroservicesService.createPortMappingEndPoint(uuid, portMappingData, false) +} + +const createSystemMicroservicePortMappingEndPoint = async function (req) { + const uuid = req.params.uuid + const portMappingData = req.body + return MicroservicesService.createSystemPortMappingEndPoint(uuid, portMappingData, false) +} + +const deleteMicroservicePortMappingEndPoint = async function (req) { + const uuid = req.params.uuid + const internalPort = req.params.internalPort + return MicroservicesService.deletePortMappingEndPoint(uuid, internalPort, false) } -const deleteMicroservicePortMappingEndPoint = async function (req, user) { +const deleteSystemMicroservicePortMappingEndPoint = async function (req) { const uuid = req.params.uuid const internalPort = req.params.internalPort - return MicroservicesService.deletePortMappingEndPoint(uuid, internalPort, user, false) + return MicroservicesService.deleteSystemPortMappingEndPoint(uuid, internalPort, false) } -const listMicroservicePortMappingsEndPoint = async function (req, user) { +const listMicroservicePortMappingsEndPoint = async function (req) { const uuid = req.params.uuid - const ports = await MicroservicesService.listMicroservicePortMappingsEndPoint(uuid, user, false) + const ports = await MicroservicesService.listMicroservicePortMappingsEndPoint(uuid, false) return { ports: ports } } -const createMicroserviceVolumeMappingEndPoint = async function (req, user) { +const createMicroserviceVolumeMappingEndPoint = async function (req) { + const microserviceUuid = req.params.uuid + const volumeMappingData = req.body + const volumeMapping = await MicroservicesService.createVolumeMappingEndPoint(microserviceUuid, volumeMappingData, false) + return { + id: volumeMapping.id + } +} + +const createSystemMicroserviceVolumeMappingEndPoint = async function (req) { const microserviceUuid = req.params.uuid const volumeMappingData = req.body - const volumeMapping = await MicroservicesService.createVolumeMappingEndPoint(microserviceUuid, volumeMappingData, user, false) + const volumeMapping = await MicroservicesService.createSystemVolumeMappingEndPoint(microserviceUuid, volumeMappingData, false) return { id: volumeMapping.id } } -const listMicroserviceVolumeMappingsEndPoint = async function (req, user) { +const listMicroserviceVolumeMappingsEndPoint = async function (req) { const uuid = req.params.uuid - const volumeMappings = await MicroservicesService.listVolumeMappingsEndPoint(uuid, user, false) + const volumeMappings = await MicroservicesService.listVolumeMappingsEndPoint(uuid, false) return { volumeMappings: volumeMappings } } -const deleteMicroserviceVolumeMappingEndPoint = async function (req, user) { +const deleteMicroserviceVolumeMappingEndPoint = async function (req) { const uuid = req.params.uuid const id = req.params.id - return MicroservicesService.deleteVolumeMappingEndPoint(uuid, id, user, false) + return MicroservicesService.deleteVolumeMappingEndPoint(uuid, id, false) } -const listAllPublicPortsEndPoint = async function (req, user) { - return MicroservicesService.listAllPublicPortsEndPoint(user) +const deleteSystemMicroserviceVolumeMappingEndPoint = async function (req) { + const uuid = req.params.uuid + const id = req.params.id + return MicroservicesService.deleteSystemVolumeMappingEndPoint(uuid, id, false) +} + +const createMicroserviceExecEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.createExecEndPoint(uuid, false) +} + +const deleteMicroserviceExecEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.deleteExecEndPoint(uuid, false) +} + +const createSystemMicroserviceExecEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.createSystemExecEndPoint(uuid, false) +} + +const deleteSystemMicroserviceExecEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.deleteSystemExecEndPoint(uuid, false) +} + +const startMicroserviceEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.startMicroserviceEndPoint(uuid, false) +} + +const stopMicroserviceEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.stopMicroserviceEndPoint(uuid, false) } module.exports = { - createMicroserviceOnFogEndPoint: AuthDecorator.checkAuthToken(createMicroserviceOnFogEndPoint), - getMicroserviceEndPoint: AuthDecorator.checkAuthToken(getMicroserviceEndPoint), - updateMicroserviceEndPoint: AuthDecorator.checkAuthToken(updateMicroserviceEndPoint), - deleteMicroserviceEndPoint: AuthDecorator.checkAuthToken(deleteMicroserviceEndPoint), - getMicroservicesByApplicationEndPoint: AuthDecorator.checkAuthToken(getMicroservicesByApplicationEndPoint), - createMicroserviceRouteEndPoint: AuthDecorator.checkAuthToken(createMicroserviceRouteEndPoint), - deleteMicroserviceRouteEndPoint: AuthDecorator.checkAuthToken(deleteMicroserviceRouteEndPoint), - createMicroservicePortMappingEndPoint: AuthDecorator.checkAuthToken(createMicroservicePortMappingEndPoint), - deleteMicroservicePortMappingEndPoint: AuthDecorator.checkAuthToken(deleteMicroservicePortMappingEndPoint), - getMicroservicePortMappingListEndPoint: AuthDecorator.checkAuthToken(listMicroservicePortMappingsEndPoint), - createMicroserviceVolumeMappingEndPoint: AuthDecorator.checkAuthToken(createMicroserviceVolumeMappingEndPoint), - listMicroserviceVolumeMappingsEndPoint: AuthDecorator.checkAuthToken(listMicroserviceVolumeMappingsEndPoint), - deleteMicroserviceVolumeMappingEndPoint: AuthDecorator.checkAuthToken(deleteMicroserviceVolumeMappingEndPoint), - listAllPublicPortsEndPoint: AuthDecorator.checkAuthToken(listAllPublicPortsEndPoint), - createMicroserviceYAMLEndPoint: AuthDecorator.checkAuthToken(createMicroserviceYAMLEndPoint), - updateMicroserviceYAMLEndPoint: AuthDecorator.checkAuthToken(updateMicroserviceYAMLEndPoint) + createMicroserviceOnFogEndPoint: (createMicroserviceOnFogEndPoint), + getMicroserviceEndPoint: (getMicroserviceEndPoint), + getSystemMicroserviceEndPoint: (getSystemMicroserviceEndPoint), + listMicroserviceByPubTagEndPoint: (listMicroserviceByPubTagEndPoint), + listMicroserviceBySubTagEndPoint: (listMicroserviceBySubTagEndPoint), + updateMicroserviceEndPoint: (updateMicroserviceEndPoint), + updateSystemMicroserviceEndPoint: (updateSystemMicroserviceEndPoint), + rebuildMicroserviceEndPoint: (rebuildMicroserviceEndPoint), + rebuildSystemMicroserviceEndPoint: (rebuildSystemMicroserviceEndPoint), + deleteMicroserviceEndPoint: (deleteMicroserviceEndPoint), + getMicroservicesByApplicationEndPoint: (getMicroservicesByApplicationEndPoint), + getSystemMicroservicesByApplicationEndPoint: (getSystemMicroservicesByApplicationEndPoint), + createMicroserviceRouteEndPoint: (createMicroserviceRouteEndPoint), + deleteMicroserviceRouteEndPoint: (deleteMicroserviceRouteEndPoint), + createMicroservicePortMappingEndPoint: (createMicroservicePortMappingEndPoint), + createSystemMicroservicePortMappingEndPoint: (createSystemMicroservicePortMappingEndPoint), + deleteMicroservicePortMappingEndPoint: (deleteMicroservicePortMappingEndPoint), + deleteSystemMicroservicePortMappingEndPoint: (deleteSystemMicroservicePortMappingEndPoint), + getMicroservicePortMappingListEndPoint: (listMicroservicePortMappingsEndPoint), + createMicroserviceVolumeMappingEndPoint: (createMicroserviceVolumeMappingEndPoint), + createSystemMicroserviceVolumeMappingEndPoint: (createSystemMicroserviceVolumeMappingEndPoint), + listMicroserviceVolumeMappingsEndPoint: (listMicroserviceVolumeMappingsEndPoint), + deleteMicroserviceVolumeMappingEndPoint: (deleteMicroserviceVolumeMappingEndPoint), + deleteSystemMicroserviceVolumeMappingEndPoint: (deleteSystemMicroserviceVolumeMappingEndPoint), + createMicroserviceYAMLEndPoint: (createMicroserviceYAMLEndPoint), + updateMicroserviceYAMLEndPoint: (updateMicroserviceYAMLEndPoint), + updateSystemMicroserviceYAMLEndPoint: (updateSystemMicroserviceYAMLEndPoint), + updateMicroserviceConfigEndPoint: (updateMicroserviceConfigEndPoint), + getMicroserviceConfigEndPoint: (getMicroserviceConfigEndPoint), + updateSystemMicroserviceConfigEndPoint: (updateSystemMicroserviceConfigEndPoint), + getSystemMicroserviceConfigEndPoint: (getSystemMicroserviceConfigEndPoint), + deleteMicroserviceConfigEndPoint: (deleteMicroserviceConfigEndPoint), + deleteSystemMicroserviceConfigEndPoint: (deleteSystemMicroserviceConfigEndPoint), + createMicroserviceExecEndPoint: (createMicroserviceExecEndPoint), + deleteMicroserviceExecEndPoint: (deleteMicroserviceExecEndPoint), + createSystemMicroserviceExecEndPoint: (createSystemMicroserviceExecEndPoint), + deleteSystemMicroserviceExecEndPoint: (deleteSystemMicroserviceExecEndPoint), + startMicroserviceEndPoint: (startMicroserviceEndPoint), + stopMicroserviceEndPoint: (stopMicroserviceEndPoint) } diff --git a/src/controllers/registry-controller.js b/src/controllers/registry-controller.js index 4ba7af91f..ab633cb0e 100644 --- a/src/controllers/registry-controller.js +++ b/src/controllers/registry-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,34 +11,33 @@ * */ -const AuthDecorator = require('../decorators/authorization-decorator') const RegistryService = require('../services/registry-service') -const createRegistryEndPoint = async function (req, user) { +const createRegistryEndPoint = async function (req) { const registry = req.body - return RegistryService.createRegistry(registry, user) + return RegistryService.createRegistry(registry) } -const getRegistriesEndPoint = async function (req, user) { - return RegistryService.findRegistries(user, false) +const getRegistriesEndPoint = async function (req) { + return RegistryService.findRegistries(false) } -const deleteRegistryEndPoint = async function (req, user) { +const deleteRegistryEndPoint = async function (req) { const deleteRegistry = { id: parseInt(req.params.id) } - return RegistryService.deleteRegistry(deleteRegistry, user, false) + return RegistryService.deleteRegistry(deleteRegistry, false) } -const updateRegistryEndPoint = async function (req, user) { +const updateRegistryEndPoint = async function (req) { const registry = req.body const registryId = req.params.id - return RegistryService.updateRegistry(registry, registryId, user, false) + return RegistryService.updateRegistry(registry, registryId, false) } module.exports = { - createRegistryEndPoint: AuthDecorator.checkAuthToken(createRegistryEndPoint), - getRegistriesEndPoint: AuthDecorator.checkAuthToken(getRegistriesEndPoint), - deleteRegistryEndPoint: AuthDecorator.checkAuthToken(deleteRegistryEndPoint), - updateRegistryEndPoint: AuthDecorator.checkAuthToken(updateRegistryEndPoint) + createRegistryEndPoint: (createRegistryEndPoint), + getRegistriesEndPoint: (getRegistriesEndPoint), + deleteRegistryEndPoint: (deleteRegistryEndPoint), + updateRegistryEndPoint: (updateRegistryEndPoint) } diff --git a/src/controllers/router-controller.js b/src/controllers/router-controller.js index 8e8776c28..7c3734145 100644 --- a/src/controllers/router-controller.js +++ b/src/controllers/router-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,7 +11,6 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const RouterService = require('../services/router-service') const upsertDefaultRouter = async function (req) { @@ -24,6 +23,6 @@ const getRouterEndPoint = async function () { } module.exports = { - upsertDefaultRouter: AuthDecorator.checkAuthToken(upsertDefaultRouter), - getRouterEndPoint: AuthDecorator.checkAuthToken(getRouterEndPoint) + upsertDefaultRouter: (upsertDefaultRouter), + getRouterEndPoint: (getRouterEndPoint) } diff --git a/src/controllers/routing-controller.js b/src/controllers/routing-controller.js index 53316caaa..8d6df8bc4 100644 --- a/src/controllers/routing-controller.js +++ b/src/controllers/routing-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,41 +11,40 @@ * */ -const AuthDecorator = require('./../decorators/authorization-decorator') const RoutingService = require('../services/routing-service') -const createRoutingEndpoint = async function (req, user) { +const createRoutingEndpoint = async function (req) { const routerData = req.body - return RoutingService.createRouting(routerData, user, false) + return RoutingService.createRouting(routerData, false) } -const getRoutingsEndPoint = async function (req, user) { - return RoutingService.getRoutings(user, false) +const getRoutingsEndPoint = async function (req) { + return RoutingService.getRoutings(false) } -const getRoutingEndPoint = async function (req, user) { +const getRoutingEndPoint = async function (req) { const routeName = req.params.name const appName = req.params.appName - return RoutingService.getRouting(appName, routeName, user, false) + return RoutingService.getRouting(appName, routeName, false) } -const updateRoutingEndpoint = async function (req, user) { +const updateRoutingEndpoint = async function (req) { const routeName = req.params.name const appName = req.params.appName const routeData = req.body - return RoutingService.updateRouting(appName, routeName, routeData, user, false) + return RoutingService.updateRouting(appName, routeName, routeData, false) } -const deleteRoutingEndpoint = async function (req, user) { +const deleteRoutingEndpoint = async function (req) { const routeName = req.params.name const appName = req.params.appName - return RoutingService.deleteRouting(appName, routeName, user, false) + return RoutingService.deleteRouting(appName, routeName, false) } module.exports = { - deleteRoutingEndpoint: AuthDecorator.checkAuthToken(deleteRoutingEndpoint), - updateRoutingEndpoint: AuthDecorator.checkAuthToken(updateRoutingEndpoint), - createRoutingEndpoint: AuthDecorator.checkAuthToken(createRoutingEndpoint), - getRoutingEndPoint: AuthDecorator.checkAuthToken(getRoutingEndPoint), - getRoutingsEndPoint: AuthDecorator.checkAuthToken(getRoutingsEndPoint) + deleteRoutingEndpoint: (deleteRoutingEndpoint), + updateRoutingEndpoint: (updateRoutingEndpoint), + createRoutingEndpoint: (createRoutingEndpoint), + getRoutingEndPoint: (getRoutingEndPoint), + getRoutingsEndPoint: (getRoutingsEndPoint) } diff --git a/src/controllers/secret-controller.js b/src/controllers/secret-controller.js new file mode 100644 index 000000000..d4457a068 --- /dev/null +++ b/src/controllers/secret-controller.js @@ -0,0 +1,66 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const SecretService = require('../services/secret-service') +const YamlParserService = require('../services/yaml-parser-service') + +const createSecretEndpoint = async function (req) { + const secret = req.body + return SecretService.createSecretEndpoint(secret) +} + +const updateSecretEndpoint = async function (req) { + const secret = req.body + const secretName = req.params.name + return SecretService.updateSecretEndpoint(secretName, secret) +} + +const getSecretEndpoint = async function (req) { + const secretName = req.params.name + return SecretService.getSecretEndpoint(secretName) +} + +const listSecretsEndpoint = async function (req) { + return SecretService.listSecretsEndpoint() +} + +const deleteSecretEndpoint = async function (req) { + const secretName = req.params.name + return SecretService.deleteSecretEndpoint(secretName) +} + +const createSecretFromYamlEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const secretData = await YamlParserService.parseSecretFile(fileContent) + return SecretService.createSecretEndpoint(secretData) +} + +const updateSecretFromYamlEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const secretName = req.params.name + const secretData = await YamlParserService.parseSecretFile(fileContent, { + isUpdate: true, + secretName: secretName + }) + return SecretService.updateSecretEndpoint(secretName, secretData) +} + +module.exports = { + createSecretEndpoint, + updateSecretEndpoint, + getSecretEndpoint, + listSecretsEndpoint, + deleteSecretEndpoint, + createSecretFromYamlEndpoint, + updateSecretFromYamlEndpoint +} diff --git a/src/controllers/service-controller.js b/src/controllers/service-controller.js new file mode 100644 index 000000000..b07df17a7 --- /dev/null +++ b/src/controllers/service-controller.js @@ -0,0 +1,66 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const ServiceService = require('../services/services-service') +const YamlParserService = require('../services/yaml-parser-service') + +const createServiceEndpoint = async function (req) { + const serviceData = req.body + return ServiceService.createServiceEndpoint(serviceData) +} + +const updateServiceEndpoint = async function (req) { + const serviceName = req.params.name + const serviceData = req.body + return ServiceService.updateServiceEndpoint(serviceName, serviceData) +} + +const deleteServiceEndpoint = async function (req) { + const serviceName = req.params.name + return ServiceService.deleteServiceEndpoint(serviceName) +} + +const getServiceEndpoint = async function (req) { + const serviceName = req.params.name + return ServiceService.getServiceEndpoint(serviceName) +} + +const listServicesEndpoint = async function (req) { + return ServiceService.getServicesListEndpoint() +} + +const createServiceYAMLEndpoint = async function (req) { + const fileContent = req.file.buffer.toString() + const serviceData = await YamlParserService.parseServiceFile(fileContent) + return ServiceService.createServiceEndpoint(serviceData) +} + +const updateServiceYAMLEndpoint = async function (req) { + const serviceName = req.params.name + const fileContent = req.file.buffer.toString() + const serviceData = await YamlParserService.parseServiceFile(fileContent, { + isUpdate: true, + serviceName: serviceName + }) + return ServiceService.updateServiceEndpoint(serviceName, serviceData) +} + +module.exports = { + createServiceEndpoint, + updateServiceEndpoint, + deleteServiceEndpoint, + getServiceEndpoint, + listServicesEndpoint, + createServiceYAMLEndpoint, + updateServiceYAMLEndpoint +} diff --git a/src/controllers/tunnel-controller.js b/src/controllers/tunnel-controller.js index 2157de409..fb3ea34c9 100644 --- a/src/controllers/tunnel-controller.js +++ b/src/controllers/tunnel-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,12 +11,11 @@ * */ -const AuthDecorator = require('../decorators/authorization-decorator') const TunnelService = require('../services/tunnel-service') const Errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') -const manageTunnelEndPoint = async function (req, user) { +const manageTunnelEndPoint = async function (req) { const action = req.body.action const tunnelData = { iofogUuid: req.params.id @@ -24,24 +23,24 @@ const manageTunnelEndPoint = async function (req, user) { switch (action) { case 'open': - await TunnelService.openTunnel(tunnelData, user, false) + await TunnelService.openTunnel(tunnelData, false) break case 'close': - await TunnelService.closeTunnel(tunnelData, user) + await TunnelService.closeTunnel(tunnelData) break default: throw new Errors.ValidationError(ErrorMessages.INVALID_ACTION_PROPERTY) } } -const getTunnelEndPoint = async function (req, user) { +const getTunnelEndPoint = async function (req) { const tunnelData = { iofogUuid: req.params.id } - return TunnelService.findTunnel(tunnelData, user) + return TunnelService.findTunnel(tunnelData) } module.exports = { - manageTunnelEndPoint: AuthDecorator.checkAuthToken(manageTunnelEndPoint), - getTunnelEndPoint: AuthDecorator.checkAuthToken(getTunnelEndPoint) + manageTunnelEndPoint: (manageTunnelEndPoint), + getTunnelEndPoint: (getTunnelEndPoint) } diff --git a/src/controllers/user-controller.js b/src/controllers/user-controller.js index 6314c8b2c..9cff77445 100644 --- a/src/controllers/user-controller.js +++ b/src/controllers/user-controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,26 +12,8 @@ */ const UserService = require('../services/user-service') -const AuthDecorator = require('../decorators/authorization-decorator') -const AppHelper = require('../helpers/app-helper') - const Validator = require('../schemas') -const userSignupEndPoint = async function (req) { - const user = req.body - - await Validator.validate(user, Validator.schemas.signUp) - - const encryptedPassword = AppHelper.encryptText(user.password, user.email) - const newUser = { - firstName: user.firstName, - lastName: user.lastName, - email: user.email, - password: encryptedPassword - } - return UserService.signUp(newUser, false) -} - const userLoginEndPoint = async function (req) { const user = req.body @@ -39,66 +21,36 @@ const userLoginEndPoint = async function (req) { const credentials = { email: user.email, - password: user.password + password: user.password, + totp: user.totp } return UserService.login(credentials, false) } -const resendActivationEndPoint = async function (req) { - const emailData = req.query - return UserService.resendActivation(emailData, false) -} +const refreshTokenEndPoint = async function (req) { + const token = req.body -const activateUserAccountEndPoint = async function (req) { - const codeData = req.body + await Validator.validate(token, Validator.schemas.refresh) - await UserService.activateUser(codeData, false) -} - -const userLogoutEndPoint = async function (req, user) { - return UserService.logout(user, false) -} + const credentials = { + refreshToken: token.refreshToken -const getUserProfileEndPoint = async function (req, user) { - return { - firstName: user.firstName, - lastName: user.lastName, - email: user.email } + return UserService.refresh(credentials, false) } -const updateUserProfileEndPoint = async function (req, user) { - const profileData = req.body - return UserService.updateUserDetails(user, profileData, false) -} - -const deleteUserProfileEndPoint = async function (req, user) { - return UserService.deleteUser(req.body.force, user, false) -} - -const updateUserPasswordEndPoint = async function (req, user) { - const passwordUpdates = req.body - - await Validator.validate(passwordUpdates, Validator.schemas.updatePassword) - - return UserService.updateUserPassword(passwordUpdates, user, false) +const getUserProfileEndPoint = async function (req) { + return UserService.profile(req, false) } -const resetUserPasswordEndPoint = async function (req) { - const emailObj = req.body - return UserService.resetUserPassword(emailObj, false) +const userLogoutEndPoint = async function (req) { + return UserService.logout(req, false) } module.exports = { - userSignupEndPoint: userSignupEndPoint, userLoginEndPoint: userLoginEndPoint, - resendActivationEndPoint: resendActivationEndPoint, - activateUserAccountEndPoint: activateUserAccountEndPoint, - userLogoutEndPoint: AuthDecorator.checkAuthToken(userLogoutEndPoint), - getUserProfileEndPoint: AuthDecorator.checkAuthToken(getUserProfileEndPoint), - updateUserProfileEndPoint: AuthDecorator.checkAuthToken(updateUserProfileEndPoint), - deleteUserProfileEndPoint: AuthDecorator.checkAuthToken(deleteUserProfileEndPoint), - updateUserPasswordEndPoint: AuthDecorator.checkAuthToken(updateUserPasswordEndPoint), - resetUserPasswordEndPoint: resetUserPasswordEndPoint + refreshTokenEndPoint: refreshTokenEndPoint, + getUserProfileEndPoint: getUserProfileEndPoint, + userLogoutEndPoint: userLogoutEndPoint } diff --git a/src/controllers/volume-mount-controller.js b/src/controllers/volume-mount-controller.js new file mode 100644 index 000000000..9628a8bad --- /dev/null +++ b/src/controllers/volume-mount-controller.js @@ -0,0 +1,76 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const VolumeMountService = require('../services/volume-mount-service') +const YAMLParserService = require('../services/yaml-parser-service') + +const listVolumeMountsEndpoint = async (req) => { + return VolumeMountService.listVolumeMountsEndpoint() +} + +const getVolumeMountEndpoint = async (req) => { + return VolumeMountService.getVolumeMountEndpoint(req.params.name) +} + +const createVolumeMountEndpoint = async (req) => { + return VolumeMountService.createVolumeMountEndpoint(req.body) +} + +const updateVolumeMountEndpoint = async (req) => { + return VolumeMountService.updateVolumeMountEndpoint(req.params.name, req.body) +} + +const deleteVolumeMountEndpoint = async (req) => { + return VolumeMountService.deleteVolumeMountEndpoint(req.params.name) +} + +const createVolumeMountYamlEndpoint = async (req) => { + const fileContent = req.file.buffer.toString() + const volumeMountData = await YAMLParserService.parseVolumeMountFile(fileContent) + return VolumeMountService.createVolumeMountEndpoint(volumeMountData) +} + +const updateVolumeMountYamlEndpoint = async (req) => { + const fileContent = req.file.buffer.toString() + const name = req.params.name + const volumeMountData = await YAMLParserService.parseVolumeMountFile(fileContent, { + isUpdate: true, + volumeMountName: name + }) + return VolumeMountService.updateVolumeMountEndpoint(name, volumeMountData) +} + +const getVolumeMountLinkEndpoint = async (req) => { + return VolumeMountService.getVolumeMountLinkEndpoint(req.params.name) +} + +const linkVolumeMountEndpoint = async (req) => { + return VolumeMountService.linkVolumeMountEndpoint(req.params.name, req.body.fogUuids) +} + +const unlinkVolumeMountEndpoint = async (req) => { + return VolumeMountService.unlinkVolumeMountEndpoint(req.params.name, req.body.fogUuids) +} + +module.exports = { + listVolumeMountsEndpoint, + getVolumeMountEndpoint, + createVolumeMountEndpoint, + updateVolumeMountEndpoint, + deleteVolumeMountEndpoint, + createVolumeMountYamlEndpoint, + updateVolumeMountYamlEndpoint, + getVolumeMountLinkEndpoint, + linkVolumeMountEndpoint, + unlinkVolumeMountEndpoint +} diff --git a/src/daemon.js b/src/daemon.js index 779508c3d..105ff882b 100644 --- a/src/daemon.js +++ b/src/daemon.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/constants.js b/src/data/constants.js index 83c9b7af6..4c75fdb28 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -1,5 +1,4 @@ module.exports = { ROUTER_CATALOG_NAME: 'Router', - PROXY_CATALOG_NAME: 'Proxy', - PORT_ROUTER_CATALOG_NAME: 'PortRouter' + DEBUG_CATALOG_NAME: 'Debug' } diff --git a/src/data/managers/application-manager.js b/src/data/managers/application-manager.js index 3b452729c..84f62e0ba 100644 --- a/src/data/managers/application-manager.js +++ b/src/data/managers/application-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/application-template-manager.js b/src/data/managers/application-template-manager.js index 5c8c6eba4..98ef4ca0f 100644 --- a/src/data/managers/application-template-manager.js +++ b/src/data/managers/application-template-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/application-template-variable-manager.js b/src/data/managers/application-template-variable-manager.js index 6488eb931..66fae8f2f 100644 --- a/src/data/managers/application-template-variable-manager.js +++ b/src/data/managers/application-template-variable-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/base-manager.js b/src/data/managers/base-manager.js index e0ca80b50..4dc4ed24c 100644 --- a/src/data/managers/base-manager.js +++ b/src/data/managers/base-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* -* * Copyright (c) 2020 Edgeworx, Inc. +* * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/catalog-item-image-manager.js b/src/data/managers/catalog-item-image-manager.js index bc4eae181..14076f1c5 100644 --- a/src/data/managers/catalog-item-image-manager.js +++ b/src/data/managers/catalog-item-image-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/catalog-item-input-type-manager.js b/src/data/managers/catalog-item-input-type-manager.js index 881217033..2aa19d81e 100644 --- a/src/data/managers/catalog-item-input-type-manager.js +++ b/src/data/managers/catalog-item-input-type-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/catalog-item-manager.js b/src/data/managers/catalog-item-manager.js index 2651de7bc..c190fffe6 100644 --- a/src/data/managers/catalog-item-manager.js +++ b/src/data/managers/catalog-item-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/catalog-item-output-type-manager.js b/src/data/managers/catalog-item-output-type-manager.js index c6e5602d3..6ec69012c 100644 --- a/src/data/managers/catalog-item-output-type-manager.js +++ b/src/data/managers/catalog-item-output-type-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/certificate-manager.js b/src/data/managers/certificate-manager.js new file mode 100644 index 000000000..a54c62a06 --- /dev/null +++ b/src/data/managers/certificate-manager.js @@ -0,0 +1,227 @@ +const BaseManager = require('./base-manager') +const models = require('../models') +const Certificate = models.Certificate +const { Op } = require('sequelize') +const SecretManager = require('./secret-manager') +const AppHelper = require('../../helpers/app-helper') + +class CertificateManager extends BaseManager { + getEntity () { + return Certificate + } + + async createCertificateRecord (certData, transaction) { + // First find the secret by name to get its ID + const secret = await SecretManager.findOne({ name: certData.name }, transaction) + + if (secret) { + // Link the certificate to the secret + certData.secretId = secret.id + } + + return this.create(certData, transaction) + } + + async findCertificatesByCA (caId, transaction) { + AppHelper.checkTransaction(transaction) + + const options = transaction.fakeTransaction + ? { + where: { signedById: caId }, + include: ['secret'] } + : { + where: { signedById: caId }, + include: ['secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async findExpiringCertificates (days = 30, transaction) { + AppHelper.checkTransaction(transaction) + + const expirationDate = new Date() + expirationDate.setDate(expirationDate.getDate() + days) + + const options = transaction.fakeTransaction + ? { + where: { validTo: { [Op.lt]: expirationDate + } + }, + include: ['signingCA'] } + : { + where: { validTo: { [Op.lt]: expirationDate + } + }, + include: ['signingCA'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async findCertificateByName (name, transaction) { + AppHelper.checkTransaction(transaction) + + const options = transaction.fakeTransaction + ? { + where: { name }, + include: ['signingCA', 'secret'] } + : { + where: { name }, + include: ['signingCA', 'secret'], + transaction: transaction } + return this.getEntity().findOne(options) + } + + async findAllCAs (transaction) { + AppHelper.checkTransaction(transaction) + + const options = transaction.fakeTransaction + ? { + where: { isCA: true }, + include: ['secret'] } + : { + where: { isCA: true }, + include: ['secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async findAllCertificates (transaction) { + AppHelper.checkTransaction(transaction) + + const options = transaction.fakeTransaction + ? { + include: ['signingCA', 'secret'] } + : { + include: ['signingCA', 'secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async deleteCertificate (name, transaction) { + return this.delete({ name }, transaction) + } + + async updateCertificate (id, updates, transaction) { + AppHelper.checkTransaction(transaction) + + // Find existing certificate + const options = transaction.fakeTransaction + ? { + where: { id } } + : { + where: { id }, + transaction: transaction } + const cert = await this.getEntity().findOne(options) + + if (!cert) { + throw new Error(`Certificate with id ${id} not found`) + } + + // Update certificate + return this.update({ id }, updates, transaction) + } + + async findExpiredCertificates (transaction) { + AppHelper.checkTransaction(transaction) + + const currentDate = new Date() + + const options = transaction.fakeTransaction + ? { + where: { validTo: { [Op.lt]: currentDate + } + }, + include: ['signingCA', 'secret'] } + : { + where: { validTo: { [Op.lt]: currentDate + } + }, + include: ['signingCA', 'secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async getCertificateChain (certId, transaction) { + AppHelper.checkTransaction(transaction) + const chain = [] + + const options = transaction.fakeTransaction + ? { + where: { id: certId }, + include: ['signingCA', 'secret'] } + : { + where: { id: certId }, + include: ['signingCA', 'secret'], + transaction: transaction } + let currentCert = await this.getEntity().findOne(options) + + if (!currentCert) { + return chain + } + + chain.push(currentCert) + + // Traverse up the chain of signing CAs + while (currentCert.signingCA) { + const parentOptions = transaction.fakeTransaction + ? { where: { id: currentCert.signedById }, include: ['signingCA', 'secret'] + } + : { where: { id: currentCert.signedById }, include: ['signingCA', 'secret'], transaction: transaction + } + currentCert = await this.getEntity().findOne(parentOptions) + + if (currentCert) { + chain.push(currentCert) + } else { + break + } + } + + return chain + } + + async findCertificatesForRenewal (days = 30, transaction) { + AppHelper.checkTransaction(transaction) + + // Calculate the date range - we want certificates that expire between now and (now + days) + const now = new Date() + const futureDate = new Date() + futureDate.setDate(futureDate.getDate() + days) + + const options = transaction.fakeTransaction + ? { + where: { + validTo: { + [Op.gt]: now, + [Op.lt]: futureDate + } + }, + include: ['signingCA', 'secret'] } + : { + where: { + validTo: { + [Op.gt]: now, + [Op.lt]: futureDate + } + }, + include: ['signingCA', 'secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } + + async getCertificateChildren (caId, transaction) { + AppHelper.checkTransaction(transaction) + + const options = transaction.fakeTransaction + ? { + where: { signedById: caId }, + include: ['secret'] } + : { + where: { signedById: caId }, + include: ['secret'], + transaction: transaction } + return this.getEntity().findAll(options) + } +} + +module.exports = new CertificateManager() diff --git a/src/data/managers/change-tracking-manager.js b/src/data/managers/change-tracking-manager.js index d86b69044..1f74c4c94 100644 --- a/src/data/managers/change-tracking-manager.js +++ b/src/data/managers/change-tracking-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/config-manager.js b/src/data/managers/config-manager.js index 67f055e02..824f23401 100644 --- a/src/data/managers/config-manager.js +++ b/src/data/managers/config-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/config-map-manager.js b/src/data/managers/config-map-manager.js new file mode 100644 index 000000000..b8da144ca --- /dev/null +++ b/src/data/managers/config-map-manager.js @@ -0,0 +1,55 @@ +const BaseManager = require('./base-manager') +const SecretHelper = require('../../helpers/secret-helper') +const models = require('../models') +const ConfigMap = models.ConfigMap + +class ConfigMapManager extends BaseManager { + getEntity () { + return ConfigMap + } + + async createConfigMap (name, immutable, data, transaction) { + return this.create({ + name, + immutable: immutable, + data: data + }, transaction) + } + + async updateConfigMap (name, immutable, data, transaction) { + const encryptedData = await SecretHelper.encryptSecret(data, name) + return this.update( + { name }, + { immutable: immutable, data: encryptedData }, + transaction + ) + } + + async getConfigMap (name, transaction) { + const configMap = await this.findOne({ name }, transaction) + if (!configMap) { + return null + } + return { + ...configMap.toJSON(), + data: configMap.data + } + } + + async listConfigMaps (transaction) { + const configMaps = await this.findAll({}, transaction) + return configMaps.map(configMap => ({ + id: configMap.id, + name: configMap.name, + immutable: configMap.immutable, + created_at: configMap.created_at, + updated_at: configMap.updated_at + })) + } + + async deleteConfigMap (name, transaction) { + return this.delete({ name }, transaction) + } +} + +module.exports = new ConfigMapManager() diff --git a/src/data/managers/email-activation-code-manager.js b/src/data/managers/email-activation-code-manager.js deleted file mode 100644 index a7cb5ee5a..000000000 --- a/src/data/managers/email-activation-code-manager.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const models = require('../models') -const EmailActivationCode = models.EmailActivationCode -const BaseManager = require('./base-manager') -const AppHelper = require('../../helpers/app-helper') -const Sequelize = require('sequelize') -const Op = Sequelize.Op - -class EmailActivationCodeManager extends BaseManager { - getEntity () { - return EmailActivationCode - } - - async getByActivationCode (activationCode, transaction) { - AppHelper.checkTransaction(transaction) - - return EmailActivationCode.findOne({ - where: { - activationCode: activationCode - } - }, { - transaction: transaction - }) - }; - - async createActivationCode (userId, activationCode, expirationTime, transaction) { - AppHelper.checkTransaction(transaction) - - return EmailActivationCode.create({ - userId: userId, - activationCode: activationCode, - expirationTime: expirationTime - }, { - transaction: transaction - }) - }; - - async verifyActivationCode (activationCode, transaction) { - return EmailActivationCode.findOne({ - where: { - activationCode: activationCode, - expirationTime: { - [Op.gt]: new Date().getTime() - } - } - }, { - transaction: transaction - }) - } -} - -const instance = new EmailActivationCodeManager() -module.exports = instance diff --git a/src/data/managers/event-manager.js b/src/data/managers/event-manager.js new file mode 100644 index 000000000..7b32bdffa --- /dev/null +++ b/src/data/managers/event-manager.js @@ -0,0 +1,152 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const Event = models.Event +const { Op } = require('sequelize') +const AppHelper = require('../../helpers/app-helper') +const logger = require('../../logger') + +class EventManager extends BaseManager { + getEntity () { + return Event + } + + async findAllWithFilters (filters, transaction) { + AppHelper.checkTransaction(transaction) + + const where = {} + + // Time range filters + if (filters.startTime || filters.endTime) { + where.timestamp = {} + if (filters.startTime) { + where.timestamp[Op.gte] = filters.startTime + } + if (filters.endTime) { + where.timestamp[Op.lte] = filters.endTime + } + } + + // Endpoint type filter + if (filters.endpointType) { + where.endpointType = filters.endpointType + } + + // Resource type filter + if (filters.resourceType) { + where.resourceType = filters.resourceType + } + + // Status filter + if (filters.status) { + where.status = filters.status + } + + // Method filter (can be array for multiple methods) + if (filters.method) { + if (Array.isArray(filters.method)) { + where.method = { [Op.in]: filters.method } + } else { + where.method = filters.method + } + } + + // Actor ID filter + if (filters.actorId) { + where.actorId = filters.actorId + } + + // Event type filter + if (filters.eventType) { + where.eventType = filters.eventType + } + + // Ensure limit is a valid positive integer + let limit = 200 // Default + if (filters.limit !== undefined && filters.limit !== null) { + const parsedLimit = parseInt(filters.limit) + if (!isNaN(parsedLimit) && parsedLimit > 0) { + limit = Math.min(parsedLimit, 1000) + } + } + + // Ensure offset is a valid non-negative integer + let offset = 0 + if (filters.offset !== undefined && filters.offset !== null) { + const parsedOffset = parseInt(filters.offset) + if (!isNaN(parsedOffset) && parsedOffset >= 0) { + offset = parsedOffset + } + } + + const options = { + where: where, + order: [['timestamp', 'DESC']], + limit: Number(limit), // Ensure it's a number + offset: Number(offset) // Ensure it's a number + } + + if (!transaction.fakeTransaction) { + options.transaction = transaction + } + + const { count, rows } = await Event.findAndCountAll(options) + + // CRITICAL: Always enforce the limit, even if Sequelize returns more rows + // Sequelize's findAndCountAll sometimes doesn't respect limit in certain scenarios + const limitedRows = rows.slice(0, limit) + + if (rows.length > limit) { + logger.warn(`Sequelize returned ${rows.length} rows but limit was ${limit}. Limiting to ${limit} rows.`) + } + + logger.debug(`Event query final - returning ${limitedRows.length} events`) + + return { + events: limitedRows, + total: count, + limit: limit, + offset: offset + } + } + + async deleteEventsOlderThanDays (days, transaction) { + AppHelper.checkTransaction(transaction) + + const options = { + where: {} + } + + // Special case: days = 0 means delete ALL events (no timestamp filter) + // days > 0 means delete events older than that many days + if (days > 0) { + const cutoffTimestamp = Date.now() - (days * 24 * 60 * 60 * 1000) + options.where.timestamp = { + [Op.lt]: cutoffTimestamp + } + } + // If days = 0, where clause is empty, so all events will be deleted + + if (!transaction.fakeTransaction) { + options.transaction = transaction + } + + const deletedCount = await Event.destroy(options) + return deletedCount + } +} + +const instance = new EventManager() +module.exports = instance diff --git a/src/data/managers/fog-used-token-manager.js b/src/data/managers/fog-used-token-manager.js new file mode 100644 index 000000000..0b9a3349c --- /dev/null +++ b/src/data/managers/fog-used-token-manager.js @@ -0,0 +1,119 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const models = require('../models') +const logger = require('../../logger') +const { Op } = require('sequelize') + +class FogUsedTokenManager { + /** + * Store a JTI (JWT ID) to mark it as used + * @param {string} jti - The JWT ID + * @param {string} fogUuid - The UUID of the fog node + * @param {number} exp - The expiration timestamp + * @param {Object} transaction - Sequelize transaction + * @returns {Promise} + */ + static async storeJti (jti, fogUuid, exp, transaction) { + try { + // Input validation + if (!jti || typeof jti !== 'string') { + throw new Error('JTI must be a non-empty string') + } + if (!fogUuid || typeof fogUuid !== 'string') { + throw new Error('Fog UUID must be a non-empty string') + } + + // Ensure exp is a valid integer (Unix timestamp) + const expiryTime = parseInt(exp, 10) + if (isNaN(expiryTime) || expiryTime <= 0) { + throw new Error('Expiration timestamp must be a positive integer') + } + + // Prepare the data object + const tokenData = { + jti, + iofogUuid: fogUuid, + expiryTime: expiryTime + } + + // Create the record with or without transaction + if (!transaction || transaction.fakeTransaction) { + await models.FogUsedToken.create(tokenData) + } else { + await models.FogUsedToken.create(tokenData, { transaction }) + } + } catch (error) { + // Check if it's a duplicate JTI error + if (error.name === 'SequelizeUniqueConstraintError' && error.fields && error.fields.jti) { + logger.warn(`JTI already exists: ${jti}`) + throw new Error('JWT token already used') + } + + logger.error(`Failed to store JTI: ${error.message}`) + throw error + } + } + + /** + * Check if a JTI has already been used + * @param {string} jti - The JWT ID to check + * @param {Object} transaction - Sequelize transaction + * @returns {Promise} True if the JTI has been used, false otherwise + */ + static async isJtiUsed (jti, transaction) { + try { + let token + if (!transaction || transaction.fakeTransaction) { + // If no transaction or fake transaction, query without transaction + token = await models.FogUsedToken.findOne({ + where: { jti } + }) + } else { + // Use the provided transaction + token = await models.FogUsedToken.findOne({ + where: { jti }, + transaction + }) + } + return !!token + } catch (error) { + logger.error(`Failed to check JTI: ${error.message}`) + throw error + } + } + + /** + * Clean up expired JTIs + * @returns {Promise} Number of deleted tokens + */ + static async cleanupExpiredJtis () { + try { + const now = Math.floor(Date.now() / 1000) // Convert to Unix timestamp (seconds) + const result = await models.FogUsedToken.destroy({ + where: { + expiryTime: { + [Op.lt]: now + } + } + }) + logger.debug(`Cleaned up ${result} expired JTIs`) + return result + } catch (error) { + logger.error(`Failed to cleanup expired JTIs: ${error.message}`) + throw error + } + } +} + +module.exports = FogUsedTokenManager diff --git a/src/data/managers/hw-info-manager.js b/src/data/managers/hw-info-manager.js index fe5674274..9a8fcadb0 100644 --- a/src/data/managers/hw-info-manager.js +++ b/src/data/managers/hw-info-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/iofog-manager.js b/src/data/managers/iofog-manager.js index 9aec666e8..a0e98c762 100644 --- a/src/data/managers/iofog-manager.js +++ b/src/data/managers/iofog-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -16,7 +16,6 @@ const models = require('../models') const Fog = models.Fog const Tags = models.Tags -const FogAccessToken = models.FogAccessToken const Microservice = models.Microservice const Strace = models.StraceDiagnostics @@ -65,19 +64,6 @@ class FogManager extends BaseManager { }) } - // no transaction required here, used by auth decorator - checkToken (token) { - return Fog.findOne({ - include: [{ - model: FogAccessToken, - as: 'accessToken', - where: { - token: token - } - }] - }) - } - // no transaction required here, used by agent-last-active decorator updateLastActive (uuid, timestamp) { return Fog.update({ diff --git a/src/data/managers/iofog-provision-key-manager.js b/src/data/managers/iofog-provision-key-manager.js index 8a6cefdab..0e6e1ca40 100644 --- a/src/data/managers/iofog-provision-key-manager.js +++ b/src/data/managers/iofog-provision-key-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/iofog-public-key-manager.js b/src/data/managers/iofog-public-key-manager.js new file mode 100644 index 000000000..93004f095 --- /dev/null +++ b/src/data/managers/iofog-public-key-manager.js @@ -0,0 +1,89 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const FogPublicKey = models.FogPublicKey + +class FogPublicKeyManager extends BaseManager { + getEntity () { + return FogPublicKey + } + + // Find public key by fog UUID + findByFogUuid (fogUuid, transaction) { + const options = transaction.fakeTransaction + ? { + where: { + iofogUuid: fogUuid + } + } + : { + where: { + iofogUuid: fogUuid + }, + transaction: transaction + } + + return FogPublicKey.findOne(options) + } + + // Update or create public key for a fog + updateOrCreate (fogUuid, publicKey, transaction) { + const options = transaction.fakeTransaction + ? { + where: { + iofogUuid: fogUuid + } + } + : { + where: { + iofogUuid: fogUuid + }, + transaction: transaction + } + + return FogPublicKey.findOne(options).then((existingKey) => { + if (existingKey) { + const updateOptions = transaction.fakeTransaction + ? { + where: { + iofogUuid: fogUuid + } + } + : { + where: { + iofogUuid: fogUuid + }, + transaction: transaction + } + + return FogPublicKey.update({ + publicKey: publicKey + }, updateOptions) + } else { + const createOptions = transaction.fakeTransaction + ? {} + : { transaction: transaction } + + return FogPublicKey.create({ + iofogUuid: fogUuid, + publicKey: publicKey + }, createOptions) + } + }) + } +} + +const instance = new FogPublicKeyManager() +module.exports = instance diff --git a/src/data/managers/iofog-type-manager.js b/src/data/managers/iofog-type-manager.js index c03a53f91..1111bf6b4 100644 --- a/src/data/managers/iofog-type-manager.js +++ b/src/data/managers/iofog-type-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/iofog-version-command-manager.js b/src/data/managers/iofog-version-command-manager.js index 1def0136a..db027a5b3 100644 --- a/src/data/managers/iofog-version-command-manager.js +++ b/src/data/managers/iofog-version-command-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/microservice-arg-manager.js b/src/data/managers/microservice-arg-manager.js index a4bf19c20..5ccf6343f 100644 --- a/src/data/managers/microservice-arg-manager.js +++ b/src/data/managers/microservice-arg-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/microservice-cap-add-manager.js b/src/data/managers/microservice-cap-add-manager.js new file mode 100644 index 000000000..b92430fa2 --- /dev/null +++ b/src/data/managers/microservice-cap-add-manager.js @@ -0,0 +1,35 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const MicroserviceCapAdd = models.MicroserviceCapAdd + +const MicroserviceCapAddExcludedFields = [ + 'id', + 'microservice_uuid', + 'microserviceUuid' +] + +class MicroserviceCapAddManager extends BaseManager { + getEntity () { + return MicroserviceCapAdd + } + + findAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: MicroserviceCapAddExcludedFields }, transaction) + } +} + +const instance = new MicroserviceCapAddManager() +module.exports = instance diff --git a/src/data/managers/microservice-cap-drop-manager.js b/src/data/managers/microservice-cap-drop-manager.js new file mode 100644 index 000000000..2dce889f2 --- /dev/null +++ b/src/data/managers/microservice-cap-drop-manager.js @@ -0,0 +1,35 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const MicroserviceCapDrop = models.MicroserviceCapDrop + +const MicroserviceCapDropExcludedFields = [ + 'id', + 'microservice_uuid', + 'microserviceUuid' +] + +class MicroserviceCapDropManager extends BaseManager { + getEntity () { + return MicroserviceCapDrop + } + + findAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: MicroserviceCapDropExcludedFields }, transaction) + } +} + +const instance = new MicroserviceCapDropManager() +module.exports = instance diff --git a/src/data/managers/microservice-cdi-device-manager.js b/src/data/managers/microservice-cdi-device-manager.js new file mode 100644 index 000000000..dc13ab60b --- /dev/null +++ b/src/data/managers/microservice-cdi-device-manager.js @@ -0,0 +1,35 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const MicroserviceCdiDev = models.MicroserviceCdiDev + +const MicroserviceCdiDevExcludedFields = [ + 'id', + 'microservice_uuid', + 'microserviceUuid' +] + +class MicroserviceCdiDevManager extends BaseManager { + getEntity () { + return MicroserviceCdiDev + } + + findAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: MicroserviceCdiDevExcludedFields }, transaction) + } +} + +const instance = new MicroserviceCdiDevManager() +module.exports = instance diff --git a/src/data/managers/microservice-env-manager.js b/src/data/managers/microservice-env-manager.js index cc14c1c51..a9ffae343 100644 --- a/src/data/managers/microservice-env-manager.js +++ b/src/data/managers/microservice-env-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/access-token-manager.js b/src/data/managers/microservice-exec-status-manager.js similarity index 50% rename from src/data/managers/access-token-manager.js rename to src/data/managers/microservice-exec-status-manager.js index 9d89a009f..ff5edec7f 100644 --- a/src/data/managers/access-token-manager.js +++ b/src/data/managers/microservice-exec-status-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,24 +13,25 @@ const BaseManager = require('./base-manager') const models = require('../models') -const AccessToken = models.AccessToken +const MicroserviceExecStatus = models.MicroserviceExecStatus -class AccessTokenManager extends BaseManager { +const microserviceExecStatusExcludedFields = [ + 'id', + 'microservice_uuid', + 'microserviceUuid', + 'created_at', + 'updated_at' +] + +class MicroserviceExecStatusManager extends BaseManager { getEntity () { - return AccessToken + return MicroserviceExecStatus } - // no transaction required here, used by auth decorator - updateExpirationTime (id, newTime) { - return AccessToken.update({ - expirationTime: newTime - }, { - where: { - id: id - } - }) + findAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: microserviceExecStatusExcludedFields }, transaction) } } -const instance = new AccessTokenManager() +const instance = new MicroserviceExecStatusManager() module.exports = instance diff --git a/src/data/managers/microservice-extra-host-manager.js b/src/data/managers/microservice-extra-host-manager.js index 9d343d5f4..22e0a3406 100644 --- a/src/data/managers/microservice-extra-host-manager.js +++ b/src/data/managers/microservice-extra-host-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/iofog-access-token-manager.js b/src/data/managers/microservice-healthcheck-manager.js similarity index 50% rename from src/data/managers/iofog-access-token-manager.js rename to src/data/managers/microservice-healthcheck-manager.js index 9ad49ef5e..9d905bcd3 100644 --- a/src/data/managers/iofog-access-token-manager.js +++ b/src/data/managers/microservice-healthcheck-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,24 +13,25 @@ const BaseManager = require('./base-manager') const models = require('../models') -const FogAccessToken = models.FogAccessToken +const MicroserviceHealthCheck = models.MicroserviceHealthCheck -class FogAccessTokenManager extends BaseManager { +const microserviceHealthCheckExcludedFields = [ + 'id', + 'microservice_uuid', + 'microserviceUuid', + 'created_at', + 'updated_at' +] + +class MicroserviceHealthCheckManager extends BaseManager { getEntity () { - return FogAccessToken + return MicroserviceHealthCheck } - // no transaction required here, used by auth decorator - updateExpirationTime (id, newTime) { - return FogAccessToken.update({ - expirationTime: newTime - }, { - where: { - id: id - } - }) + findAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: microserviceHealthCheckExcludedFields }, transaction) } } -const instance = new FogAccessTokenManager() +const instance = new MicroserviceHealthCheckManager() module.exports = instance diff --git a/src/data/managers/microservice-manager.js b/src/data/managers/microservice-manager.js index 4c63af9e8..6c50fcd07 100644 --- a/src/data/managers/microservice-manager.js +++ b/src/data/managers/microservice-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -18,16 +18,21 @@ const MicroservicePort = models.MicroservicePort const MicroserviceEnv = models.MicroserviceEnv const MicroserviceExtraHost = models.MicroserviceExtraHost const MicroserviceArg = models.MicroserviceArg +const MicroserviceCdiDev = models.MicroserviceCdiDev +const MicroserviceCapAdd = models.MicroserviceCapAdd +const MicroserviceCapDrop = models.MicroserviceCapDrop const VolumeMapping = models.VolumeMapping const StraceDiagnostics = models.StraceDiagnostics const CatalogItem = models.CatalogItem const CatalogItemImage = models.CatalogItemImage const Fog = models.Fog +const Tags = models.Tags const Application = models.Application -const User = models.User const Routing = models.Routing const Registry = models.Registry const MicroserviceStatus = models.MicroserviceStatus +const MicroserviceExecStatus = models.MicroserviceExecStatus +const MicroserviceHealthCheck = models.MicroserviceHealthCheck const Op = require('sequelize').Op const microserviceExcludedFields = [ @@ -35,7 +40,6 @@ const microserviceExcludedFields = [ 'created_at', 'updated_at', 'updatedBy', - 'isNetwork', 'rebuild', 'deleteWithCleanUp', 'imageSnapshot', @@ -68,6 +72,24 @@ class MicroserviceManager extends BaseManager { required: false, attributes: ['cmd'] }, + { + model: MicroserviceCdiDev, + as: 'cdiDevices', + required: false, + attributes: ['cdiDevices'] + }, + { + model: MicroserviceCapAdd, + as: 'capAdd', + required: false, + attributes: ['capAdd'] + }, + { + model: MicroserviceCapDrop, + as: 'capDrop', + required: false, + attributes: ['capDrop'] + }, { model: MicroservicePort, as: 'ports', @@ -125,8 +147,13 @@ class MicroserviceManager extends BaseManager { attributes: ['uuid'] }], attributes: { exclude: ['id', 'source_microservice_uuid', - 'sourceMicroserviceUuid', 'destMicroserviceUuid', 'sourceNetworkMicroserviceUuid', - 'destNetworkMicroserviceUuid', 'sourceIofogUuid', 'destIofogUuid'] } + 'sourceMicroserviceUuid', 'destMicroserviceUuid'] } + }, + { + model: MicroserviceHealthCheck, + as: 'healthCheck', + required: false, + attributes: ['test', 'interval', 'timeout', 'startPeriod', 'startInterval', 'retries'] } ], where: where, @@ -154,6 +181,24 @@ class MicroserviceManager extends BaseManager { required: false, attributes: ['cmd', 'id'] }, + { + model: MicroserviceCdiDev, + as: 'cdiDevices', + required: false, + attributes: ['cdiDevices'] + }, + { + model: MicroserviceCapAdd, + as: 'capAdd', + required: false, + attributes: ['capAdd'] + }, + { + model: MicroserviceCapDrop, + as: 'capDrop', + required: false, + attributes: ['capDrop'] + }, { model: MicroservicePort, as: 'ports', @@ -203,6 +248,24 @@ class MicroserviceManager extends BaseManager { as: 'application', required: false, attributes: ['isActivated'] + }, + { + model: Tags, + as: 'pubTags', + attributes: ['value'], + through: { attributes: [] } + }, + { + model: Tags, + as: 'subTags', + attributes: ['value'], + through: { attributes: [] } + }, + { + model: MicroserviceHealthCheck, + as: 'healthCheck', + required: false, + attributes: ['test', 'interval', 'timeout', 'startPeriod', 'startInterval', 'retries'] } ], where: { @@ -210,7 +273,10 @@ class MicroserviceManager extends BaseManager { [Op.or]: [ { - '$application.is_activated$': true + [Op.and]: [ + { '$application.is_activated$': true }, + { isActivated: true } + ] }, { '$catalogItem.category$': { [Op.eq]: 'SYSTEM' }, @@ -242,6 +308,24 @@ class MicroserviceManager extends BaseManager { required: false, attributes: ['cmd'] }, + { + model: MicroserviceCdiDev, + as: 'cdiDevices', + required: false, + attributes: ['cdiDevices'] + }, + { + model: MicroserviceCapAdd, + as: 'capAdd', + required: false, + attributes: ['capAdd'] + }, + { + model: MicroserviceCapDrop, + as: 'capDrop', + required: false, + attributes: ['capDrop'] + }, { model: MicroservicePort, as: 'ports', @@ -299,9 +383,13 @@ class MicroserviceManager extends BaseManager { attributes: ['uuid'] }], attributes: { exclude: ['id', - 'sourceMicroserviceUuid', 'destMicroserviceUuid', - 'sourceNetworkMicroserviceUuid', 'destNetworkMicroserviceUuid', - 'sourceIofogUuid', 'destIofogUuid'] } + 'sourceMicroserviceUuid', 'destMicroserviceUuid'] } + }, + { + model: MicroserviceHealthCheck, + as: 'healthCheck', + required: false, + attributes: ['test', 'interval', 'timeout', 'startPeriod', 'startInterval', 'retries'] } ], where: where, @@ -334,6 +422,11 @@ class MicroserviceManager extends BaseManager { model: MicroserviceStatus, as: 'microserviceStatus', required: false + }, + { + model: MicroserviceExecStatus, + as: 'microserviceExecStatus', + required: false } ], where: where @@ -347,14 +440,26 @@ class MicroserviceManager extends BaseManager { model: Application, as: 'application', required: true, - include: [ - { - model: User, - as: 'user', - required: true, - attributes: ['id'] - } - ], + where: { + isSystem: false + }, + attributes: ['id'] + } + ], + where: where, + attributes: ['uuid'] + }, { transaction: transaction }) + } + findSystemMicroserviceOnGet (where, transaction) { + return Microservice.findOne({ + include: [ + { + model: Application, + as: 'application', + required: true, + where: { + isSystem: true + }, attributes: ['id'] } ], @@ -362,26 +467,87 @@ class MicroserviceManager extends BaseManager { attributes: ['uuid'] }, { transaction: transaction }) } - async findOneExcludeFields (where, transaction) { return Microservice.findOne({ + include: [ + { + model: Tags, + as: 'pubTags', + attributes: ['value'], + through: { attributes: [] } + }, + { + model: Tags, + as: 'subTags', + attributes: ['value'], + through: { attributes: [] } + } + ], where: where, attributes: { exclude: microserviceExcludedFields - } }, { - transaction: transaction - }) + } + }, { transaction: transaction }) } async findAllExcludeFields (where, transaction) { return Microservice.findAll({ + include: [ + { + model: Application, + as: 'application', + required: true, + where: { isSystem: false } + }, + { + model: Tags, + as: 'pubTags', + attributes: ['value'], + through: { attributes: [] } + }, + { + model: Tags, + as: 'subTags', + attributes: ['value'], + through: { attributes: [] } + } + ], where: where, - order: [ [ 'name', 'ASC' ] ], + order: [['name', 'ASC']], attributes: { exclude: microserviceExcludedFields - } }, { - transaction: transaction - }) + } + }, { transaction: transaction }) + } + + async findAllSystemExcludeFields (where, transaction) { + return Microservice.findAll({ + include: [ + { + model: Application, + as: 'application', + required: true, + where: { isSystem: true } + }, + { + model: Tags, + as: 'pubTags', + attributes: ['value'], + through: { attributes: [] } + }, + { + model: Tags, + as: 'subTags', + attributes: ['value'], + through: { attributes: [] } + } + ], + where: where, + order: [['name', 'ASC']], + attributes: { + exclude: microserviceExcludedFields + } + }, { transaction: transaction }) } findOneWithCategory (where, transaction) { diff --git a/src/data/managers/microservice-port-manager.js b/src/data/managers/microservice-port-manager.js index 7b6041604..84c7c22f4 100644 --- a/src/data/managers/microservice-port-manager.js +++ b/src/data/managers/microservice-port-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,43 +14,11 @@ const BaseManager = require('./base-manager') const models = require('../models') const MicroservicePort = models.MicroservicePort -const MicroservicePublicPort = models.MicroservicePublicPort -const MicroserviceProxyPort = models.MicroserviceProxyPort class MicroservicePortManager extends BaseManager { getEntity () { return MicroservicePort } - - findAllPublicPorts (transaction) { - return MicroservicePort.findAll({ - include: [ - { - model: MicroservicePublicPort, - as: 'publicPort', - required: true, - attributes: ['queueName', 'publicPort', 'protocol', 'isTcp', 'hostId'] - } - ], - where: { isPublic: true }, - attributes: ['microserviceUuid'] - }, { transaction: transaction }) - } - - findAllProxyPorts (transaction) { - return MicroservicePort.findAll({ - include: [ - { - model: MicroserviceProxyPort, - as: 'proxyPort', - required: true, - attributes: ['publicPort', 'protocol', 'host'] - } - ], - where: { isProxy: true }, - attributes: ['microserviceUuid'] - }, { transaction: transaction }) - } } const instance = new MicroservicePortManager() diff --git a/src/data/managers/microservice-proxy-port-manager.js b/src/data/managers/microservice-proxy-port-manager.js deleted file mode 100644 index c77ea068b..000000000 --- a/src/data/managers/microservice-proxy-port-manager.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('../models') -const MicroserviceProxyPort = models.MicroserviceProxyPort - -class MicroserviceProxyPortManager extends BaseManager { - getEntity () { - return MicroserviceProxyPort - } -} - -const instance = new MicroserviceProxyPortManager() -module.exports = instance diff --git a/src/data/managers/microservice-public-mode-manager.js b/src/data/managers/microservice-public-mode-manager.js deleted file mode 100644 index 2281a7f8d..000000000 --- a/src/data/managers/microservice-public-mode-manager.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('../models') -const MicroservicePublicMode = models.MicroservicePublicMode - -class MicroservicePublicModeManager extends BaseManager { - getEntity () { - return MicroservicePublicMode - } -} - -const instance = new MicroservicePublicModeManager() -module.exports = instance diff --git a/src/data/managers/microservice-public-port-manager.js b/src/data/managers/microservice-public-port-manager.js deleted file mode 100644 index 7a952a320..000000000 --- a/src/data/managers/microservice-public-port-manager.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('../models') -const MicroservicePublicPort = models.MicroservicePublicPort - -class MicroservicePublicPortManager extends BaseManager { - getEntity () { - return MicroservicePublicPort - } -} - -const instance = new MicroservicePublicPortManager() -module.exports = instance diff --git a/src/data/managers/microservice-status-manager.js b/src/data/managers/microservice-status-manager.js index 516db62c1..9690e21dd 100644 --- a/src/data/managers/microservice-status-manager.js +++ b/src/data/managers/microservice-status-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/router-connection-manager.js b/src/data/managers/router-connection-manager.js index 68306ca9d..57108ff44 100644 --- a/src/data/managers/router-connection-manager.js +++ b/src/data/managers/router-connection-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/router-manager.js b/src/data/managers/router-manager.js index 937c7025a..645d7b4dd 100644 --- a/src/data/managers/router-manager.js +++ b/src/data/managers/router-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/routing-manager.js b/src/data/managers/routing-manager.js index 8a5140481..1d82e1fce 100644 --- a/src/data/managers/routing-manager.js +++ b/src/data/managers/routing-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/scheduler-access-token-manager.js b/src/data/managers/scheduler-access-token-manager.js deleted file mode 100644 index 9c0b3fb5c..000000000 --- a/src/data/managers/scheduler-access-token-manager.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('../models') -const SchedulerAccessToken = models.SchedulerAccessToken - -class SchedulerAccessTokenManager extends BaseManager { - getEntity () { - return SchedulerAccessToken - } -} - -const instance = new SchedulerAccessTokenManager() -module.exports = instance diff --git a/src/data/managers/secret-manager.js b/src/data/managers/secret-manager.js new file mode 100644 index 000000000..6cca804a4 --- /dev/null +++ b/src/data/managers/secret-manager.js @@ -0,0 +1,57 @@ +const BaseManager = require('./base-manager') +const SecretHelper = require('../../helpers/secret-helper') +const models = require('../models') +const Secret = models.Secret + +class SecretManager extends BaseManager { + getEntity () { + return Secret + } + + async createSecret (name, type, data, transaction) { + // const encryptedData = await SecretHelper.encryptSecret(data, name) + return this.create({ + name, + type, + data: data + }, transaction) + } + + async updateSecret (name, data, transaction) { + const encryptedData = await SecretHelper.encryptSecret(data, name) + return this.update( + { name }, + { data: encryptedData }, + transaction + ) + } + + async getSecret (name, transaction) { + const secret = await this.findOne({ name }, transaction) + if (!secret) { + return null + } + // const decryptedData = await SecretHelper.decryptSecret(secret.data, name) + return { + ...secret.toJSON(), + data: secret.data + } + } + + async listSecrets (transaction) { + const secrets = await this.findAll({}, transaction) + return secrets.map(secret => ({ + id: secret.id, + name: secret.name, + type: secret.type, + created_at: secret.created_at, + updated_at: secret.updated_at + })) + } + + async deleteSecret (name, transaction) { + return this.delete({ name }, transaction) + } +} + +module.exports = new SecretManager() diff --git a/src/data/managers/service-manager.js b/src/data/managers/service-manager.js new file mode 100644 index 000000000..9942b36af --- /dev/null +++ b/src/data/managers/service-manager.js @@ -0,0 +1,90 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const Service = models.Service +const Tags = models.Tags +const ServiceTag = models.ServiceTag + +class ServiceManager extends BaseManager { + getEntity () { + return Service + } + + async findAllWithTags (where, transaction) { + return Service.findAll({ + where: where, + order: [ [ 'name', 'ASC' ] ], + include: [ + { model: Tags, + as: 'tags', + through: { + attributes: [] + } + } + ] + }, { + transaction: transaction + }) + } + + async findOneWithTags (where, transaction) { + return Service.findOne({ + where, + include: [ + { model: Tags, + as: 'tags', + through: { + attributes: [] + } + } + ] + }, { transaction }) + } + + async setTags (serviceId, tagIds, transaction) { + // First remove all existing tags + await ServiceTag.destroy({ + where: { service_id: serviceId } + }, { transaction }) + + // Then add new tags + if (tagIds && tagIds.length > 0) { + const serviceTags = tagIds.map(tagId => ({ + service_id: serviceId, + tag_id: tagId + })) + await ServiceTag.bulkCreate(serviceTags, { transaction }) + } + } + + async addTag (serviceId, tagId, transaction) { + await ServiceTag.create({ + service_id: serviceId, + tag_id: tagId + }, { transaction }) + } + + async removeTag (serviceId, tagId, transaction) { + await ServiceTag.destroy({ + where: { + service_id: serviceId, + tag_id: tagId + } + }, { transaction }) + } +} + +const instance = new ServiceManager() +module.exports = instance diff --git a/src/data/managers/strace-diagnostics-manager.js b/src/data/managers/strace-diagnostics-manager.js index fe04377de..9b2402bab 100644 --- a/src/data/managers/strace-diagnostics-manager.js +++ b/src/data/managers/strace-diagnostics-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/strace-manager.js b/src/data/managers/strace-manager.js index f590c577f..577cbac1a 100644 --- a/src/data/managers/strace-manager.js +++ b/src/data/managers/strace-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/tags-manager.js b/src/data/managers/tags-manager.js index 375646196..ebde21a18 100644 --- a/src/data/managers/tags-manager.js +++ b/src/data/managers/tags-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/usb-info-manager.js b/src/data/managers/usb-info-manager.js index ba99023ae..85f3a423b 100644 --- a/src/data/managers/usb-info-manager.js +++ b/src/data/managers/usb-info-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/user-manager.js b/src/data/managers/user-manager.js deleted file mode 100644 index 5fe41018d..000000000 --- a/src/data/managers/user-manager.js +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('../models') -const User = models.User -const AccessToken = models.AccessToken -const AppHelper = require('../../helpers/app-helper') - -class UserManager extends BaseManager { - getEntity () { - return User - } - - findByAccessToken (token, transaction) { - AppHelper.checkTransaction(transaction) - - return User.findOne({ - include: [{ - model: AccessToken, - as: 'accessToken', - where: { - token: token - } - }] - }, { - transaction: transaction - }) - } - - findByEmail (email) { - return User.findOne({ - where: { - email: email - } - }) - } - - // no transaction required here, used by auth decorator - checkAuthentication (token) { - return User.findOne({ - include: [{ - model: AccessToken, - as: 'accessToken', - where: { - token: token - } - }] - }) - } - - // no transaction required here, used by cli decorator - findById (id) { - return User.findOne({ where: { id: id } }) - } - - updateDetails (user, updateObject, transaction) { - return this.update({ - id: user.id - }, updateObject, transaction) - } - - updatePassword (userId, newPassword, transaction) { - return this.update({ - id: userId - }, { - password: newPassword - }, transaction) - } - - updateTempPassword (userId, tempPassword, transaction) { - return this.update({ - id: userId - }, { - tempPassword: tempPassword - }, transaction) - } -} - -const instance = new UserManager() -module.exports = instance diff --git a/src/data/managers/volume-mapping-manager.js b/src/data/managers/volume-mapping-manager.js index c6d8ecb3a..a9e77ff71 100644 --- a/src/data/managers/volume-mapping-manager.js +++ b/src/data/managers/volume-mapping-manager.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/data/managers/volume-mounting-manager.js b/src/data/managers/volume-mounting-manager.js new file mode 100644 index 000000000..b4f443842 --- /dev/null +++ b/src/data/managers/volume-mounting-manager.js @@ -0,0 +1,62 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('../models') +const VolumeMount = models.VolumeMount + +const volumeMountExcludedFields = [ + 'created_at', + 'updated_at' +] + +class VolumeMountingManager extends BaseManager { + getEntity () { + return VolumeMount + } + + getAllExcludeFields (where, transaction) { + return this.findAllWithAttributes(where, { exclude: volumeMountExcludedFields }, transaction) + } + + getAll (where, transaction) { + return VolumeMount.findAll({ + where: where, + attributes: ['uuid', 'name', 'configMapName', 'secretName'] + }, { transaction: transaction }) + } + + getOne (where, transaction) { + return VolumeMount.findOne({ + where: where, + attributes: ['uuid', 'name', 'configMapName', 'secretName', 'version'] + }, { transaction: transaction }) + } + + findOne (where, transaction) { + return VolumeMount.findOne({ + where: where, + attributes: ['uuid', 'name', 'configMapName', 'secretName', 'version'] + }, { transaction: transaction }) + } + + findAll (where, transaction) { + return VolumeMount.findAll({ + where: where, + attributes: ['uuid', 'name', 'configMapName', 'secretName', 'version'] + }, { transaction: transaction }) + } +} + +const instance = new VolumeMountingManager() +module.exports = instance diff --git a/src/data/migrations/20180930155645-create-user.js b/src/data/migrations/20180930155645-create-user.js deleted file mode 100644 index 6a1a686d9..000000000 --- a/src/data/migrations/20180930155645-create-user.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Users', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: Sequelize.INTEGER, - field: 'id' - }, - firstName: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'first_name', - defaultValue: '' - }, - lastName: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'last_name', - defaultValue: '' - }, - email: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'email', - defaultValue: '' - }, - password: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'password' - }, - tempPassword: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'temp_password' - }, - emailActivated: { - type: Sequelize.BOOLEAN, - field: 'email_activated', - defaultValue: false - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Users') - } -} diff --git a/src/data/migrations/20180930164635-create-flow.js b/src/data/migrations/20180930164635-create-flow.js deleted file mode 100644 index acdaaf6e2..000000000 --- a/src/data/migrations/20180930164635-create-flow.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Flows', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - - }, - name: { - type: Sequelize.TEXT, - field: 'name', - defaultValue: 'New Application' - }, - description: { - type: Sequelize.TEXT, - field: 'description', - defaultValue: '' - }, - isActivated: { - type: Sequelize.BOOLEAN, - field: 'is_activated', - defaultValue: false - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Flows') - } -} diff --git a/src/data/migrations/20180930173823-create-registry.js b/src/data/migrations/20180930173823-create-registry.js deleted file mode 100644 index 9e8fe7cf7..000000000 --- a/src/data/migrations/20180930173823-create-registry.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Registries', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - url: { - type: Sequelize.TEXT, - field: 'url' - }, - isPublic: { - type: Sequelize.BOOLEAN, - field: 'is_public' - }, - secure: { - type: Sequelize.BOOLEAN, - field: 'secure' - }, - certificate: { - type: Sequelize.TEXT, - field: 'certificate' - }, - requiresCert: { - type: Sequelize.BOOLEAN, - field: 'requires_cert' - }, - username: { - type: Sequelize.TEXT, - field: 'user_name' - }, - password: { - type: Sequelize.TEXT, - field: 'password' - }, - userEmail: { - type: Sequelize.TEXT, - field: 'user_email' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Registries') - } -} diff --git a/src/data/migrations/20180930184436-create-catalog-item.js b/src/data/migrations/20180930184436-create-catalog-item.js deleted file mode 100644 index cb7624b99..000000000 --- a/src/data/migrations/20180930184436-create-catalog-item.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('CatalogItems', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - name: { - type: Sequelize.TEXT, - field: 'name', - defaultValue: 'New Catalog Item' - }, - description: { - type: Sequelize.TEXT, - field: 'description', - defaultValue: '' - }, - category: { - type: Sequelize.TEXT, - field: 'category' - }, - configExample: { - type: Sequelize.TEXT, - field: 'config_example', - defaultValue: '{}' - }, - publisher: { - type: Sequelize.TEXT, - field: 'publisher' - }, - diskRequired: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('diskRequired')) - }, - field: 'disk_required', - defaultValue: 0 - }, - ramRequired: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('ramRequired')) - }, - field: 'ram_required', - defaultValue: 0 - }, - picture: { - type: Sequelize.TEXT, - field: 'picture', - defaultValue: 'images/shared/default.png' - }, - isPublic: { - type: Sequelize.BOOLEAN, - field: 'is_public', - defaultValue: false - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - }, - registryId: { - type: Sequelize.INTEGER, - field: 'registry_id', - as: 'registryId', - references: { model: 'Registries', key: 'id' }, - onDelete: 'set null', - defaultValue: 1 - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('CatalogItems') - } -} diff --git a/src/data/migrations/20180930184703-create-fog-type.js b/src/data/migrations/20180930184703-create-fog-type.js deleted file mode 100644 index 8a9607648..000000000 --- a/src/data/migrations/20180930184703-create-fog-type.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('FogTypes', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - name: { - type: Sequelize.TEXT, - field: 'name' - }, - image: { - type: Sequelize.TEXT, - field: 'image' - }, - description: { - type: Sequelize.TEXT, - field: 'description' - }, - networkCatalogItemId: { - type: Sequelize.INTEGER, - field: 'network_catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - }, - halCatalogItemId: { - type: Sequelize.INTEGER, - field: 'hal_catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - }, - bluetoothCatalogItemId: { - type: Sequelize.INTEGER, - field: 'bluetooth_catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('FogTypes') - } -} diff --git a/src/data/migrations/20180930184921-create-catalog-item-image.js b/src/data/migrations/20180930184921-create-catalog-item-image.js deleted file mode 100644 index b69109136..000000000 --- a/src/data/migrations/20180930184921-create-catalog-item-image.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('CatalogItemImages', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - containerImage: { - type: Sequelize.TEXT, - field: 'container_image' - }, - catalogItemId: { - type: Sequelize.INTEGER, - field: 'catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - }, - fogTypeId: { - type: Sequelize.INTEGER, - field: 'fog_type_id', - references: { model: 'FogTypes', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('CatalogItemImages') - } -} diff --git a/src/data/migrations/20180930194506-create-catalog-item-input-type.js b/src/data/migrations/20180930194506-create-catalog-item-input-type.js deleted file mode 100644 index 0606561b2..000000000 --- a/src/data/migrations/20180930194506-create-catalog-item-input-type.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('CatalogItemInputTypes', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - infoType: { - type: Sequelize.TEXT, - field: 'info_type' - }, - infoFormat: { - type: Sequelize.TEXT, - field: 'info_format' - }, - catalogItemId: { - type: Sequelize.INTEGER, - field: 'catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('CatalogItemInputTypes') - } -} diff --git a/src/data/migrations/20180930195746-create-catalog-item-output-type.js b/src/data/migrations/20180930195746-create-catalog-item-output-type.js deleted file mode 100644 index 5b1a8d0ee..000000000 --- a/src/data/migrations/20180930195746-create-catalog-item-output-type.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('CatalogItemOutputTypes', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - infoType: { - type: Sequelize.TEXT, - field: 'info_type' - }, - infoFormat: { - type: Sequelize.TEXT, - field: 'info_format' - }, - catalogItemId: { - type: Sequelize.INTEGER, - field: 'catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('CatalogItemOutputTypes') - } -} diff --git a/src/data/migrations/20180930204039-create-email-activation-code.js b/src/data/migrations/20180930204039-create-email-activation-code.js deleted file mode 100644 index 8516ef13b..000000000 --- a/src/data/migrations/20180930204039-create-email-activation-code.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('EmailActivationCodes', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - activationCode: { - type: Sequelize.TEXT, - field: 'activation_code' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('EmailActivationCodes') - } -} diff --git a/src/data/migrations/20180930225403-create-fog.js b/src/data/migrations/20180930225403-create-fog.js deleted file mode 100644 index 1015056f8..000000000 --- a/src/data/migrations/20180930225403-create-fog.js +++ /dev/null @@ -1,277 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Fogs', { - uuid: { - type: Sequelize.STRING(32), - primaryKey: true, - allowNull: false, - field: 'uuid' - }, - name: { - type: Sequelize.TEXT, - defaultValue: 'Unnamed ioFog 1', - field: 'name' - }, - location: { - type: Sequelize.TEXT, - field: 'location' - }, - gpsMode: { - type: Sequelize.TEXT, - field: 'gps_mode' - }, - latitude: { - type: Sequelize.FLOAT, - field: 'latitude' - }, - longitude: { - type: Sequelize.FLOAT, - field: 'longitude' - }, - description: { - type: Sequelize.TEXT, - field: 'description' - }, - lastactive: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('lastactive')) - }, - field: 'last_active' - }, - daemonStatus: { - type: Sequelize.TEXT, - defaultValue: 'UNKNOWN', - field: 'daemon_status' - }, - daemonOperatingDuration: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('daemonOperatingDuration')) - }, - defaultValue: 0, - field: 'daemon_operating_duration' - }, - daemonLastStart: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('daemonLastStart')) - }, - field: 'daemon_last_start' - }, - memoryUsage: { - type: Sequelize.FLOAT, - defaultValue: 0.000, - field: 'memory_usage' - }, - diskUsage: { - type: Sequelize.FLOAT, - defaultValue: 0.000, - field: 'disk_usage' - }, - cpuUsage: { - type: Sequelize.FLOAT, - defaultValue: 0.00, - field: 'cpu_usage' - }, - memoryViolation: { - type: Sequelize.TEXT, - field: 'memory_violation' - }, - diskViolation: { - type: Sequelize.TEXT, - field: 'disk_violation' - }, - cpuViolation: { - type: Sequelize.TEXT, - field: 'cpu_violation' - }, - catalogItemStatus: { - type: Sequelize.TEXT, - field: 'catalog_item_status' - }, - repositoryCount: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('repositoryCount')) - }, - field: 'repository_count' - }, - repositoryStatus: { - type: Sequelize.TEXT, - field: 'repository_status' - }, - systemTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('systemTime')) - }, - field: 'system_time' - }, - lastStatusTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('lastStatusTime')) - }, - field: 'last_status_time' - }, - ipAddress: { - type: Sequelize.TEXT, - defaultValue: '0.0.0.0', - field: 'ip_address' - }, - processedMessages: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('processedMessages')) - }, - defaultValue: 0, - field: 'processed_messages' - }, - catalogItemMessageCounts: { - type: Sequelize.TEXT, - field: 'catalog_item_message_counts' - }, - messageSpeed: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('messageSpeed')) - }, - field: 'message_speed' - }, - lastCommandTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('lastCommandTime')) - }, - field: 'last_command_time' - }, - networkInterface: { - type: Sequelize.TEXT, - defaultValue: 'eth0', - field: 'network_interface' - }, - dockerUrl: { - type: Sequelize.TEXT, - defaultValue: 'unix:///var/run/docker.sock', - field: 'docker_url' - }, - diskLimit: { - type: Sequelize.FLOAT, - defaultValue: 50, - field: 'disk_limit' - }, - diskDirectory: { - type: Sequelize.TEXT, - defaultValue: '/var/lib/iofog/', - field: 'disk_directory' - }, - memoryLimit: { - type: Sequelize.FLOAT, - defaultValue: 4096, - field: 'memory_limit' - }, - cpuLimit: { - type: Sequelize.FLOAT, - defaultValue: 80, - field: 'cpu_limit' - }, - logLimit: { - type: Sequelize.FLOAT, - defaultValue: 10, - field: 'log_limit' - }, - logDirectory: { - type: Sequelize.TEXT, - defaultValue: '/var/log/iofog/', - field: 'log_directory' - }, - bluetoothEnabled: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'bluetooth' - }, - abstractedHardwareEnabled: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'hal' - }, - logFileCount: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('logFileCount')) - }, - defaultValue: 10, - field: 'log_file_count' - }, - version: { - type: Sequelize.TEXT, - field: 'version' - }, - isReadyToUpgrade: { - type: Sequelize.BOOLEAN, - defaultValue: true, - field: 'is_ready_to_upgrade' - }, - isReadyToRollback: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'is_ready_to_rollback' - }, - statusFrequency: { - type: Sequelize.INTEGER, - defaultValue: 10, - field: 'status_frequency' - }, - changeFrequency: { - type: Sequelize.INTEGER, - defaultValue: 20, - field: 'change_frequency' - }, - deviceScanFrequency: { - type: Sequelize.INTEGER, - defaultValue: 20, - field: 'device_scan_frequency' - }, - tunnel: { - type: Sequelize.TEXT, - defaultValue: '', - field: 'tunnel' - }, - watchdogEnabled: { - type: Sequelize.BOOLEAN, - defaultValue: true, - field: 'isolated_docker_container' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - }, - fogTypeId: { - type: Sequelize.INTEGER, - field: 'fog_type_id', - references: { model: 'FogTypes', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Fogs') - } -} diff --git a/src/data/migrations/20180930225846-create-change-tracking.js b/src/data/migrations/20180930225846-create-change-tracking.js deleted file mode 100644 index 052254c93..000000000 --- a/src/data/migrations/20180930225846-create-change-tracking.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('ChangeTrackings', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - containerConfig: { - type: Sequelize.BOOLEAN, - field: 'container_config' - }, - reboot: { - type: Sequelize.BOOLEAN, - field: 'reboot' - }, - deletenode: { - type: Sequelize.BOOLEAN, - field: 'deletenode' - }, - version: { - type: Sequelize.BOOLEAN, - field: 'version' - }, - containerList: { - type: Sequelize.BOOLEAN, - field: 'container_list' - }, - config: { - type: Sequelize.BOOLEAN, - field: 'config' - }, - routing: { - type: Sequelize.BOOLEAN, - field: 'routing' - }, - registries: { - type: Sequelize.BOOLEAN, - field: 'registries' - }, - tunnel: { - type: Sequelize.BOOLEAN, - field: 'tunnel' - }, - diagnostics: { - type: Sequelize.BOOLEAN, - field: 'diagnostics' - }, - isImageSnapshot: { - type: Sequelize.BOOLEAN, - field: 'image_snapshot' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('ChangeTrackings') - } -} diff --git a/src/data/migrations/20180930230219-create-fog-access-token.js b/src/data/migrations/20180930230219-create-fog-access-token.js deleted file mode 100644 index 269768d74..000000000 --- a/src/data/migrations/20180930230219-create-fog-access-token.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('FogAccessTokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: Sequelize.TEXT, - field: 'token' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('FogAccessTokens') - } -} diff --git a/src/data/migrations/20180930230626-create-fog-provision-key.js b/src/data/migrations/20180930230626-create-fog-provision-key.js deleted file mode 100644 index 56efd0cfc..000000000 --- a/src/data/migrations/20180930230626-create-fog-provision-key.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('FogProvisionKeys', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - provisionKey: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'provisioning_string' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('FogProvisionKeys') - } -} diff --git a/src/data/migrations/20180930231241-create-fog-version-command.js b/src/data/migrations/20180930231241-create-fog-version-command.js deleted file mode 100644 index 0b39564fc..000000000 --- a/src/data/migrations/20180930231241-create-fog-version-command.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('FogVersionCommands', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - field: 'id' - }, - versionCommand: { - /* eslint-disable new-cap */ - type: Sequelize.STRING(100), - field: 'version_command' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('FogVersionCommands') - } -} diff --git a/src/data/migrations/20180930231536-create-hw-info.js b/src/data/migrations/20180930231536-create-hw-info.js deleted file mode 100644 index ed863f076..000000000 --- a/src/data/migrations/20180930231536-create-hw-info.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('HWInfos', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - info: { - type: Sequelize.TEXT, - defaultValue: ' ', - field: 'info' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('HWInfos') - } -} diff --git a/src/data/migrations/20180930232242-create-usb-info.js b/src/data/migrations/20180930232242-create-usb-info.js deleted file mode 100644 index 354e6fb2d..000000000 --- a/src/data/migrations/20180930232242-create-usb-info.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('USBInfos', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - info: { - type: Sequelize.TEXT, - defaultValue: ' ', - field: 'info' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('USBInfos') - } -} diff --git a/src/data/migrations/20180930232508-create-tunnel.js b/src/data/migrations/20180930232508-create-tunnel.js deleted file mode 100644 index eda029e25..000000000 --- a/src/data/migrations/20180930232508-create-tunnel.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Tunnels', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - username: { - type: Sequelize.TEXT, - field: 'username' - }, - password: { - type: Sequelize.TEXT, - field: 'password' - }, - host: { - type: Sequelize.TEXT, - field: 'host' - }, - rport: { - type: Sequelize.INTEGER, - field: 'remote_port' - }, - lport: { - type: Sequelize.INTEGER, - defaultValue: 22, - field: 'local_port' - }, - rsakey: { - type: Sequelize.TEXT, - field: 'rsa_key' - }, - closed: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'closed' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Tunnels') - } -} diff --git a/src/data/migrations/20181001062956-create-microservice.js b/src/data/migrations/20181001062956-create-microservice.js deleted file mode 100644 index 7e64e19e7..000000000 --- a/src/data/migrations/20181001062956-create-microservice.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Microservices', { - uuid: { - type: Sequelize.STRING(32), - primaryKey: true, - allowNull: false, - field: 'uuid' - }, - config: { - type: Sequelize.TEXT, - field: 'config' - }, - name: { - type: Sequelize.TEXT, - field: 'name' - }, - configLastUpdated: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('configLastUpdated')) - }, - field: 'config_last_updated' - }, - isNetwork: { - type: Sequelize.BOOLEAN, - field: 'is_network' - }, - needUpdate: { - type: Sequelize.BOOLEAN, - field: 'need_update' - }, - rebuild: { - type: Sequelize.BOOLEAN, - field: 'rebuild' - }, - rootHostAccess: { - type: Sequelize.BOOLEAN, - field: 'root_host_access' - }, - logSize: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('logSize')) - }, - field: 'log_size' - }, - imageSnapshot: { - type: Sequelize.TEXT, - field: 'image_snapshot' - }, - deleteWithCleanup: { - type: Sequelize.BOOLEAN, - field: 'delete_with_cleanup' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'set null' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - }, - catalogItemId: { - type: Sequelize.INTEGER, - field: 'catalog_item_id', - references: { model: 'CatalogItems', key: 'id' }, - onDelete: 'cascade' - }, - registryId: { - type: Sequelize.INTEGER, - field: 'registry_id', - references: { model: 'Registries', key: 'id' }, - onDelete: 'cascade', - defaultValue: 1 - }, - flowId: { - type: Sequelize.INTEGER, - field: 'flow_id', - references: { model: 'Flows', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Microservices') - } -} diff --git a/src/data/migrations/20181001070828-create-microservice-port.js b/src/data/migrations/20181001070828-create-microservice-port.js deleted file mode 100644 index 69190d27b..000000000 --- a/src/data/migrations/20181001070828-create-microservice-port.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroservicePorts', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - portInternal: { - type: Sequelize.INTEGER, - field: 'port_internal' - }, - portExternal: { - type: Sequelize.INTEGER, - field: 'port_external' - }, - isPublic: { - type: Sequelize.BOOLEAN, - field: 'is_public' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroservicePorts') - } -} diff --git a/src/data/migrations/20181001071315-create-microservice-status.js b/src/data/migrations/20181001071315-create-microservice-status.js deleted file mode 100644 index 22be3cde8..000000000 --- a/src/data/migrations/20181001071315-create-microservice-status.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroserviceStatuses', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - status: { - type: Sequelize.TEXT, - field: 'status' - }, - cpuUsage: { - type: Sequelize.FLOAT, - defaultValue: 0.000, - field: 'cpu_usage' - }, - memoryUsage: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('memoryUsage')) - }, - defaultValue: 0, - field: 'memory_usage' - }, - containerId: { - type: Sequelize.TEXT, - field: 'container_id' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroserviceStatuses') - } -} diff --git a/src/data/migrations/20181001071628-create-connector.js b/src/data/migrations/20181001071628-create-connector.js deleted file mode 100644 index 3815516d4..000000000 --- a/src/data/migrations/20181001071628-create-connector.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Connectors', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - name: { - type: Sequelize.TEXT, - field: 'name' - }, - domain: { - type: Sequelize.TEXT, - field: 'domain' - }, - publicIp: { - type: Sequelize.TEXT, - field: 'public_ip' - }, - cert: { - type: Sequelize.TEXT, - field: 'cert' - }, - selfSignedCerts: { - type: Sequelize.BOOLEAN, - field: 'self_signed_certs' - }, - devMode: { - type: Sequelize.BOOLEAN, - field: 'dev_mode' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Connectors') - } -} diff --git a/src/data/migrations/20181001071858-create-connector-port.js b/src/data/migrations/20181001071858-create-connector-port.js deleted file mode 100644 index a3c847bc8..000000000 --- a/src/data/migrations/20181001071858-create-connector-port.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('ConnectorPorts', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - port1: { - type: Sequelize.INTEGER, - field: 'port1' - }, - port2: { - type: Sequelize.INTEGER, - field: 'port2' - }, - maxConnectionsPort1: { - type: Sequelize.INTEGER, - field: 'max_connections_port1' - }, - maxConnectionsPort2: { - type: Sequelize.INTEGER, - field: 'max_connection_port2' - }, - passcodePort1: { - type: Sequelize.TEXT, - field: 'passcode_port1' - }, - passcodePort2: { - type: Sequelize.TEXT, - field: 'passcode_port2' - }, - heartBeatAbsenceThresholdPort1: { - type: Sequelize.INTEGER, - field: 'heartbeat_absence_threshold_port1' - }, - heartBeatAbsenceThresholdPort2: { - type: Sequelize.INTEGER, - field: 'heartbeat_absence_threshold_port2' - }, - mappingId: { - type: Sequelize.TEXT, - field: 'mapping_id' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - connectorId: { - type: Sequelize.INTEGER, - field: 'connector_id', - references: { model: 'Connectors', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('ConnectorPorts') - } -} diff --git a/src/data/migrations/20181001073429-create-strace-diagnostics.js b/src/data/migrations/20181001073429-create-strace-diagnostics.js deleted file mode 100644 index 9683f253e..000000000 --- a/src/data/migrations/20181001073429-create-strace-diagnostics.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('StraceDiagnostics', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - straceRun: { - type: Sequelize.BOOLEAN, - field: 'strace_run' - }, - buffer: { - type: Sequelize.TEXT, - field: 'buffer', - defaultValue: '' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('StraceDiagnostics') - } -} diff --git a/src/data/migrations/20181003102815-create-volume-mapping.js b/src/data/migrations/20181003102815-create-volume-mapping.js deleted file mode 100644 index 2d66e698f..000000000 --- a/src/data/migrations/20181003102815-create-volume-mapping.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('VolumeMappings', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'uuid' - }, - hostDestination: { - type: Sequelize.TEXT, - field: 'host_destination' - }, - containerDestination: { - type: Sequelize.TEXT, - field: 'container_destination' - }, - accessMode: { - type: Sequelize.TEXT, - field: 'access_mode' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('VolumeMappings') - } -} diff --git a/src/data/migrations/20181003104606-create-access-token.js b/src/data/migrations/20181003104606-create-access-token.js deleted file mode 100644 index 1decdeba5..000000000 --- a/src/data/migrations/20181003104606-create-access-token.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('AccessTokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - token: { - type: Sequelize.STRING, - field: 'token' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('AccessTokens') - } -} diff --git a/src/data/migrations/20181022110529-create-routing.js b/src/data/migrations/20181022110529-create-routing.js deleted file mode 100644 index 0c78a27f9..000000000 --- a/src/data/migrations/20181022110529-create-routing.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Routings', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - isNetworkConnection: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'is_network_connection' - }, - sourceMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'source_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - destMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'dest_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - sourceNetworkMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'source_network_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'set null' - }, - destNetworkMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'dest_network_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'set null' - }, - sourceIofogUuid: { - type: Sequelize.STRING(32), - field: 'source_iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'set null' - }, - destIofogUuid: { - type: Sequelize.STRING(32), - field: 'dest_iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'set null' - }, - connectorPortId: { - type: Sequelize.INTEGER, - field: 'connector_port_id', - references: { model: 'ConnectorPorts', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Routings') - } -} diff --git a/src/data/migrations/20181022114905-create-microservice-public-mode.js b/src/data/migrations/20181022114905-create-microservice-public-mode.js deleted file mode 100644 index 2ee2f95d4..000000000 --- a/src/data/migrations/20181022114905-create-microservice-public-mode.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroservicePublicModes', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - networkMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'network_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'set null' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'set null' - }, - microservicePortId: { - type: Sequelize.INTEGER, - field: 'microservice_port_id', - references: { model: 'MicroservicePorts', key: 'id' }, - onDelete: 'set null' - }, - connectorPortId: { - type: Sequelize.INTEGER, - field: 'connector_port_id', - references: { model: 'ConnectorPorts', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroservicePublicModes') - } -} diff --git a/src/data/migrations/20181031094923-drop-need-update-col-microservices.js b/src/data/migrations/20181031094923-drop-need-update-col-microservices.js deleted file mode 100644 index 5c14f7ab0..000000000 --- a/src/data/migrations/20181031094923-drop-need-update-col-microservices.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Microservices', 'need_update') - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Microservices', - 'need_update', - Sequelize.BOOLEAN - ) - } -} diff --git a/src/data/migrations/20181102105758-microservice-status-add-missing-time-cols.js b/src/data/migrations/20181102105758-microservice-status-add-missing-time-cols.js deleted file mode 100644 index 2973c2f9b..000000000 --- a/src/data/migrations/20181102105758-microservice-status-add-missing-time-cols.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroserviceStatuses', - 'operating_duration', - Sequelize.BIGINT - ) - .then(() => queryInterface.addColumn('MicroserviceStatuses', - 'start_time', - Sequelize.BIGINT)) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('MicroserviceStatuses', 'operating_duration') - .then(() => queryInterface.removeColumn('MicroserviceStatuses', 'start_time')) - } -} diff --git a/src/data/migrations/20181102163657-microservice-add-col-remove.js b/src/data/migrations/20181102163657-microservice-add-col-remove.js deleted file mode 100644 index 4bf5679e8..000000000 --- a/src/data/migrations/20181102163657-microservice-add-col-remove.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Microservices', - 'delete', - Sequelize.BOOLEAN - ) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Microservices', 'delete') - } -} diff --git a/src/data/migrations/20181105120036-change-tracking-container-to-microservice-renaming.js b/src/data/migrations/20181105120036-change-tracking-container-to-microservice-renaming.js deleted file mode 100644 index 285a4caf8..000000000 --- a/src/data/migrations/20181105120036-change-tracking-container-to-microservice-renaming.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('ChangeTrackings', 'container_config', 'microservice_config') - .then(() => { - return queryInterface.renameColumn('ChangeTrackings', 'container_list', 'microservice_list') - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('ChangeTrackings', 'microservice_config', 'container_config') - .then(() => { - return queryInterface.renameColumn('ChangeTrackings', 'microservice_list', 'container_list') - }) - } -} diff --git a/src/data/migrations/20181109132609-microservice-rename-updatedBy-to-userId.js b/src/data/migrations/20181109132609-microservice-rename-updatedBy-to-userId.js deleted file mode 100644 index daa76ad5a..000000000 --- a/src/data/migrations/20181109132609-microservice-rename-updatedBy-to-userId.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('Microservices', 'updated_by', 'user_id') - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('Microservices', 'user_id', 'updated_by') - } -} diff --git a/src/data/migrations/20181109132704-microservice-port-rename-updatedBy-to-userId.js b/src/data/migrations/20181109132704-microservice-port-rename-updatedBy-to-userId.js deleted file mode 100644 index f033334fe..000000000 --- a/src/data/migrations/20181109132704-microservice-port-rename-updatedBy-to-userId.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('MicroservicePorts', 'updated_by', 'user_id') - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('MicroservicePorts', 'user_id', 'updated_by') - } -} diff --git a/src/data/migrations/20181109132723-flow-remove-updatedBy.js b/src/data/migrations/20181109132723-flow-remove-updatedBy.js deleted file mode 100644 index b51994b45..000000000 --- a/src/data/migrations/20181109132723-flow-remove-updatedBy.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Flows', 'updated_by') - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Flows', - 'updated_by', - Sequelize.INTEGER - ) - } -} diff --git a/src/data/migrations/20181113094807-add-missing-constraints.js b/src/data/migrations/20181113094807-add-missing-constraints.js deleted file mode 100644 index 36e06acb9..000000000 --- a/src/data/migrations/20181113094807-add-missing-constraints.js +++ /dev/null @@ -1,133 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addConstraint('Flows', { - fields: ['user_id'], - type: 'foreign key', - name: 'flows_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - .then(() => { - return queryInterface.addConstraint('Microservices', { - fields: ['user_id'], - type: 'foreign key', - name: 'mscv_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - .then(() => { - return queryInterface.addConstraint('Microservices', { - fields: ['iofog_uuid'], - type: 'foreign key', - name: 'msvc_fogs_fkey_constraint', - references: { - table: 'Fogs', - field: 'uuid' - }, - onDelete: 'cascade' - }) - }) - .then(() => { - return queryInterface.addConstraint('Microservices', { - fields: ['catalog_item_id'], - type: 'foreign key', - name: 'msvc_catalogItems_fkey_constraint', - references: { - table: 'CatalogItems', - field: 'id' - }, - onDelete: 'cascade' - }) - }) - .then(() => { - return queryInterface.addConstraint('Microservices', { - fields: ['registry_id'], - type: 'foreign key', - name: 'msvc_registries_fkey_constraint', - references: { - table: 'Registries', - field: 'id' - }, - onDelete: 'cascade' - }) - }) - .then(() => { - return queryInterface.addConstraint('Microservices', { - fields: ['flow_id'], - type: 'foreign key', - name: 'msvc_flows_fkey_constraint', - references: { - table: 'Flows', - field: 'id' - }, - onDelete: 'cascade' - }) - }) - .then(() => { - return queryInterface.addConstraint('ChangeTrackings', { - fields: ['iofog_uuid'], - type: 'foreign key', - name: 'changetracking_fogs_fkey_constraint', - references: { - table: 'Fogs', - field: 'uuid' - }, - onDelete: 'cascade' - }) - }) - }) - .then(() => { - return queryInterface.addConstraint('MicroservicePorts', { - fields: ['user_id'], - type: 'foreign key', - name: 'msvcports_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - }) - .then(() => { - return queryInterface.addConstraint('MicroservicePorts', { - fields: ['microservice_uuid'], - type: 'foreign key', - name: 'mcsvcports_msvc_fkey_constraint', - references: { - table: 'Microservices', - field: 'uuid' - }, - onDelete: 'cascade' - }) - }) - }, - - down: - (queryInterface, Sequelize) => { - // for SQLite - return queryInterface.renameColumn('Flows', 'user_id', 'user_id1') - .then(() => { - return queryInterface.renameColumn('Flows', 'user_id1', 'user_id') - }).then(() => { - return queryInterface.renameColumn('Microservices', 'user_id', 'user_id1') - }).then(() => { - return queryInterface.renameColumn('Microservices', 'user_id1', 'user_id') - }).then(() => { - return queryInterface.renameColumn('ChangeTrackings', 'iofog_uuid', 'iofog_uuid1') - }).then(() => { - return queryInterface.renameColumn('ChangeTrackings', 'iofog_uuid1', 'iofog_uuid') - }).then(() => { - return queryInterface.renameColumn('MicroservicePorts', 'user_id', 'user_id1') - }).then(() => { - return queryInterface.renameColumn('MicroservicePorts', 'user_id1', 'user_id') - }) - } -} diff --git a/src/data/migrations/20190117110458-create-tracking-event.js b/src/data/migrations/20190117110458-create-tracking-event.js deleted file mode 100644 index 88d69bf42..000000000 --- a/src/data/migrations/20190117110458-create-tracking-event.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('TrackingEvents', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - uuid: { - type: Sequelize.TEXT, - allowNull: false, - field: 'uuid' - }, - sourceType: { - type: Sequelize.TEXT, - field: 'source_type' - }, - timestamp: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('timestamp')) - }, - field: 'timestamp' - }, - type: { - type: Sequelize.TEXT, - field: 'type' - }, - data: { - type: Sequelize.TEXT, - field: 'data' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('TrackingEvents') - } -} diff --git a/src/data/migrations/20190222135632-create-kubelet-access-token.js b/src/data/migrations/20190222135632-create-kubelet-access-token.js deleted file mode 100644 index cf62a9332..000000000 --- a/src/data/migrations/20190222135632-create-kubelet-access-token.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('KubeletAccessTokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: Sequelize.TEXT, - field: 'token' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('KubeletAccessTokens') - } -} diff --git a/src/data/migrations/20190226111416-add-system-metrics-to-fog.js b/src/data/migrations/20190226111416-add-system-metrics-to-fog.js deleted file mode 100644 index f160f9142..000000000 --- a/src/data/migrations/20190226111416-add-system-metrics-to-fog.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Fogs', - 'system-available-memory', Sequelize.BIGINT - ).then(() => { - return queryInterface.addColumn('Fogs', - 'system-available-disk', Sequelize.BIGINT - ) - }).then(() => { - return queryInterface.addColumn('Fogs', - 'system-total-cpu', Sequelize.FLOAT - ) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Fogs', 'system-available-memory') - .then(() => { - return queryInterface.removeColumn('Fogs', 'system-available-disk') - }).then(() => { - return queryInterface.removeColumn('Fogs', 'system-total-cpu') - }) - // restore constraints. Because Sequelize has problem with Sqlite constraints - .then(() => { - return queryInterface.addConstraint('Fogs', { - fields: ['user_id'], - type: 'foreign key', - name: 'fogs_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - }).then(() => { - return queryInterface.addConstraint('Fogs', { - fields: ['fog_type_id'], - type: 'foreign key', - name: 'fogs_types_fkey_constraint', - references: { - table: 'FogTypes', - field: 'id' - }, - onDelete: 'set null' - }) - }) - } -} diff --git a/src/data/migrations/20190227154512-create-scheduler-access-token.js b/src/data/migrations/20190227154512-create-scheduler-access-token.js deleted file mode 100644 index ebe7e2c8b..000000000 --- a/src/data/migrations/20190227154512-create-scheduler-access-token.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('SchedulerAccessTokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: Sequelize.TEXT, - field: 'token' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('SchedulerAccessTokens') - } -} diff --git a/src/data/migrations/20190305142715-add-security-status-fields.js b/src/data/migrations/20190305142715-add-security-status-fields.js deleted file mode 100644 index 66c1262f0..000000000 --- a/src/data/migrations/20190305142715-add-security-status-fields.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Fogs', - 'security_status', Sequelize.TEXT - ).then(() => { - return queryInterface.addColumn('Fogs', - 'security_violation_info', Sequelize.TEXT - ) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Fogs', 'security_status') - .then(() => { - return queryInterface.removeColumn('Fogs', 'security_violation_info') - }) - // restore constraints. Because Sequelize has problem with Sqlite constraints - .then(() => { - return queryInterface.addConstraint('Fogs', { - fields: ['user_id'], - type: 'foreign key', - name: 'fogs_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - }).then(() => { - return queryInterface.addConstraint('Fogs', { - fields: ['fog_type_id'], - type: 'foreign key', - name: 'fogs_types_fkey_constraint', - references: { - table: 'FogTypes', - field: 'id' - }, - onDelete: 'set null' - }) - }) - } -} diff --git a/src/data/migrations/20190409055710-add-microservices-env.js b/src/data/migrations/20190409055710-add-microservices-env.js deleted file mode 100644 index 121e18949..000000000 --- a/src/data/migrations/20190409055710-add-microservices-env.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroserviceEnvs', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - key: { - type: Sequelize.TEXT, - field: 'key' - }, - value: { - type: Sequelize.TEXT, - field: 'value' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroserviceEnvs') - } -} diff --git a/src/data/migrations/20190410055710-add-microservices-arg.js b/src/data/migrations/20190410055710-add-microservices-arg.js deleted file mode 100644 index 37b1b2c47..000000000 --- a/src/data/migrations/20190410055710-add-microservices-arg.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroserviceArgs', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - cmd: { - type: Sequelize.TEXT, - field: 'cmd' - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroserviceArgs') - } -} diff --git a/src/data/migrations/20190704043715-agent-external-ip.js b/src/data/migrations/20190704043715-agent-external-ip.js deleted file mode 100644 index 2f6e4fc17..000000000 --- a/src/data/migrations/20190704043715-agent-external-ip.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Fogs', - 'ip_address_external', Sequelize.TEXT - ) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Fogs', 'ip_address_external') - // restore constraints. Because Sequelize has problem with Sqlite constraints - .then(() => { - return queryInterface.addConstraint('Fogs', { - feilds: ['user_id'], - type: 'foreign key', - name: 'fogs_users_fkey_constraint', - references: { - table: 'Users', - field: 'id' - }, - onDelete: 'cascade' - }) - }).then(() => { - return queryInterface.addConstraint('Fogs', { - feilds: ['fog_type_id'], - type: 'foreign key', - name: 'fogs_types_fkey_constraint', - references: { - table: 'FogTypes', - field: 'id' - }, - onDelete: 'set null' - }) - }) - } -} diff --git a/src/data/migrations/20190830071324-update catalog item images to be linked to microservice.js b/src/data/migrations/20190830071324-update catalog item images to be linked to microservice.js deleted file mode 100644 index d73c3515b..000000000 --- a/src/data/migrations/20190830071324-update catalog item images to be linked to microservice.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('CatalogItemImages', 'microservice_uuid', { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('CatalogItemImages', 'microservice_uuid') - } -} diff --git a/src/data/migrations/20190916030304-connector-health.js b/src/data/migrations/20190916030304-connector-health.js deleted file mode 100644 index a551c67e5..000000000 --- a/src/data/migrations/20190916030304-connector-health.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Connectors', 'healthy', { - type: Sequelize.BOOLEAN, - field: 'healthy', - defaultValue: true - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Connectors', 'healthy') - } -} diff --git a/src/data/migrations/20190916030532-connector-port-deletion.js b/src/data/migrations/20190916030532-connector-port-deletion.js deleted file mode 100644 index 9c9567fb7..000000000 --- a/src/data/migrations/20190916030532-connector-port-deletion.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('ConnectorPorts', 'moved', { - type: Sequelize.BOOLEAN, - field: 'moved', - defaultValue: false - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('ConnectorPorts', 'moved') - } -} diff --git a/src/data/migrations/20200123000302-system-fog-and-flow.js b/src/data/migrations/20200123000302-system-fog-and-flow.js deleted file mode 100644 index 1f51ff416..000000000 --- a/src/data/migrations/20200123000302-system-fog-and-flow.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return Promise.all([ - queryInterface.addColumn('Fogs', 'is_system', { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'is_system' - }), - queryInterface.addColumn('Flows', 'is_system', { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'is_system' - }) - ]) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return Promise.all([ - queryInterface.removeColumn('Fogs', 'is_system'), - queryInterface.removeColumn('Flows', 'is_system') - ]) - } -} diff --git a/src/data/migrations/20200123045032-fog-and-change-tracking.js b/src/data/migrations/20200123045032-fog-and-change-tracking.js deleted file mode 100644 index 6d2854203..000000000 --- a/src/data/migrations/20200123045032-fog-and-change-tracking.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return Promise.all([ - queryInterface.addColumn('Fogs', 'docker_pruning_freq', { - type: Sequelize.INTEGER, - defaultValue: 60, - field: 'docker_pruning_freq' - }), - queryInterface.addColumn('Fogs', 'available_disk_threshold', { - type: Sequelize.FLOAT, - defaultValue: 20, - field: 'available_disk_threshold' - }), - queryInterface.addColumn('Fogs', 'log_level', { - type: Sequelize.TEXT, - defaultValue: 'INFO', - field: 'log_level' - }), - queryInterface.addColumn('ChangeTrackings', 'prune', { - type: Sequelize.BOOLEAN, - field: 'prune' - }) - ]) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return Promise.all([ - queryInterface.removeColumn('Fogs', 'docker_pruning_freq'), - queryInterface.removeColumn('Fogs', 'available_disk_threshold'), - queryInterface.removeColumn('Fogs', 'log_level'), - queryInterface.removeColumn('ChangeTrackings', 'prune') - ]) - } -} diff --git a/src/data/migrations/20200123163412-create-router.js b/src/data/migrations/20200123163412-create-router.js deleted file mode 100644 index 0c8cc2cba..000000000 --- a/src/data/migrations/20200123163412-create-router.js +++ /dev/null @@ -1,103 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('Routers', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - isEdge: { - type: Sequelize.BOOLEAN, - field: 'is_edge' - }, - messagingPort: { - type: Sequelize.INTEGER, - field: 'messaging_port' - }, - edgeRouterPort: { - type: Sequelize.INTEGER, - field: 'edge_router_port' - }, - interRouterPort: { - type: Sequelize.INTEGER, - field: 'inter_router_port' - }, - host: { - type: Sequelize.TEXT, - field: 'host' - }, - isDefault: { - type: Sequelize.BOOLEAN, - field: 'is_default' - }, - iofogUuid: { - type: Sequelize.STRING(32), - field: 'iofog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'created_at' - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'updated_at' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }) - - await queryInterface.createTable('RouterConnections', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - sourceRouter: { - type: Sequelize.INTEGER, - field: 'source_router', - references: { model: 'Routers', key: 'id' }, - onDelete: 'cascade' - }, - destRouter: { - type: Sequelize.INTEGER, - field: 'dest_router', - references: { model: 'Routers', key: 'id' }, - onDelete: 'cascade' - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'created_at' - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'updated_at' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('Routers') - await queryInterface.dropTable('RouterConnections') - } -} diff --git a/src/data/migrations/20200123205245-router-port-host-agent-config.js b/src/data/migrations/20200123205245-router-port-host-agent-config.js deleted file mode 100644 index 9f1e02d81..000000000 --- a/src/data/migrations/20200123205245-router-port-host-agent-config.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return Promise.all([ - queryInterface.addColumn('Fogs', 'router_id', { - type: Sequelize.INTEGER, - field: 'router_id' - }) - ]) - }, - - down: (queryInterface, Sequelize) => { - return Promise.all([ - queryInterface.removeColumn('Fogs', 'router_id') - ]) - } -} diff --git a/src/data/migrations/20200124040224-router-changed-flag.js b/src/data/migrations/20200124040224-router-changed-flag.js deleted file mode 100644 index 689696bd4..000000000 --- a/src/data/migrations/20200124040224-router-changed-flag.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.addColumn('ChangeTrackings', 'router_changed', { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'router_changed' - }) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('ChangeTrackings', 'router_changed') - } -} diff --git a/src/data/migrations/20200202113124-create-public-ports.js b/src/data/migrations/20200202113124-create-public-ports.js deleted file mode 100644 index 837850a5c..000000000 --- a/src/data/migrations/20200202113124-create-public-ports.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('MicroservicePublicPorts', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - portId: { - type: Sequelize.INTEGER, - field: 'port_id', - references: { model: 'MicroservicePorts', key: 'id' }, - onDelete: 'cascade' - }, - hostId: { - type: Sequelize.STRING(32), - field: 'host_id', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - }, - localProxyId: { - type: Sequelize.STRING(32), - field: 'local_proxy_id', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - remoteProxyId: { - type: Sequelize.STRING(32), - field: 'remote_proxy_id', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - publicPort: { - type: Sequelize.INTEGER, - field: 'public_port' - }, - queueName: { - type: Sequelize.STRING(32), - field: 'queue_name' - }, - isTcp: { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'is_tcp' - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'created_at' - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'updated_at' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('MicroservicePublicPorts') - } -} diff --git a/src/data/migrations/20200213033350-create-config.js b/src/data/migrations/20200213033350-create-config.js deleted file mode 100644 index 76988e5b7..000000000 --- a/src/data/migrations/20200213033350-create-config.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Config', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - - }, - key: { - type: Sequelize.TEXT, - field: 'key', - unique: true, - allowNull: false - }, - value: { - type: Sequelize.TEXT, - field: 'value', - allowNull: false - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'created_at' - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'updated_at' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }).then(() => queryInterface.addIndex('Config', ['key'], { indicesType: 'UNIQUE' })) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Config') - } -} diff --git a/src/data/migrations/20200220210224-change-tracking-timestamp.js b/src/data/migrations/20200220210224-change-tracking-timestamp.js deleted file mode 100644 index c76c84f55..000000000 --- a/src/data/migrations/20200220210224-change-tracking-timestamp.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('ChangeTrackings', 'last_updated', { - type: Sequelize.STRING, - defaultValue: false, - field: 'last_updated' - }) - }, - - down: async (queryInterface, Sequelize) => { - await queryInterface.removeColumn('ChangeTrackings', 'last_updated') - } -} diff --git a/src/data/migrations/20200316011904-microservice-extra-host.js b/src/data/migrations/20200316011904-microservice-extra-host.js deleted file mode 100644 index d907a7ed8..000000000 --- a/src/data/migrations/20200316011904-microservice-extra-host.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('MicroserviceExtraHost', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - name: { - type: Sequelize.TEXT - }, - templateType: { - type: Sequelize.TEXT, - field: 'template_type' - }, - publicPort: { - type: Sequelize.INTEGER, - field: 'public_port' - }, // Only if type is Apps - template: { - type: Sequelize.TEXT - }, // Contains the template string - value: { - type: Sequelize.TEXT - }, - microserviceUuid: { - type: Sequelize.STRING(32), - field: 'microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - targetMicroserviceUuid: { - type: Sequelize.STRING(32), - field: 'target_microservice_uuid', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - targetFogUuid: { - type: Sequelize.STRING(32), - field: 'target_fog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('MicroserviceExtraHost') - } -} diff --git a/src/data/migrations/20200316032240-add-fog-host.js b/src/data/migrations/20200316032240-add-fog-host.js deleted file mode 100644 index 005fb1691..000000000 --- a/src/data/migrations/20200316032240-add-fog-host.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.addColumn('Fogs', 'host', { - type: Sequelize.TEXT - }) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('Fogs', 'host') - } -} diff --git a/src/data/migrations/20200323050813-fog-update-messagespeed.js b/src/data/migrations/20200323050813-fog-update-messagespeed.js deleted file mode 100644 index 36d922d7b..000000000 --- a/src/data/migrations/20200323050813-fog-update-messagespeed.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.changeColumn('Fogs', 'message_speed', { - type: Sequelize.FLOAT, - field: 'message_speed' - }) - }, - - down: async (queryInterface, Sequelize) => { - await queryInterface.changeColumn('Fogs', 'message_speed', { - type: Sequelize.BIGINT, - get () { - return convertToInt(this.getDataValue('messageSpeed')) - }, - field: 'message_speed' - }) - } -} diff --git a/src/data/migrations/20200420020250-remove-connector.js b/src/data/migrations/20200420020250-remove-connector.js deleted file mode 100644 index 14452825a..000000000 --- a/src/data/migrations/20200420020250-remove-connector.js +++ /dev/null @@ -1,136 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Routings', 'connector_port_id') - .then(() => queryInterface.removeColumn('MicroservicePublicModes', 'connector_port_id')) - .then(() => queryInterface.dropTable('ConnectorPorts')) - .then(() => queryInterface.dropTable('Connectors')) - }, - - down: (queryInterface, Sequelize) => { - const connectorTable = queryInterface.createTable('Connectors', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - name: { - type: Sequelize.TEXT, - field: 'name' - }, - domain: { - type: Sequelize.TEXT, - field: 'domain' - }, - publicIp: { - type: Sequelize.TEXT, - field: 'public_ip' - }, - cert: { - type: Sequelize.TEXT, - field: 'cert' - }, - selfSignedCerts: { - type: Sequelize.BOOLEAN, - field: 'self_signed_certs' - }, - devMode: { - type: Sequelize.BOOLEAN, - field: 'dev_mode' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - } - }) - const connectorPortTable = queryInterface.createTable('ConnectorPorts', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - port1: { - type: Sequelize.INTEGER, - field: 'port1' - }, - port2: { - type: Sequelize.INTEGER, - field: 'port2' - }, - maxConnectionsPort1: { - type: Sequelize.INTEGER, - field: 'max_connections_port1' - }, - maxConnectionsPort2: { - type: Sequelize.INTEGER, - field: 'max_connection_port2' - }, - passcodePort1: { - type: Sequelize.TEXT, - field: 'passcode_port1' - }, - passcodePort2: { - type: Sequelize.TEXT, - field: 'passcode_port2' - }, - heartBeatAbsenceThresholdPort1: { - type: Sequelize.INTEGER, - field: 'heartbeat_absence_threshold_port1' - }, - heartBeatAbsenceThresholdPort2: { - type: Sequelize.INTEGER, - field: 'heartbeat_absence_threshold_port2' - }, - mappingId: { - type: Sequelize.TEXT, - field: 'mapping_id' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - connectorId: { - type: Sequelize.INTEGER, - field: 'connector_id', - references: { model: 'Connectors', key: 'id' }, - onDelete: 'cascade' - } - }) - - const connectorPortIdColumn = queryInterface.addColumn('Routings', 'connectorPortId', { - type: Sequelize.INTEGER, - field: 'connector_port_id', - references: { model: 'ConnectorPorts', key: 'id' }, - onDelete: 'set null' - }) - - const msvcConnectorPortIdColumn = queryInterface.addColumn('MicroservicePublicModes', 'connectorPortId', { - type: Sequelize.INTEGER, - field: 'connector_port_id', - references: { model: 'ConnectorPorts', key: 'id' }, - onDelete: 'set null' - }) - - return connectorTable - .then(() => connectorPortTable) - .then(() => connectorPortIdColumn) - .then(() => msvcConnectorPortIdColumn) - } -} diff --git a/src/data/migrations/20200423201804-add-volume-mapping-type.js b/src/data/migrations/20200423201804-add-volume-mapping-type.js deleted file mode 100644 index d6d0cc5e7..000000000 --- a/src/data/migrations/20200423201804-add-volume-mapping-type.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('VolumeMappings', 'type', { - type: Sequelize.TEXT - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('VolumeMappings', 'type') - } -} diff --git a/src/data/migrations/20200506004924-add-route-name.js b/src/data/migrations/20200506004924-add-route-name.js deleted file mode 100644 index b02ba2d83..000000000 --- a/src/data/migrations/20200506004924-add-route-name.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Routings', 'name', { - type: Sequelize.TEXT, - allowNull: false, - defaultValue: 'route' - }).then(() => queryInterface.addIndex('Routings', ['name'], { - indicesType: 'UNIQUE' - })) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('Routings', 'name') - } -} diff --git a/src/data/migrations/20200512044934-rename-flow.js b/src/data/migrations/20200512044934-rename-flow.js deleted file mode 100644 index 0f9403888..000000000 --- a/src/data/migrations/20200512044934-rename-flow.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('Microservices', 'flow_id', 'application_id') - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.renameColumn('Microservices', 'application_id', 'flow_id') - } -} diff --git a/src/data/migrations/20200512214530-add-application-to-routing.js b/src/data/migrations/20200512214530-add-application-to-routing.js deleted file mode 100644 index 88a263c7a..000000000 --- a/src/data/migrations/20200512214530-add-application-to-routing.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('Routings', 'application_id', - { - type: Sequelize.INTEGER, - field: 'application_id', - references: { model: 'Flows', key: 'id' }, - onDelete: 'cascade' - } - ) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('Routings', 'application_id') - } -} diff --git a/src/data/migrations/20200901053246-add-percentage-msvc-status.js b/src/data/migrations/20200901053246-add-percentage-msvc-status.js deleted file mode 100644 index 9cd03a291..000000000 --- a/src/data/migrations/20200901053246-add-percentage-msvc-status.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroserviceStatuses', 'percentage', { - type: Sequelize.FLOAT, - defaultValue: 0.00 - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('MicroserviceStatuses', 'percentage') - } -} diff --git a/src/data/migrations/20201001230303-add-tags-fogs.js b/src/data/migrations/20201001230303-add-tags-fogs.js deleted file mode 100644 index 6b435c431..000000000 --- a/src/data/migrations/20201001230303-add-tags-fogs.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('Tags', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - value: { - type: Sequelize.TEXT, - field: 'value', - unique: true, - allowNull: false - } - }) - await queryInterface.createTable('IofogTags', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - TagId: { - type: Sequelize.INTEGER, - field: 'tag_id', - references: { model: 'Tags', key: 'id' }, - onDelete: 'cascade' - }, - FogUuid: { - type: Sequelize.TEXT, - field: 'fog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('Tags') - await queryInterface.dropTable('IofogTags') - } -} diff --git a/src/data/migrations/20201028005645-create-edge-resources.js b/src/data/migrations/20201028005645-create-edge-resources.js deleted file mode 100644 index e03b5f07c..000000000 --- a/src/data/migrations/20201028005645-create-edge-resources.js +++ /dev/null @@ -1,119 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('EdgeResources', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - name: { - type: Sequelize.TEXT, - allowNull: false - }, - version: { - type: Sequelize.TEXT, - allowNull: false - }, - description: Sequelize.TEXT, - displayName: { type: Sequelize.TEXT, field: 'display_name' }, - displayIcon: { type: Sequelize.TEXT, field: 'display_icon' }, - displayColor: { type: Sequelize.TEXT, field: 'display_color' }, - interfaceProtocol: { type: Sequelize.TEXT, field: 'interface_protocol' }, - interfaceId: { type: Sequelize.INTEGER, field: 'interface_id' }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - await queryInterface.createTable('AgentEdgeResources', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - EdgeResourceId: { - type: Sequelize.INTEGER, - field: 'edge_resource_id', - references: { model: 'EdgeResources', key: 'id' }, - onDelete: 'cascade' - }, - FogUuid: { - type: Sequelize.TEXT, - field: 'fog_uuid', - references: { model: 'Fogs', key: 'uuid' }, - onDelete: 'cascade' - } - }) - await queryInterface.createTable('EdgeResourceOrchestrationTags', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - EdgeResourceId: { - type: Sequelize.INTEGER, - field: 'edge_resource_id', - references: { model: 'EdgeResources', key: 'id' }, - onDelete: 'cascade' - }, - TagId: { - type: Sequelize.INTEGER, - field: 'tag_id', - references: { model: 'Tags', key: 'id' }, - onDelete: 'cascade' - } - }) - await queryInterface.createTable('HTTPBasedResourceInterfaces', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - edgeResourceId: { - type: Sequelize.INTEGER, - field: 'edge_resource_id', - references: { model: 'EdgeResources', key: 'id' }, - onDelete: 'cascade' - } - }) - await queryInterface.createTable('HTTPBasedResourceInterfaceEndpoints', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - name: Sequelize.TEXT, - description: Sequelize.TEXT, - method: Sequelize.TEXT, - url: Sequelize.TEXT, - requestType: { type: Sequelize.TEXT, field: 'request_type' }, - responseType: { type: Sequelize.TEXT, field: 'response_type' }, - requestPayloadExample: { type: Sequelize.TEXT, field: 'request_payload_example' }, - responsePayloadExample: { type: Sequelize.TEXT, field: 'response_payload_example' }, - interfaceId: { - type: Sequelize.INTEGER, - field: 'interface_id', - references: { model: 'HTTPBasedResourceInterfaces', key: 'id' }, - onDelete: 'cascade' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('EdgeResources') - await queryInterface.dropTable('AgentEdgeResources') - await queryInterface.dropTable('EdgeResourceOrchestrationTags') - await queryInterface.dropTable('HTTPBasedResourceInterfaces') - await queryInterface.dropTable('HTTPBasedResourceInterfaceEndpoints') - } -} diff --git a/src/data/migrations/20201105003849-create-edge-resources-agent-flag.js b/src/data/migrations/20201105003849-create-edge-resources-agent-flag.js deleted file mode 100644 index 8c0a56763..000000000 --- a/src/data/migrations/20201105003849-create-edge-resources-agent-flag.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('ChangeTrackings', 'linked_edge_resources', { - type: Sequelize.BOOLEAN, - defaultValue: false, - field: 'linked_edge_resources' - }) - }, - - down: async (queryInterface, Sequelize) => { - await queryInterface.removeColumn('ChangeTrackings', 'linked_edge_resources') - } -} diff --git a/src/data/migrations/20201203011708-microservice-status-error-msg.js b/src/data/migrations/20201203011708-microservice-status-error-msg.js deleted file mode 100644 index e2871c672..000000000 --- a/src/data/migrations/20201203011708-microservice-status-error-msg.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroserviceStatuses', 'error_message', { - type: Sequelize.TEXT, - defaultValue: '' - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeColumn('MicroserviceStatuses', 'error_message') - } -} diff --git a/src/data/migrations/20201204003312-create_application_templates.js b/src/data/migrations/20201204003312-create_application_templates.js deleted file mode 100644 index 02f513767..000000000 --- a/src/data/migrations/20201204003312-create_application_templates.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('ApplicationTemplates', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - name: { - type: Sequelize.TEXT, - field: 'name', - defaultValue: 'new-application-template', - unique: true - }, - description: { - type: Sequelize.TEXT, - field: 'description', - defaultValue: '' - }, - schemaVersion: { - type: Sequelize.TEXT, - field: 'schema_version', - defaultValue: '' - }, - applicationJSON: { - type: Sequelize.TEXT, - field: 'application_json', - defaultValue: '{}' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade' - } - }) - await queryInterface.createTable('ApplicationTemplateVariables', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - key: { - type: Sequelize.TEXT, - field: 'key' - }, - description: { - type: Sequelize.TEXT, - field: 'description', - defaultValue: '' - }, - defaultValue: { - type: Sequelize.TEXT, - field: 'default_value' - }, - applicationTemplateId: { - type: Sequelize.INTEGER, - field: 'application_template_id', - references: { model: 'ApplicationTemplates', key: 'id' }, - onDelete: 'cascade' - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'created_at' - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - field: 'updated_at' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('ApplicationTemplates') - await queryInterface.dropTable('ApplicationTemplateVariables') - } -} diff --git a/src/data/migrations/20210104191500-add_custom_to_edge_resources.js b/src/data/migrations/20210104191500-add_custom_to_edge_resources.js deleted file mode 100644 index 407225bc1..000000000 --- a/src/data/migrations/20210104191500-add_custom_to_edge_resources.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn('EdgeResources', 'custom', - { - type: Sequelize.TEXT - } - ) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('EdgeResources', 'custom') - } -} diff --git a/src/data/migrations/20210118012712-add_is_udp_to_port.js b/src/data/migrations/20210118012712-add_is_udp_to_port.js deleted file mode 100644 index b2039618d..000000000 --- a/src/data/migrations/20210118012712-add_is_udp_to_port.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroservicePorts', 'is_udp', - { - type: Sequelize.BOOLEAN - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('MicroservicePorts', 'is_udp') - } -} diff --git a/src/data/migrations/20211019214923-add-schemes-to-public-ports.js b/src/data/migrations/20211019214923-add-schemes-to-public-ports.js deleted file mode 100644 index 29d3d7be8..000000000 --- a/src/data/migrations/20211019214923-add-schemes-to-public-ports.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroservicePublicPorts', 'schemes', - { - type: Sequelize.TEXT, - defaultValue: JSON.stringify(['https']) - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('MicroservicePublicPorts', 'schemes') - } -} diff --git a/src/data/migrations/20211020014715-add-index-to-public-ports.js b/src/data/migrations/20211020014715-add-index-to-public-ports.js deleted file mode 100644 index 70af9276c..000000000 --- a/src/data/migrations/20211020014715-add-index-to-public-ports.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addConstraint('MicroservicePublicPorts', - { - fields: ['public_port', 'host_id'], - type: 'unique', - name: 'port_host_unique_constraint' - } - ) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.removeConstraint('MicroservicePublicPorts', 'port_host_unique_constraint') - } -} diff --git a/src/data/migrations/20211022010318-add-unique-constraint-msvc.js b/src/data/migrations/20211022010318-add-unique-constraint-msvc.js deleted file mode 100644 index b0aff579f..000000000 --- a/src/data/migrations/20211022010318-add-unique-constraint-msvc.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.addConstraint('Microservices', - { - fields: ['application_id', 'name'], - type: 'unique', - name: 'app_name_unique_constraint' - }) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeConstraint('Microservices', 'app_name_unique_constraint') - } -} diff --git a/src/data/migrations/20211022013326-add-unique-constraint-route.js b/src/data/migrations/20211022013326-add-unique-constraint-route.js deleted file mode 100644 index 4c5259834..000000000 --- a/src/data/migrations/20211022013326-add-unique-constraint-route.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.addConstraint('Routings', - { - fields: ['application_id', 'name'], - type: 'unique', - name: 'routings_app_name_unique_constraint' - }) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeConstraint('Routings', 'routings_app_name_unique_constraint') - } -} diff --git a/src/data/migrations/20220405053419-add-time-zone-agent-config.js b/src/data/migrations/20220405053419-add-time-zone-agent-config.js deleted file mode 100644 index 9cb59e0f5..000000000 --- a/src/data/migrations/20220405053419-add-time-zone-agent-config.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.addColumn('Fogs', 'time_zone', { - type: Sequelize.TEXT - }) - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('Fogs', 'time_zone') - } -} diff --git a/src/data/migrations/20220409021708-drop-table-tracking.js b/src/data/migrations/20220409021708-drop-table-tracking.js deleted file mode 100644 index 6d6f2e322..000000000 --- a/src/data/migrations/20220409021708-drop-table-tracking.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - /* - Add altering commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ - return queryInterface.dropTable('TrackingEvent') - }, - - down: (queryInterface, Sequelize) => { - /* - Add reverting commands here. - Return a promise to correctly handle asynchronicity. - - Example: - return queryInterface.dropTable('users'); - */ - return queryInterface.createTable('TrackingEvent') - } -} diff --git a/src/data/migrations/20221021102712-add_is_proxy_to_port.js b/src/data/migrations/20221021102712-add_is_proxy_to_port.js deleted file mode 100644 index d5ccfa8e9..000000000 --- a/src/data/migrations/20221021102712-add_is_proxy_to_port.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroservicePorts', 'is_proxy', - { - type: Sequelize.BOOLEAN - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('MicroservicePorts', 'is_proxy') - } -} diff --git a/src/data/migrations/20221021132000-create-proxy-ports.js b/src/data/migrations/20221021132000-create-proxy-ports.js deleted file mode 100644 index 8893f1f32..000000000 --- a/src/data/migrations/20221021132000-create-proxy-ports.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('MicroserviceProxyPorts', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - portId: { - type: Sequelize.INTEGER, - field: 'port_id', - references: { model: 'MicroservicePorts', key: 'id' }, - onDelete: 'cascade' - }, - host: { - type: Sequelize.STRING(128), - field: 'host' - }, - localProxyId: { - type: Sequelize.STRING(32), - field: 'local_proxy_id', - references: { model: 'Microservices', key: 'uuid' }, - onDelete: 'cascade' - }, - publicPort: { - type: Sequelize.INTEGER, - field: 'public_port' - }, - protocol: { - type: Sequelize.STRING(3), - field: 'protocol' - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'created_at' - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - field: 'updated_at' - }, - updatedBy: { - type: Sequelize.INTEGER, - field: 'updated_by', - references: { model: 'Users', key: 'id' }, - onDelete: 'set null' - } - }) - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('MicroserviceProxyPorts') - } -} diff --git a/src/data/migrations/20221021152712-add_admin_port_to_proxy_ports.js b/src/data/migrations/20221021152712-add_admin_port_to_proxy_ports.js deleted file mode 100644 index 605ba2dc7..000000000 --- a/src/data/migrations/20221021152712-add_admin_port_to_proxy_ports.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroserviceProxyPorts', 'admin_port', - { - type: Sequelize.BOOLEAN - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('MicroserviceProxyPorts', 'admin_port') - } -} diff --git a/src/data/migrations/20221031162712-add_tokens_to_proxy_ports.js b/src/data/migrations/20221031162712-add_tokens_to_proxy_ports.js deleted file mode 100644 index 97f593c70..000000000 --- a/src/data/migrations/20221031162712-add_tokens_to_proxy_ports.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - return queryInterface.addColumn('MicroserviceProxyPorts', 'proxy_token', - { - type: Sequelize.TEXT - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - return queryInterface.removeColumn('MicroserviceProxyPorts', 'proxy_token') - } -} diff --git a/src/data/migrations/20221110151246-add_port_uuid_to_proxy_ports copy.js b/src/data/migrations/20221110151246-add_port_uuid_to_proxy_ports copy.js deleted file mode 100644 index 2199670b3..000000000 --- a/src/data/migrations/20221110151246-add_port_uuid_to_proxy_ports copy.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('MicroserviceProxyPorts', 'port_uuid', - { - type: Sequelize.TEXT - } - ) - await queryInterface.addColumn('MicroserviceProxyPorts', 'server_token', - { - type: Sequelize.TEXT - } - ) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - await queryInterface.removeColumn('MicroserviceProxyPorts', 'port_uuid') - await queryInterface.removeColumn('MicroserviceProxyPorts', 'server_token') - } -} diff --git a/src/data/migrations/mysql/db_migration_mysql_v1.0.6.sql b/src/data/migrations/mysql/db_migration_mysql_v1.0.6.sql new file mode 100644 index 000000000..278686462 --- /dev/null +++ b/src/data/migrations/mysql/db_migration_mysql_v1.0.6.sql @@ -0,0 +1,844 @@ +START TRANSACTION; + +CREATE TABLE IF NOT EXISTS Flows ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) UNIQUE, + description VARCHAR(255) DEFAULT '', + is_activated BOOLEAN DEFAULT false, + is_system BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME +); + +CREATE TABLE IF NOT EXISTS Registries ( + id INT AUTO_INCREMENT PRIMARY KEY, + url VARCHAR(255), + is_public BOOLEAN, + user_name TEXT, + password TEXT, + user_email TEXT +); + + +CREATE TABLE IF NOT EXISTS CatalogItems ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) UNIQUE, + description VARCHAR(255), + category TEXT, + config_example VARCHAR(255) DEFAULT '{}', + publisher TEXT, + disk_required BIGINT DEFAULT 0, + ram_required BIGINT DEFAULT 0, + picture VARCHAR(255) DEFAULT 'images/shared/default.png', + is_public BOOLEAN DEFAULT false, + registry_id INT, + FOREIGN KEY (registry_id) REFERENCES Registries (id) ON DELETE SET NULL +); + +CREATE INDEX idx_catalog_item_registry_id ON CatalogItems (registry_id); + + +CREATE TABLE IF NOT EXISTS FogTypes ( + id INT PRIMARY KEY, + name TEXT, + image TEXT, + description TEXT, + network_catalog_item_id INT, + hal_catalog_item_id INT, + bluetooth_catalog_item_id INT, + FOREIGN KEY (network_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (hal_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (bluetooth_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_type_network_catalog_item_id ON FogTypes (network_catalog_item_id); +CREATE INDEX idx_fog_type_hal_catalog_item_id ON FogTypes (hal_catalog_item_id); +CREATE INDEX idx_fog_type_bluetooth_catalog_item_id ON FogTypes (bluetooth_catalog_item_id); + + +CREATE TABLE IF NOT EXISTS Fogs ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) DEFAULT 'Unnamed ioFog 1', + location TEXT, + gps_mode TEXT, + latitude FLOAT, + longitude FLOAT, + description TEXT, + last_active BIGINT, + daemon_status VARCHAR(36) DEFAULT 'NOT_PROVISIONED', + daemon_operating_duration BIGINT DEFAULT 0, + daemon_last_start BIGINT, + memory_usage FLOAT DEFAULT 0.000, + disk_usage FLOAT DEFAULT 0.000, + cpu_usage FLOAT DEFAULT 0.00, + memory_violation TEXT, + disk_violation TEXT, + cpu_violation TEXT, + system_available_disk BIGINT, + system_available_memory BIGINT, + system_total_cpu FLOAT, + security_status VARCHAR(36) DEFAULT 'OK', + security_violation_info VARCHAR(36) DEFAULT 'No violation', + catalog_item_status TEXT, + repository_count BIGINT DEFAULT 0, + repository_status TEXT, + system_time BIGINT, + last_status_time BIGINT, + ip_address VARCHAR(36) DEFAULT '0.0.0.0', + ip_address_external VARCHAR(36) DEFAULT '0.0.0.0', + host VARCHAR(36), + processed_messages BIGINT DEFAULT 0, + catalog_item_message_counts TEXT, + message_speed FLOAT DEFAULT 0.000, + last_command_time BIGINT, + network_interface VARCHAR(36) DEFAULT 'dynamic', + docker_url VARCHAR(255) DEFAULT 'unix:///var/run/docker.sock', + disk_limit FLOAT DEFAULT 50, + disk_directory VARCHAR(255) DEFAULT '/var/lib/iofog/', + memory_limit FLOAT DEFAULT 4096, + cpu_limit FLOAT DEFAULT 80, + log_limit FLOAT DEFAULT 10, + log_directory VARCHAR(255) DEFAULT '/var/log/iofog/', + bluetooth BOOLEAN DEFAULT FALSE, + hal BOOLEAN DEFAULT FALSE, + log_file_count BIGINT DEFAULT 10, + `version` TEXT, + is_ready_to_upgrade BOOLEAN DEFAULT TRUE, + is_ready_to_rollback BOOLEAN DEFAULT FALSE, + status_frequency INT DEFAULT 10, + change_frequency INT DEFAULT 20, + device_scan_frequency INT DEFAULT 20, + tunnel VARCHAR(255) DEFAULT '', + isolated_docker_container BOOLEAN DEFAULT FALSE, + docker_pruning_freq INT DEFAULT 0, + available_disk_threshold FLOAT DEFAULT 20, + log_level VARCHAR(10) DEFAULT 'INFO', + is_system BOOLEAN DEFAULT FALSE, + router_id INT DEFAULT 0, + time_zone VARCHAR(36) DEFAULT 'Etc/UTC', + created_at DATETIME, + updated_at DATETIME, + fog_type_id INT DEFAULT 0, + FOREIGN KEY (fog_type_id) REFERENCES FogTypes (id) +); + +CREATE INDEX idx_fog_fog_type_id ON Fogs (fog_type_id); + +CREATE TABLE IF NOT EXISTS ChangeTrackings ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + microservice_config BOOLEAN DEFAULT false, + reboot BOOLEAN DEFAULT false, + deletenode BOOLEAN DEFAULT false, + version BOOLEAN DEFAULT false, + microservice_list BOOLEAN DEFAULT false, + config BOOLEAN DEFAULT false, + routing BOOLEAN DEFAULT false, + registries BOOLEAN DEFAULT false, + tunnel BOOLEAN DEFAULT false, + diagnostics BOOLEAN DEFAULT false, + router_changed BOOLEAN DEFAULT false, + image_snapshot BOOLEAN DEFAULT false, + prune BOOLEAN DEFAULT false, + linked_edge_resources BOOLEAN DEFAULT false, + last_updated VARCHAR(255) DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_change_tracking_iofog_uuid ON ChangeTrackings (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogAccessTokens ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + expiration_time BIGINT, + token TEXT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_access_tokens_iofogUuid ON FogAccessTokens (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogProvisionKeys ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + provisioning_string VARCHAR(100), + expiration_time BIGINT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_provision_keys_iofogUuid ON FogProvisionKeys (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogVersionCommands ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + version_command VARCHAR(100), + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_version_commands_iofogUuid ON FogVersionCommands (iofog_uuid); + +CREATE TABLE IF NOT EXISTS HWInfos ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + info TEXT, + created_at DATETIME, + updated_at DATETIME, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_hw_infos_iofogUuid ON HWInfos (iofog_uuid); + +CREATE TABLE IF NOT EXISTS USBInfos ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + info TEXT, + created_at DATETIME, + updated_at DATETIME, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_usb_infos_iofogUuid ON USBInfos (iofog_uuid); + +CREATE TABLE IF NOT EXISTS Tunnels ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + username TEXT, + password TEXT, + host TEXT, + remote_port INT, + local_port INT DEFAULT 22, + rsa_key TEXT, + closed BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_tunnels_iofogUuid ON Tunnels (iofog_uuid); + +CREATE TABLE IF NOT EXISTS Microservices ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + config TEXT, + name VARCHAR(255) DEFAULT 'New Microservice', + config_last_updated BIGINT, + rebuild BOOLEAN DEFAULT false, + root_host_access BOOLEAN DEFAULT false, + log_size BIGINT DEFAULT 0, + image_snapshot VARCHAR(255) DEFAULT '', + `delete` BOOLEAN DEFAULT false, + delete_with_cleanup BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME, + catalog_item_id INT, + registry_id INT DEFAULT 1, + iofog_uuid VARCHAR(36), + application_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (registry_id) REFERENCES Registries (id) ON DELETE SET NULL, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES Flows (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservices_catalogItemId ON Microservices (catalog_item_id); +CREATE INDEX idx_microservices_registryId ON Microservices (registry_id); +CREATE INDEX idx_microservices_iofogUuid ON Microservices (iofog_uuid); +CREATE INDEX idx_microservices_applicationId ON Microservices (application_id); + +CREATE TABLE IF NOT EXISTS MicroserviceArgs ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + cmd TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_args_microserviceUuid ON MicroserviceArgs (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceEnvs ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + `key` TEXT, + `value` TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_envs_microserviceUuid ON MicroserviceEnvs (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceExtraHost ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + template_type TEXT, + name TEXT, + public_port INT, + template TEXT, + `value` TEXT, + microservice_uuid VARCHAR(36), + target_microservice_uuid VARCHAR(36), + target_fog_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_extra_host_microserviceUuid ON MicroserviceExtraHost (microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetMicroserviceUuid ON MicroserviceExtraHost (target_microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetFogUuid ON MicroserviceExtraHost (target_fog_uuid); + +CREATE TABLE IF NOT EXISTS MicroservicePorts ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + port_internal INT, + port_external INT, + is_udp BOOLEAN, + is_public BOOLEAN, + is_proxy BOOLEAN, + created_at DATETIME, + updated_at DATETIME, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_port_microserviceUuid ON MicroservicePorts (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroservicePublicPorts ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + port_id INT UNIQUE, + host_id VARCHAR(255) UNIQUE, + local_proxy_id TEXT, + remote_proxy_id TEXT, + public_port INT, + queue_name TEXT, + schemes VARCHAR(255) DEFAULT '["https"]', + is_tcp BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME, + protocol VARCHAR(255) AS (CASE WHEN is_tcp THEN 'tcp' ELSE 'http' END) VIRTUAL, + FOREIGN KEY (port_id) REFERENCES MicroservicePorts (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_public_port_portId ON MicroservicePublicPorts (port_id); +CREATE INDEX idx_microservice_public_port_hostId ON MicroservicePublicPorts (host_id); + + +CREATE TABLE IF NOT EXISTS MicroserviceStatuses ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + status VARCHAR(255) DEFAULT 'QUEUED', + operating_duration BIGINT DEFAULT 0, + start_time BIGINT DEFAULT 0, + cpu_usage FLOAT DEFAULT 0.000, + memory_usage BIGINT DEFAULT 0, + container_id VARCHAR(255) DEFAULT '', + percentage FLOAT DEFAULT 0.00, + error_message TEXT, + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_status_microserviceUuid ON MicroserviceStatuses (microservice_uuid); + +CREATE TABLE IF NOT EXISTS StraceDiagnostics ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + strace_run BOOLEAN, + buffer VARCHAR(255) DEFAULT '', + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_strace_diagnostics_microserviceUuid ON StraceDiagnostics (microservice_uuid); + +CREATE TABLE IF NOT EXISTS VolumeMappings ( + uuid INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + host_destination TEXT, + container_destination TEXT, + access_mode TEXT, + type TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mappings_microserviceUuid ON VolumeMappings (microservice_uuid); + + +CREATE TABLE IF NOT EXISTS CatalogItemImages ( + id INT AUTO_INCREMENT PRIMARY KEY, + container_image TEXT, + catalog_item_id INT, + microservice_uuid VARCHAR(36), + fog_type_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (fog_type_id) REFERENCES FogTypes (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_image_catalog_item_id ON CatalogItemImages (catalog_item_id); +CREATE INDEX idx_catalog_item_image_microservice_uuid ON CatalogItemImages (microservice_uuid); +CREATE INDEX idx_catalog_item_image_fog_type_id ON CatalogItemImages (fog_type_id); + +CREATE TABLE IF NOT EXISTS CatalogItemInputTypes ( + id INT AUTO_INCREMENT PRIMARY KEY, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_input_type_catalog_item_id ON CatalogItemInputTypes (catalog_item_id); + +CREATE TABLE IF NOT EXISTS CatalogItemOutputTypes ( + id INT AUTO_INCREMENT PRIMARY KEY, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_output_type_catalog_item_id ON CatalogItemOutputTypes (catalog_item_id); + + +CREATE TABLE IF NOT EXISTS Routings ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + source_microservice_uuid VARCHAR(36), + dest_microservice_uuid VARCHAR(36), + application_id INT, + FOREIGN KEY (source_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (dest_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES Flows (id) ON DELETE CASCADE +); + +CREATE INDEX idx_routing_sourceMicroserviceUuid ON Routings (source_microservice_uuid); +CREATE INDEX idx_routing_destMicroserviceUuid ON Routings (dest_microservice_uuid); +CREATE INDEX idx_routing_applicationId ON Routings (application_id); + +CREATE TABLE IF NOT EXISTS Routers ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + is_edge BOOLEAN DEFAULT true, + messaging_port INT DEFAULT 5671, + edge_router_port INT, + inter_router_port INT, + host TEXT, + is_default BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE + +); + +CREATE INDEX idx_router_iofogUuid ON Routers (iofog_uuid); + + +CREATE TABLE RouterConnections ( + id INT AUTO_INCREMENT PRIMARY KEY, + source_router INT, + dest_router INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + FOREIGN KEY (source_router) REFERENCES Routers(id) ON DELETE CASCADE, + FOREIGN KEY (dest_router) REFERENCES Routers(id) ON DELETE CASCADE +); + +CREATE INDEX idx_routerconnections_sourceRouter ON RouterConnections (source_router); +CREATE INDEX idx_routerconnections_destRouter ON RouterConnections (dest_router); + + + +CREATE TABLE IF NOT EXISTS Config ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + `key` VARCHAR(255) NOT NULL UNIQUE, + value VARCHAR(255) NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_config_key ON Config (`key`); + + +CREATE TABLE IF NOT EXISTS Tags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + value VARCHAR(255) UNIQUE NOT NULL +); + +CREATE TABLE IF NOT EXISTS IofogTags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_iofogtags_fog_uuid ON IofogTags (fog_uuid); +CREATE INDEX idx_iofogtags_tag_id ON IofogTags (tag_id); + +CREATE TABLE IF NOT EXISTS EdgeResources ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + version TEXT, + description TEXT, + display_name TEXT, + display_color TEXT, + display_icon TEXT, + interface_protocol TEXT, + interface_id INT, + custom TEXT +); + + +CREATE TABLE IF NOT EXISTS AgentEdgeResources ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + edge_resource_id INT, + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS EdgeResourceOrchestrationTags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + edge_resource_id INT, + tag_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_agentedgeresources_fog_id ON AgentEdgeResources (fog_uuid); +CREATE INDEX idx_agentedgeresources_edge_resource_id ON AgentEdgeResources (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_edge_resource_id ON EdgeResourceOrchestrationTags (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_tag_id ON EdgeResourceOrchestrationTags (tag_id); + +CREATE TABLE IF NOT EXISTS HTTPBasedResourceInterfaces ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + edge_resource_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS HTTPBasedResourceInterfaceEndpoints ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + interface_id INT, + name TEXT, + description TEXT, + `method` TEXT, + url TEXT, + requestType TEXT, + responseType TEXT, + requestPayloadExample TEXT, + responsePayloadExample TEXT, + FOREIGN KEY (interface_id) REFERENCES HTTPBasedResourceInterfaces (id) ON DELETE CASCADE +); + +CREATE INDEX idx_httpbasedresourceinterfaces_edge_resource_id ON HTTPBasedResourceInterfaces (edge_resource_id); +CREATE INDEX idx_httpbasedresourceinterfaceendpoints_interface_id ON HTTPBasedResourceInterfaceEndpoints (interface_id); + + +CREATE TABLE IF NOT EXISTS ApplicationTemplates ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL DEFAULT 'new-application', + description VARCHAR(255) DEFAULT '', + schema_version VARCHAR(255) DEFAULT '', + application_json LONGTEXT, + created_at DATETIME, + updated_at DATETIME + +); + + +CREATE TABLE IF NOT EXISTS ApplicationTemplateVariables ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + application_template_id INT NOT NULL, + `key` TEXT, + description VARCHAR(255) DEFAULT '', + default_value VARCHAR(255), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (application_template_id) REFERENCES ApplicationTemplates (id) ON DELETE CASCADE +); + +CREATE INDEX idx_applicationtemplatevariables_application_template_id ON ApplicationTemplateVariables (application_template_id); + +CREATE TABLE IF NOT EXISTS MicroserviceCdiDevices ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + cdi_devices TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_cdiDevices_microserviceUuid ON MicroserviceCdiDevices (microservice_uuid); + +ALTER TABLE Microservices +ADD COLUMN run_as_user TEXT DEFAULT NULL, +ADD COLUMN platform TEXT DEFAULT NULL, +ADD COLUMN runtime TEXT DEFAULT NULL; + + +CREATE TABLE IF NOT EXISTS MicroservicePubTags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS MicroserviceSubTags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservicepubtags_microservice_uuid ON MicroservicePubTags (microservice_uuid); +CREATE INDEX idx_microservicesubtags_microservice_uuid ON MicroserviceSubTags (microservice_uuid); +CREATE INDEX idx_microservicepubtags_tag_id ON MicroservicePubTags (tag_id); +CREATE INDEX idx_microservicesubtags_tag_id ON MicroserviceSubTags (tag_id); + +CREATE TABLE IF NOT EXISTS MicroserviceCapAdd ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + cap_add TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capAdd_microserviceUuid ON MicroserviceCapAdd (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceCapDrop ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + cap_drop TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capDrop_microserviceUuid ON MicroserviceCapDrop (microservice_uuid); + +ALTER TABLE Microservices +ADD COLUMN annotations TEXT; + +CREATE TABLE IF NOT EXISTS FogPublicKeys ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + public_key TEXT, + iofog_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_public_keys_iofogUuid ON FogPublicKeys (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogUsedTokens ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + jti VARCHAR(255) NOT NULL, + iofog_uuid VARCHAR(36), + expiry_time BIGINT NOT NULL, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_used_tokens_iofogUuid ON FogUsedTokens (iofog_uuid); + +DROP TABLE IF EXISTS FogAccessTokens; + +ALTER TABLE MicroserviceStatuses ADD COLUMN ip_address TEXT; + +CREATE TABLE IF NOT EXISTS Secrets ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL CHECK (type IN ('Opaque', 'tls')), + data TEXT NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_secrets_name ON Secrets (name); + +CREATE TABLE IF NOT EXISTS Certificates ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + subject TEXT NOT NULL, + is_ca BOOLEAN DEFAULT false, + signed_by_id INT, + hosts TEXT, + valid_from DATETIME NOT NULL, + valid_to DATETIME NOT NULL, + serial_number TEXT NOT NULL, + secret_id INT, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (signed_by_id) REFERENCES Certificates (id) ON DELETE SET NULL, + FOREIGN KEY (secret_id) REFERENCES Secrets (id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX idx_certificates_name_unique ON Certificates (name(255)); +CREATE INDEX idx_certificates_valid_to ON Certificates (valid_to); +CREATE INDEX idx_certificates_is_ca ON Certificates (is_ca); +CREATE INDEX idx_certificates_signed_by_id ON Certificates (signed_by_id); +CREATE INDEX idx_certificates_secret_id ON Certificates (secret_id); + +CREATE TABLE IF NOT EXISTS Services ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL, + resource TEXT NOT NULL, + target_port INT NOT NULL, + service_port INT, + k8s_type TEXT, + bridge_port INT, + default_bridge TEXT, + service_endpoint TEXT, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_services_name ON Services (name); +CREATE INDEX idx_services_id ON Services (id); + +CREATE TABLE IF NOT EXISTS ServiceTags ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + service_id INT NOT NULL, + tag_id INT NOT NULL, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (service_id) REFERENCES Services (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_service_tags_service_id ON ServiceTags (service_id); +CREATE INDEX idx_service_tags_tag_id ON ServiceTags (tag_id); + +ALTER TABLE Fogs ADD COLUMN container_engine VARCHAR(36); +ALTER TABLE Fogs ADD COLUMN deployment_type VARCHAR(36); + +ALTER TABLE MicroserviceExtraHost DROP COLUMN public_port; +ALTER TABLE MicroservicePorts DROP COLUMN is_public; +ALTER TABLE MicroservicePorts DROP COLUMN is_proxy; + +DROP TABLE IF EXISTS MicroservicePublicPorts; + +ALTER TABLE MicroserviceEnvs ADD COLUMN value_from_secret TEXT; +ALTER TABLE MicroserviceEnvs ADD COLUMN value_from_config_map TEXT; + +CREATE TABLE IF NOT EXISTS ConfigMaps ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + immutable BOOLEAN DEFAULT false, + data TEXT NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_config_maps_name ON ConfigMaps (name); + +CREATE TABLE IF NOT EXISTS VolumeMounts ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + config_map_name VARCHAR(255), + secret_name VARCHAR(255), + version INT DEFAULT 1, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (config_map_name) REFERENCES ConfigMaps (name) ON DELETE CASCADE, + FOREIGN KEY (secret_name) REFERENCES Secrets (name) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mounts_uuid ON VolumeMounts (uuid); +CREATE INDEX idx_volume_mounts_config_map_name ON VolumeMounts (config_map_name); +CREATE INDEX idx_volume_mounts_secret_name ON VolumeMounts (secret_name); + +CREATE TABLE IF NOT EXISTS FogVolumeMounts ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + volume_mount_uuid VARCHAR(36), + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (volume_mount_uuid) REFERENCES VolumeMounts (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_volume_mounts_fog_uuid ON FogVolumeMounts (fog_uuid); +CREATE INDEX idx_fog_volume_mounts_volume_mount_uuid ON FogVolumeMounts (volume_mount_uuid); + +ALTER TABLE Fogs ADD COLUMN active_volume_mounts BIGINT DEFAULT 0; +ALTER TABLE Fogs ADD COLUMN volume_mount_last_update BIGINT DEFAULT 0; + +ALTER TABLE ChangeTrackings ADD COLUMN volume_mounts BOOLEAN DEFAULT false; +ALTER TABLE ChangeTrackings ADD COLUMN exec_sessions BOOLEAN DEFAULT false; + +ALTER TABLE Services ADD COLUMN provisioning_status VARCHAR(36) DEFAULT 'pending'; +ALTER TABLE Services ADD COLUMN provisioning_error TEXT; + +ALTER TABLE Fogs ADD COLUMN warning_message TEXT; +ALTER TABLE Fogs ADD COLUMN gps_device VARCHAR(36); +ALTER TABLE Fogs ADD COLUMN gps_scan_frequency INT DEFAULT 60; +ALTER TABLE Fogs ADD COLUMN edge_guard_frequency INT DEFAULT 0; + +ALTER TABLE Microservices ADD COLUMN pid_mode VARCHAR(36); +ALTER TABLE Microservices ADD COLUMN ipc_mode VARCHAR(36); +ALTER TABLE Microservices ADD COLUMN exec_enabled BOOLEAN DEFAULT false; + +ALTER TABLE MicroserviceStatuses ADD COLUMN exec_session_ids TEXT; + +ALTER TABLE Microservices ADD COLUMN schedule INT DEFAULT 50; + +CREATE TABLE IF NOT EXISTS MicroserviceExecStatuses ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + status VARCHAR(255) DEFAULT 'INACTIVE', + exec_session_id VARCHAR(255), + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_exec_status_microservice_uuid ON MicroserviceExecStatuses (microservice_uuid); + +ALTER TABLE Fogs ADD COLUMN gps_status VARCHAR(32); + +ALTER TABLE Microservices ADD COLUMN cpu_set_cpus TEXT; +ALTER TABLE Microservices ADD COLUMN memory_limit FLOAT; + +CREATE TABLE IF NOT EXISTS MicroserviceHealthChecks ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + test TEXT, + interval BIGINT, + timeout BIGINT, + start_period BIGINT, + start_interval BIGINT, + retries INT, + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_health_check_microservice_uuid ON MicroserviceHealthChecks (microservice_uuid); + +ALTER TABLE MicroserviceStatuses ADD COLUMN health_status TEXT; + +ALTER TABLE Microservices ADD COLUMN is_activated BOOLEAN DEFAULT true; + +ALTER TABLE Microservices ADD COLUMN host_network_mode BOOLEAN DEFAULT false; +ALTER TABLE Microservices ADD COLUMN is_privileged BOOLEAN DEFAULT false; +ALTER TABLE Microservices DROP COLUMN root_host_access; + +CREATE TABLE IF NOT EXISTS Events ( + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + timestamp BIGINT NOT NULL, + event_type VARCHAR(20) NOT NULL, + endpoint_type VARCHAR(10) NOT NULL, + actor_id VARCHAR(255), + method VARCHAR(10), + resource_type VARCHAR(50), + resource_id VARCHAR(255), + endpoint_path TEXT NOT NULL, + ip_address VARCHAR(45), + status VARCHAR(20) NOT NULL, + status_code INT, + status_message TEXT, + request_id VARCHAR(255), + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_events_timestamp ON Events (timestamp); +CREATE INDEX idx_events_endpoint_type ON Events (endpoint_type); +CREATE INDEX idx_events_actor_id ON Events (actor_id); +CREATE INDEX idx_events_resource_type ON Events (resource_type); +CREATE INDEX idx_events_status ON Events (status); +CREATE INDEX idx_events_method ON Events (method); +CREATE INDEX idx_events_event_type ON Events (event_type); +CREATE INDEX idx_events_created_at ON Events (created_at); + +COMMIT; \ No newline at end of file diff --git a/src/data/migrations/postgres/db_migration_pg_v1.0.6.sql b/src/data/migrations/postgres/db_migration_pg_v1.0.6.sql new file mode 100644 index 000000000..405d70d26 --- /dev/null +++ b/src/data/migrations/postgres/db_migration_pg_v1.0.6.sql @@ -0,0 +1,844 @@ +CREATE TABLE IF NOT EXISTS "Flows" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(255) UNIQUE, + description VARCHAR(255) DEFAULT '', + is_activated BOOLEAN DEFAULT false, + is_system BOOLEAN DEFAULT false, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + +CREATE TABLE IF NOT EXISTS "Registries" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + url VARCHAR(255), + is_public BOOLEAN, + user_name TEXT, + password TEXT, + user_email TEXT +); + + +CREATE TABLE IF NOT EXISTS "CatalogItems" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(255) UNIQUE, + description VARCHAR(255), + category TEXT, + config_example VARCHAR(255) DEFAULT '{}', + publisher TEXT, + disk_required BIGINT DEFAULT 0, + ram_required BIGINT DEFAULT 0, + picture VARCHAR(255) DEFAULT 'images/shared/default.png', + is_public BOOLEAN DEFAULT false, + registry_id INT, + FOREIGN KEY (registry_id) REFERENCES "Registries" (id) ON DELETE SET NULL +); + +CREATE INDEX idx_catalog_item_registry_id ON "CatalogItems" (registry_id); + + +CREATE TABLE IF NOT EXISTS "FogTypes" ( + id INT PRIMARY KEY, + name TEXT, + image TEXT, + description TEXT, + network_catalog_item_id INT, + hal_catalog_item_id INT, + bluetooth_catalog_item_id INT, + FOREIGN KEY (network_catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE, + FOREIGN KEY (hal_catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE, + FOREIGN KEY (bluetooth_catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_type_network_catalog_item_id ON "FogTypes" (network_catalog_item_id); +CREATE INDEX idx_fog_type_hal_catalog_item_id ON "FogTypes" (hal_catalog_item_id); +CREATE INDEX idx_fog_type_bluetooth_catalog_item_id ON "FogTypes" (bluetooth_catalog_item_id); + + +CREATE TABLE IF NOT EXISTS "Fogs" ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) DEFAULT 'Unnamed ioFog 1', + location TEXT, + gps_mode TEXT, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + description TEXT, + last_active BIGINT, + daemon_status VARCHAR(36) DEFAULT 'NOT_PROVISIONED', + daemon_operating_duration BIGINT DEFAULT 0, + daemon_last_start BIGINT, + memory_usage DOUBLE PRECISION DEFAULT 0.000, + disk_usage DOUBLE PRECISION DEFAULT 0.000, + cpu_usage DOUBLE PRECISION DEFAULT 0.00, + memory_violation TEXT, + disk_violation TEXT, + cpu_violation TEXT, + system_available_disk BIGINT, + system_available_memory BIGINT, + system_total_cpu DOUBLE PRECISION, + security_status VARCHAR(36) DEFAULT 'OK', + security_violation_info VARCHAR(36) DEFAULT 'No violation', + catalog_item_status TEXT, + repository_count BIGINT DEFAULT 0, + repository_status TEXT, + system_time BIGINT, + last_status_time BIGINT, + ip_address VARCHAR(36) DEFAULT '0.0.0.0', + ip_address_external VARCHAR(36) DEFAULT '0.0.0.0', + host VARCHAR(36), + processed_messages BIGINT DEFAULT 0, + catalog_item_message_counts TEXT, + message_speed DOUBLE PRECISION DEFAULT 0.000, + last_command_time BIGINT, + network_interface VARCHAR(36) DEFAULT 'dynamic', + docker_url VARCHAR(255) DEFAULT 'unix:///var/run/docker.sock', + disk_limit DOUBLE PRECISION DEFAULT 50, + disk_directory VARCHAR(255) DEFAULT '/var/lib/iofog/', + memory_limit DOUBLE PRECISION DEFAULT 4096, + cpu_limit DOUBLE PRECISION DEFAULT 80, + log_limit DOUBLE PRECISION DEFAULT 10, + log_directory VARCHAR(255) DEFAULT '/var/log/iofog/', + bluetooth BOOLEAN DEFAULT FALSE, + hal BOOLEAN DEFAULT FALSE, + log_file_count BIGINT DEFAULT 10, + version TEXT, + is_ready_to_upgrade BOOLEAN DEFAULT TRUE, + is_ready_to_rollback BOOLEAN DEFAULT FALSE, + status_frequency INT DEFAULT 10, + change_frequency INT DEFAULT 20, + device_scan_frequency INT DEFAULT 20, + tunnel VARCHAR(255) DEFAULT '', + isolated_docker_container BOOLEAN DEFAULT FALSE, + docker_pruning_freq INT DEFAULT 0, + available_disk_threshold DOUBLE PRECISION DEFAULT 20, + log_level VARCHAR(10) DEFAULT 'INFO', + is_system BOOLEAN DEFAULT FALSE, + router_id INT DEFAULT 0, + time_zone VARCHAR(36) DEFAULT 'Etc/UTC', + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + fog_type_id INT DEFAULT 0, + FOREIGN KEY (fog_type_id) REFERENCES "FogTypes" (id) +); + +CREATE INDEX idx_fog_fog_type_id ON "Fogs" (fog_type_id); + +CREATE TABLE IF NOT EXISTS "ChangeTrackings" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + microservice_config BOOLEAN DEFAULT false, + reboot BOOLEAN DEFAULT false, + deletenode BOOLEAN DEFAULT false, + version BOOLEAN DEFAULT false, + microservice_list BOOLEAN DEFAULT false, + config BOOLEAN DEFAULT false, + routing BOOLEAN DEFAULT false, + registries BOOLEAN DEFAULT false, + tunnel BOOLEAN DEFAULT false, + diagnostics BOOLEAN DEFAULT false, + router_changed BOOLEAN DEFAULT false, + image_snapshot BOOLEAN DEFAULT false, + prune BOOLEAN DEFAULT false, + linked_edge_resources BOOLEAN DEFAULT false, + last_updated VARCHAR(255) DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_change_tracking_iofog_uuid ON "ChangeTrackings" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "FogAccessTokens" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + expiration_time BIGINT, + token TEXT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_access_tokens_iofogUuid ON "FogAccessTokens" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "FogProvisionKeys" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + provisioning_string VARCHAR(100), + expiration_time BIGINT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_provision_keys_iofogUuid ON "FogProvisionKeys" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "FogVersionCommands" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + version_command VARCHAR(100), + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_version_commands_iofogUuid ON "FogVersionCommands" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "HWInfos" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + info TEXT, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_hw_infos_iofogUuid ON "HWInfos" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "USBInfos" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + info TEXT, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_usb_infos_iofogUuid ON "USBInfos" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "Tunnels" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + username TEXT, + password TEXT, + host TEXT, + remote_port INT, + local_port INT DEFAULT 22, + rsa_key TEXT, + closed BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_tunnels_iofogUuid ON "Tunnels" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "Microservices" ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + config TEXT, + name VARCHAR(255) DEFAULT 'New Microservice', + config_last_updated BIGINT, + rebuild BOOLEAN DEFAULT false, + root_host_access BOOLEAN DEFAULT false, + log_size BIGINT DEFAULT 0, + image_snapshot VARCHAR(255) DEFAULT '', + delete BOOLEAN DEFAULT false, + delete_with_cleanup BOOLEAN DEFAULT false, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + catalog_item_id INT, + registry_id INT DEFAULT 1, + iofog_uuid VARCHAR(36), + application_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE, + FOREIGN KEY (registry_id) REFERENCES "Registries" (id) ON DELETE SET NULL, + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES "Flows" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservices_catalogItemId ON "Microservices" (catalog_item_id); +CREATE INDEX idx_microservices_registryId ON "Microservices" (registry_id); +CREATE INDEX idx_microservices_iofogUuid ON "Microservices" (iofog_uuid); +CREATE INDEX idx_microservices_applicationId ON "Microservices" (application_id); + +CREATE TABLE IF NOT EXISTS "MicroserviceArgs" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + cmd TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_args_microserviceUuid ON "MicroserviceArgs" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "MicroserviceEnvs" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + key TEXT, + value TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_envs_microserviceUuid ON "MicroserviceEnvs" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "MicroserviceExtraHost" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + template_type TEXT, + name TEXT, + public_port INT, + template TEXT, + value TEXT, + microservice_uuid VARCHAR(36), + target_microservice_uuid VARCHAR(36), + target_fog_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_fog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_extra_host_microserviceUuid ON "MicroserviceExtraHost" (microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetMicroserviceUuid ON "MicroserviceExtraHost" (target_microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetFogUuid ON "MicroserviceExtraHost" (target_fog_uuid); + +CREATE TABLE IF NOT EXISTS "MicroservicePorts" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + port_internal INT, + port_external INT, + is_udp BOOLEAN, + is_public BOOLEAN, + is_proxy BOOLEAN, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_port_microserviceUuid ON "MicroservicePorts" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "MicroservicePublicPorts" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + port_id INT UNIQUE, + host_id VARCHAR(255) UNIQUE, + local_proxy_id TEXT, + remote_proxy_id TEXT, + public_port INT, + queue_name TEXT, + schemes VARCHAR(255) DEFAULT '["https"]', + is_tcp BOOLEAN DEFAULT false, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + protocol VARCHAR(255) GENERATED ALWAYS AS (CASE WHEN is_tcp THEN 'tcp' ELSE 'http' END) STORED, + FOREIGN KEY (port_id) REFERENCES "MicroservicePorts" (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_public_port_portId ON "MicroservicePublicPorts" (port_id); +CREATE INDEX idx_microservice_public_port_hostId ON "MicroservicePublicPorts" (host_id); + + +CREATE TABLE IF NOT EXISTS "MicroserviceStatuses" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + status VARCHAR(255) DEFAULT 'QUEUED', + operating_duration BIGINT DEFAULT 0, + start_time BIGINT DEFAULT 0, + cpu_usage DOUBLE PRECISION DEFAULT 0.000, + memory_usage BIGINT DEFAULT 0, + container_id VARCHAR(255) DEFAULT '', + percentage DOUBLE PRECISION DEFAULT 0.00, + error_message TEXT, + microservice_uuid VARCHAR(36), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_status_microserviceUuid ON "MicroserviceStatuses" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "StraceDiagnostics" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + strace_run BOOLEAN, + buffer VARCHAR(255) DEFAULT '', + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_strace_diagnostics_microserviceUuid ON "StraceDiagnostics" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "VolumeMappings" ( + uuid INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + host_destination TEXT, + container_destination TEXT, + access_mode TEXT, + type TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mappings_microserviceUuid ON "VolumeMappings" (microservice_uuid); + + +CREATE TABLE IF NOT EXISTS "CatalogItemImages" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + container_image TEXT, + catalog_item_id INT, + microservice_uuid VARCHAR(36), + fog_type_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE, + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (fog_type_id) REFERENCES "FogTypes" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_image_catalog_item_id ON "CatalogItemImages" (catalog_item_id); +CREATE INDEX idx_catalog_item_image_microservice_uuid ON "CatalogItemImages" (microservice_uuid); +CREATE INDEX idx_catalog_item_image_fog_type_id ON "CatalogItemImages" (fog_type_id); + +CREATE TABLE IF NOT EXISTS "CatalogItemInputTypes" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_input_type_catalog_item_id ON "CatalogItemInputTypes" (catalog_item_id); + +CREATE TABLE IF NOT EXISTS "CatalogItemOutputTypes" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES "CatalogItems" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_output_type_catalog_item_id ON "CatalogItemOutputTypes" (catalog_item_id); + + +CREATE TABLE IF NOT EXISTS "Routings" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + source_microservice_uuid VARCHAR(36), + dest_microservice_uuid VARCHAR(36), + application_id INT, + FOREIGN KEY (source_microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (dest_microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES "Flows" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_routing_sourceMicroserviceUuid ON "Routings" (source_microservice_uuid); +CREATE INDEX idx_routing_destMicroserviceUuid ON "Routings" (dest_microservice_uuid); +CREATE INDEX idx_routing_applicationId ON "Routings" (application_id); + +CREATE TABLE IF NOT EXISTS "Routers" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + is_edge BOOLEAN DEFAULT true, + messaging_port INT DEFAULT 5671, + edge_router_port INT, + inter_router_port INT, + host TEXT, + is_default BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE + +); + +CREATE INDEX idx_router_iofogUuid ON "Routers" (iofog_uuid); + + +CREATE TABLE "RouterConnections" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + source_router INT, + dest_router INT, + created_at TIMESTAMP(0) NOT NULL, + updated_at TIMESTAMP(0) NOT NULL, + FOREIGN KEY (source_router) REFERENCES "Routers"(id) ON DELETE CASCADE, + FOREIGN KEY (dest_router) REFERENCES "Routers"(id) ON DELETE CASCADE +); + +CREATE INDEX idx_routerconnections_sourceRouter ON "RouterConnections" (source_router); +CREATE INDEX idx_routerconnections_destRouter ON "RouterConnections" (dest_router); + + + +CREATE TABLE IF NOT EXISTS "Config" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + key VARCHAR(255) NOT NULL UNIQUE, + value VARCHAR(255) NOT NULL, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + +CREATE INDEX idx_config_key ON "Config" (key); + + +CREATE TABLE IF NOT EXISTS "Tags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + value VARCHAR(255) UNIQUE NOT NULL +); + +CREATE TABLE IF NOT EXISTS "IofogTags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (fog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES "Tags" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_iofogtags_fog_uuid ON "IofogTags" (fog_uuid); +CREATE INDEX idx_iofogtags_tag_id ON "IofogTags" (tag_id); + +CREATE TABLE IF NOT EXISTS "EdgeResources" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + version TEXT, + description TEXT, + display_name TEXT, + display_color TEXT, + display_icon TEXT, + interface_protocol TEXT, + interface_id INT, + custom TEXT +); + + +CREATE TABLE IF NOT EXISTS "AgentEdgeResources" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + edge_resource_id INT, + FOREIGN KEY (fog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE, + FOREIGN KEY (edge_resource_id) REFERENCES "EdgeResources" (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "EdgeResourceOrchestrationTags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + edge_resource_id INT, + tag_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES "EdgeResources" (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES "Tags" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_agentedgeresources_fog_id ON "AgentEdgeResources" (fog_uuid); +CREATE INDEX idx_agentedgeresources_edge_resource_id ON "AgentEdgeResources" (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_edge_resource_id ON "EdgeResourceOrchestrationTags" (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_tag_id ON "EdgeResourceOrchestrationTags" (tag_id); + +CREATE TABLE IF NOT EXISTS "HTTPBasedResourceInterfaces" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + edge_resource_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES "EdgeResources" (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "HTTPBasedResourceInterfaceEndpoints" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + interface_id INT, + name TEXT, + description TEXT, + method TEXT, + url TEXT, + requestType TEXT, + responseType TEXT, + requestPayloadExample TEXT, + responsePayloadExample TEXT, + FOREIGN KEY (interface_id) REFERENCES "HTTPBasedResourceInterfaces" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_httpbasedresourceinterfaces_edge_resource_id ON "HTTPBasedResourceInterfaces" (edge_resource_id); +CREATE INDEX idx_httpbasedresourceinterfaceendpoints_interface_id ON "HTTPBasedResourceInterfaceEndpoints" (interface_id); + + +CREATE TABLE IF NOT EXISTS "ApplicationTemplates" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL DEFAULT 'new-application', + description VARCHAR(255) DEFAULT '', + schema_version VARCHAR(255) DEFAULT '', + application_json TEXT, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) + +); + + +CREATE TABLE IF NOT EXISTS "ApplicationTemplateVariables" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + application_template_id INT NOT NULL, + key TEXT, + description VARCHAR(255) DEFAULT '', + default_value VARCHAR(255), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (application_template_id) REFERENCES "ApplicationTemplates" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_applicationtemplatevariables_application_template_id ON "ApplicationTemplateVariables" (application_template_id); + +CREATE TABLE IF NOT EXISTS "MicroserviceCdiDevices" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + cdi_devices TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_cdiDevices_microserviceUuid ON "MicroserviceCdiDevices" (microservice_uuid); + +ALTER TABLE "Microservices" +ADD COLUMN run_as_user TEXT DEFAULT NULL, +ADD COLUMN platform TEXT DEFAULT NULL, +ADD COLUMN runtime TEXT DEFAULT NULL; + + +CREATE TABLE IF NOT EXISTS "MicroservicePubTags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES "Tags" (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "MicroserviceSubTags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES "Tags" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservicepubtags_microservice_uuid ON "MicroservicePubTags" (microservice_uuid); +CREATE INDEX idx_microservicesubtags_microservice_uuid ON "MicroserviceSubTags" (microservice_uuid); +CREATE INDEX idx_microservicepubtags_tag_id ON "MicroservicePubTags" (tag_id); +CREATE INDEX idx_microservicesubtags_tag_id ON "MicroserviceSubTags" (tag_id); + +CREATE TABLE IF NOT EXISTS "MicroserviceCapAdd" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + cap_add TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capAdd_microserviceUuid ON "MicroserviceCapAdd" (microservice_uuid); + +CREATE TABLE IF NOT EXISTS "MicroserviceCapDrop" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + cap_drop TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capDrop_microserviceUuid ON "MicroserviceCapDrop" (microservice_uuid); + +ALTER TABLE "Microservices" +ADD COLUMN annotations TEXT; + +CREATE TABLE IF NOT EXISTS "FogPublicKeys" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + public_key TEXT, + iofog_uuid VARCHAR(36), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_public_keys_iofogUuid ON "FogPublicKeys" (iofog_uuid); + +CREATE TABLE IF NOT EXISTS "FogUsedTokens" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + jti VARCHAR(255) NOT NULL, + iofog_uuid VARCHAR(36), + expiry_time BIGINT NOT NULL, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (iofog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_used_tokens_iofogUuid ON "FogUsedTokens" (iofog_uuid); + +ALTER TABLE "MicroserviceStatuses" +ADD COLUMN ip_address TEXT; + +DROP TABLE IF EXISTS "FogAccessTokens"; + +CREATE TABLE IF NOT EXISTS "Secrets" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL CHECK (type IN ('Opaque', 'tls')), + data TEXT NOT NULL, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + +CREATE INDEX idx_secrets_name ON "Secrets" (name); + +CREATE TABLE IF NOT EXISTS "Certificates" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name TEXT UNIQUE NOT NULL, + subject TEXT NOT NULL, + is_ca BOOLEAN DEFAULT false, + signed_by_id INT, + hosts TEXT, + valid_from TIMESTAMP(0) NOT NULL, + valid_to TIMESTAMP(0) NOT NULL, + serial_number TEXT NOT NULL, + secret_id INT, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (signed_by_id) REFERENCES "Certificates" (id) ON DELETE SET NULL, + FOREIGN KEY (secret_id) REFERENCES "Secrets" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_certificates_name ON "Certificates" (name); +CREATE INDEX idx_certificates_valid_to ON "Certificates" (valid_to); +CREATE INDEX idx_certificates_is_ca ON "Certificates" (is_ca); +CREATE INDEX idx_certificates_signed_by_id ON "Certificates" (signed_by_id); +CREATE INDEX idx_certificates_secret_id ON "Certificates" (secret_id); + +CREATE TABLE IF NOT EXISTS "Services" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name TEXT UNIQUE NOT NULL, + type TEXT NOT NULL, + resource TEXT NOT NULL, + target_port INTEGER NOT NULL, + service_port INTEGER, + k8s_type TEXT, + bridge_port INTEGER, + default_bridge TEXT, + service_endpoint TEXT, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + +CREATE INDEX idx_services_name ON "Services" (name); +CREATE INDEX idx_services_id ON "Services" (id); + +CREATE TABLE IF NOT EXISTS "ServiceTags" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + service_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (service_id) REFERENCES "Services" (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES "Tags" (id) ON DELETE CASCADE +); + +CREATE INDEX idx_service_tags_service_id ON "ServiceTags" (service_id); +CREATE INDEX idx_service_tags_tag_id ON "ServiceTags" (tag_id); + + +ALTER TABLE "Fogs" ADD COLUMN container_engine VARCHAR(36); +ALTER TABLE "Fogs" ADD COLUMN deployment_type VARCHAR(36); + +ALTER TABLE "MicroserviceExtraHost" DROP COLUMN IF EXISTS public_port; +ALTER TABLE "MicroservicePorts" DROP COLUMN IF EXISTS is_public; +ALTER TABLE "MicroservicePorts" DROP COLUMN IF EXISTS is_proxy; + +DROP TABLE IF EXISTS "MicroservicePublicPorts"; + +ALTER TABLE "MicroserviceEnvs" ADD COLUMN value_from_secret TEXT; +ALTER TABLE "MicroserviceEnvs" ADD COLUMN value_from_config_map TEXT; + +CREATE TABLE IF NOT EXISTS "ConfigMaps" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + immutable BOOLEAN DEFAULT false, + data TEXT NOT NULL, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + +CREATE INDEX idx_config_maps_name ON "ConfigMaps" (name); + +CREATE TABLE IF NOT EXISTS "VolumeMounts" ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + config_map_name VARCHAR(255), + secret_name VARCHAR(255), + version INT DEFAULT 1, + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (config_map_name) REFERENCES "ConfigMaps" (name) ON DELETE CASCADE, + FOREIGN KEY (secret_name) REFERENCES "Secrets" (name) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mounts_uuid ON "VolumeMounts" (uuid); +CREATE INDEX idx_volume_mounts_config_map_name ON "VolumeMounts" (config_map_name); +CREATE INDEX idx_volume_mounts_secret_name ON "VolumeMounts" (secret_name); + +CREATE TABLE IF NOT EXISTS "FogVolumeMounts" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + fog_uuid VARCHAR(36), + volume_mount_uuid VARCHAR(36), + FOREIGN KEY (fog_uuid) REFERENCES "Fogs" (uuid) ON DELETE CASCADE, + FOREIGN KEY (volume_mount_uuid) REFERENCES "VolumeMounts" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_volume_mounts_fog_uuid ON "FogVolumeMounts" (fog_uuid); +CREATE INDEX idx_fog_volume_mounts_volume_mount_uuid ON "FogVolumeMounts" (volume_mount_uuid); + +ALTER TABLE "Fogs" ADD COLUMN active_volume_mounts BIGINT DEFAULT 0; +ALTER TABLE "Fogs" ADD COLUMN volume_mount_last_update BIGINT DEFAULT 0; + +ALTER TABLE "ChangeTrackings" ADD COLUMN volume_mounts BOOLEAN DEFAULT false; +ALTER TABLE "ChangeTrackings" ADD COLUMN exec_sessions BOOLEAN DEFAULT false; + +ALTER TABLE "Services" ADD COLUMN provisioning_status VARCHAR(36) DEFAULT 'pending'; +ALTER TABLE "Services" ADD COLUMN provisioning_error TEXT; + +ALTER TABLE "Fogs" ADD COLUMN warning_message TEXT DEFAULT 'HEALTHY'; +ALTER TABLE "Fogs" ADD COLUMN gps_device VARCHAR(36); +ALTER TABLE "Fogs" ADD COLUMN gps_scan_frequency INT DEFAULT 60; +ALTER TABLE "Fogs" ADD COLUMN edge_guard_frequency INT DEFAULT 0; + +ALTER TABLE "Microservices" ADD COLUMN pid_mode VARCHAR(36); +ALTER TABLE "Microservices" ADD COLUMN ipc_mode VARCHAR(36); +ALTER TABLE "Microservices" ADD COLUMN exec_enabled BOOLEAN DEFAULT false; + +ALTER TABLE "MicroserviceStatuses" ADD COLUMN exec_session_ids TEXT; + +ALTER TABLE "Microservices" ADD COLUMN schedule INT DEFAULT 50; + +CREATE TABLE IF NOT EXISTS "MicroserviceExecStatuses" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + status VARCHAR(255) DEFAULT 'INACTIVE', + exec_session_id VARCHAR(255), + microservice_uuid VARCHAR(36), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_exec_status_microservice_uuid ON "MicroserviceExecStatuses" (microservice_uuid); + +ALTER TABLE "Fogs" ADD COLUMN gps_status VARCHAR(32); + +ALTER TABLE "Microservices" ADD COLUMN cpu_set_cpus TEXT; +ALTER TABLE "Microservices" ADD COLUMN memory_limit DOUBLE PRECISION; + +CREATE TABLE IF NOT EXISTS "MicroserviceHealthChecks" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + test TEXT, + interval DOUBLE PRECISION, + timeout DOUBLE PRECISION, + start_period DOUBLE PRECISION, + start_interval DOUBLE PRECISION, + retries INT, + microservice_uuid VARCHAR(36), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0), + FOREIGN KEY (microservice_uuid) REFERENCES "Microservices" (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_health_check_microservice_uuid ON "MicroserviceHealthChecks" (microservice_uuid); + +ALTER TABLE "MicroserviceStatuses" ADD COLUMN health_status TEXT; + +ALTER TABLE "Microservices" ADD COLUMN is_activated BOOLEAN DEFAULT true; + +ALTER TABLE "Microservices" ADD COLUMN host_network_mode BOOLEAN DEFAULT false; +ALTER TABLE "Microservices" ADD COLUMN is_privileged BOOLEAN DEFAULT false; +ALTER TABLE "Microservices" DROP COLUMN root_host_access; + +CREATE TABLE IF NOT EXISTS "Events" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, + timestamp BIGINT NOT NULL, + event_type VARCHAR(20) NOT NULL, + endpoint_type VARCHAR(10) NOT NULL, + actor_id VARCHAR(255), + method VARCHAR(10), + resource_type VARCHAR(50), + resource_id VARCHAR(255), + endpoint_path TEXT NOT NULL, + ip_address VARCHAR(45), + status VARCHAR(20) NOT NULL, + status_code INT, + status_message TEXT, + request_id VARCHAR(255), + created_at TIMESTAMP(0), + updated_at TIMESTAMP(0) +); + + +CREATE INDEX idx_events_timestamp ON "Events" (timestamp); +CREATE INDEX idx_events_endpoint_type ON "Events" (endpoint_type); +CREATE INDEX idx_events_actor_id ON "Events" (actor_id); +CREATE INDEX idx_events_resource_type ON "Events" (resource_type); +CREATE INDEX idx_events_status ON "Events" (status); +CREATE INDEX idx_events_method ON "Events" (method); +CREATE INDEX idx_events_event_type ON "Events" (event_type); +CREATE INDEX idx_events_created_at ON "Events" (created_at); + diff --git a/src/data/migrations/sqlite/db_migration_sqlite_v1.0.6.sql b/src/data/migrations/sqlite/db_migration_sqlite_v1.0.6.sql new file mode 100644 index 000000000..8e3ca8e18 --- /dev/null +++ b/src/data/migrations/sqlite/db_migration_sqlite_v1.0.6.sql @@ -0,0 +1,830 @@ +CREATE TABLE IF NOT EXISTS Flows ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) UNIQUE, + description VARCHAR(255) DEFAULT '', + is_activated BOOLEAN DEFAULT false, + is_system BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME +); + +CREATE TABLE IF NOT EXISTS Registries ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + url VARCHAR(255), + is_public BOOLEAN, + user_name TEXT, + password TEXT, + user_email TEXT +); + + +CREATE TABLE IF NOT EXISTS CatalogItems ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) UNIQUE, + description VARCHAR(255), + category TEXT, + config_example VARCHAR(255) DEFAULT '{}', + publisher TEXT, + disk_required BIGINT DEFAULT 0, + ram_required BIGINT DEFAULT 0, + picture VARCHAR(255) DEFAULT 'images/shared/default.png', + is_public BOOLEAN DEFAULT false, + registry_id INT, + FOREIGN KEY (registry_id) REFERENCES Registries (id) ON DELETE SET NULL +); + +CREATE INDEX idx_catalog_item_registry_id ON CatalogItems (registry_id); + + +CREATE TABLE IF NOT EXISTS FogTypes ( + id INT PRIMARY KEY, + name TEXT, + image TEXT, + description TEXT, + network_catalog_item_id INT, + hal_catalog_item_id INT, + bluetooth_catalog_item_id INT, + FOREIGN KEY (network_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (hal_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (bluetooth_catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_type_network_catalog_item_id ON FogTypes (network_catalog_item_id); +CREATE INDEX idx_fog_type_hal_catalog_item_id ON FogTypes (hal_catalog_item_id); +CREATE INDEX idx_fog_type_bluetooth_catalog_item_id ON FogTypes (bluetooth_catalog_item_id); + + +CREATE TABLE IF NOT EXISTS Fogs ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) DEFAULT 'Unnamed ioFog 1', + location TEXT, + gps_mode TEXT, + latitude FLOAT, + longitude FLOAT, + description TEXT, + last_active BIGINT, + daemon_status VARCHAR(36) DEFAULT 'NOT_PROVISIONED', + daemon_operating_duration BIGINT DEFAULT 0, + daemon_last_start BIGINT, + memory_usage FLOAT DEFAULT 0.000, + disk_usage FLOAT DEFAULT 0.000, + cpu_usage FLOAT DEFAULT 0.00, + memory_violation TEXT, + disk_violation TEXT, + cpu_violation TEXT, + system_available_disk BIGINT, + system_available_memory BIGINT, + system_total_cpu FLOAT, + security_status VARCHAR(36) DEFAULT 'OK', + security_violation_info VARCHAR(36) DEFAULT 'No violation', + catalog_item_status TEXT, + repository_count BIGINT DEFAULT 0, + repository_status TEXT, + system_time BIGINT, + last_status_time BIGINT, + ip_address VARCHAR(36) DEFAULT '0.0.0.0', + ip_address_external VARCHAR(36) DEFAULT '0.0.0.0', + host VARCHAR(36), + processed_messages BIGINT DEFAULT 0, + catalog_item_message_counts TEXT, + message_speed FLOAT DEFAULT 0.000, + last_command_time BIGINT, + network_interface VARCHAR(36) DEFAULT 'dynamic', + docker_url VARCHAR(255) DEFAULT 'unix:///var/run/docker.sock', + disk_limit FLOAT DEFAULT 50, + disk_directory VARCHAR(255) DEFAULT '/var/lib/iofog/', + memory_limit FLOAT DEFAULT 4096, + cpu_limit FLOAT DEFAULT 80, + log_limit FLOAT DEFAULT 10, + log_directory VARCHAR(255) DEFAULT '/var/log/iofog/', + bluetooth BOOLEAN DEFAULT FALSE, + hal BOOLEAN DEFAULT FALSE, + log_file_count BIGINT DEFAULT 10, + `version` TEXT, + is_ready_to_upgrade BOOLEAN DEFAULT TRUE, + is_ready_to_rollback BOOLEAN DEFAULT FALSE, + status_frequency INT DEFAULT 10, + change_frequency INT DEFAULT 20, + device_scan_frequency INT DEFAULT 20, + tunnel VARCHAR(255) DEFAULT '', + isolated_docker_container BOOLEAN DEFAULT FALSE, + docker_pruning_freq INT DEFAULT 0, + available_disk_threshold FLOAT DEFAULT 20, + log_level VARCHAR(10) DEFAULT 'INFO', + is_system BOOLEAN DEFAULT FALSE, + router_id INT DEFAULT 0, + time_zone VARCHAR(36) DEFAULT 'Etc/UTC', + created_at DATETIME, + updated_at DATETIME, + fog_type_id INT DEFAULT 0, + FOREIGN KEY (fog_type_id) REFERENCES FogTypes (id) +); + +CREATE INDEX idx_fog_fog_type_id ON Fogs (fog_type_id); + +CREATE TABLE IF NOT EXISTS ChangeTrackings ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + microservice_config BOOLEAN DEFAULT false, + reboot BOOLEAN DEFAULT false, + deletenode BOOLEAN DEFAULT false, + version BOOLEAN DEFAULT false, + microservice_list BOOLEAN DEFAULT false, + config BOOLEAN DEFAULT false, + routing BOOLEAN DEFAULT false, + registries BOOLEAN DEFAULT false, + tunnel BOOLEAN DEFAULT false, + diagnostics BOOLEAN DEFAULT false, + router_changed BOOLEAN DEFAULT false, + image_snapshot BOOLEAN DEFAULT false, + prune BOOLEAN DEFAULT false, + linked_edge_resources BOOLEAN DEFAULT false, + last_updated VARCHAR(255) DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_change_tracking_iofog_uuid ON ChangeTrackings (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogAccessTokens ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + expiration_time BIGINT, + token TEXT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_access_tokens_iofogUuid ON FogAccessTokens (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogProvisionKeys ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + provisioning_string VARCHAR(100), + expiration_time BIGINT, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_provision_keys_iofogUuid ON FogProvisionKeys (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogVersionCommands ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + version_command VARCHAR(100), + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_version_commands_iofogUuid ON FogVersionCommands (iofog_uuid); + +CREATE TABLE IF NOT EXISTS HWInfos ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + info TEXT, + created_at DATETIME, + updated_at DATETIME, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_hw_infos_iofogUuid ON HWInfos (iofog_uuid); + +CREATE TABLE IF NOT EXISTS USBInfos ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + info TEXT, + created_at DATETIME, + updated_at DATETIME, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_usb_infos_iofogUuid ON USBInfos (iofog_uuid); + +CREATE TABLE IF NOT EXISTS Tunnels ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + username TEXT, + password TEXT, + host TEXT, + remote_port INT, + local_port INT DEFAULT 22, + rsa_key TEXT, + closed BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_tunnels_iofogUuid ON Tunnels (iofog_uuid); + +CREATE TABLE IF NOT EXISTS Microservices ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + config TEXT, + name VARCHAR(255) DEFAULT 'New Microservice', + config_last_updated BIGINT, + rebuild BOOLEAN DEFAULT false, + root_host_access BOOLEAN DEFAULT false, + log_size BIGINT DEFAULT 0, + image_snapshot VARCHAR(255) DEFAULT '', + `delete` BOOLEAN DEFAULT false, + delete_with_cleanup BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME, + catalog_item_id INT, + registry_id INT DEFAULT 1, + iofog_uuid VARCHAR(36), + application_id INT, + run_as_user TEXT, + platform TEXT, + runtime TEXT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (registry_id) REFERENCES Registries (id) ON DELETE SET NULL, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES Flows (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservices_catalogItemId ON Microservices (catalog_item_id); +CREATE INDEX idx_microservices_registryId ON Microservices (registry_id); +CREATE INDEX idx_microservices_iofogUuid ON Microservices (iofog_uuid); +CREATE INDEX idx_microservices_applicationId ON Microservices (application_id); + +CREATE TABLE IF NOT EXISTS MicroserviceArgs ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + cmd TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_args_microserviceUuid ON MicroserviceArgs (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceEnvs ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `key` TEXT, + `value` TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_envs_microserviceUuid ON MicroserviceEnvs (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceExtraHost ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + template_type TEXT, + name TEXT, + template TEXT, + `value` TEXT, + microservice_uuid VARCHAR(36), + target_microservice_uuid VARCHAR(36), + target_fog_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (target_fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_extra_host_microserviceUuid ON MicroserviceExtraHost (microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetMicroserviceUuid ON MicroserviceExtraHost (target_microservice_uuid); +CREATE INDEX idx_microservice_extra_host_targetFogUuid ON MicroserviceExtraHost (target_fog_uuid); + +CREATE TABLE IF NOT EXISTS MicroservicePorts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + port_internal INT, + port_external INT, + is_udp BOOLEAN, + created_at DATETIME, + updated_at DATETIME, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_port_microserviceUuid ON MicroservicePorts (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroservicePublicPorts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + port_id INT UNIQUE, + host_id VARCHAR(255) UNIQUE, + local_proxy_id TEXT, + remote_proxy_id TEXT, + public_port INT, + queue_name TEXT, + schemes VARCHAR(255) DEFAULT '["https"]', + is_tcp BOOLEAN DEFAULT false, + created_at DATETIME, + updated_at DATETIME, + protocol VARCHAR(255) AS (CASE WHEN is_tcp THEN 'tcp' ELSE 'http' END) VIRTUAL, + FOREIGN KEY (port_id) REFERENCES MicroservicePorts (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_public_port_portId ON MicroservicePublicPorts (port_id); +CREATE INDEX idx_microservice_public_port_hostId ON MicroservicePublicPorts (host_id); + + +CREATE TABLE IF NOT EXISTS MicroserviceStatuses ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + status VARCHAR(255) DEFAULT 'QUEUED', + operating_duration BIGINT DEFAULT 0, + start_time BIGINT DEFAULT 0, + cpu_usage FLOAT DEFAULT 0.000, + memory_usage BIGINT DEFAULT 0, + container_id VARCHAR(255) DEFAULT '', + percentage FLOAT DEFAULT 0.00, + error_message TEXT, + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_status_microserviceUuid ON MicroserviceStatuses (microservice_uuid); + +CREATE TABLE IF NOT EXISTS StraceDiagnostics ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + strace_run BOOLEAN, + buffer VARCHAR(255) DEFAULT '', + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_strace_diagnostics_microserviceUuid ON StraceDiagnostics (microservice_uuid); + +CREATE TABLE IF NOT EXISTS VolumeMappings ( + uuid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + host_destination TEXT, + container_destination TEXT, + access_mode TEXT, + type TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mappings_microserviceUuid ON VolumeMappings (microservice_uuid); + + +CREATE TABLE IF NOT EXISTS CatalogItemImages ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + container_image TEXT, + catalog_item_id INT, + microservice_uuid VARCHAR(36), + fog_type_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (fog_type_id) REFERENCES FogTypes (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_image_catalog_item_id ON CatalogItemImages (catalog_item_id); +CREATE INDEX idx_catalog_item_image_microservice_uuid ON CatalogItemImages (microservice_uuid); +CREATE INDEX idx_catalog_item_image_fog_type_id ON CatalogItemImages (fog_type_id); + +CREATE TABLE IF NOT EXISTS CatalogItemInputTypes ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_input_type_catalog_item_id ON CatalogItemInputTypes (catalog_item_id); + +CREATE TABLE IF NOT EXISTS CatalogItemOutputTypes ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + info_type TEXT, + info_format TEXT, + catalog_item_id INT, + FOREIGN KEY (catalog_item_id) REFERENCES CatalogItems (id) ON DELETE CASCADE +); + +CREATE INDEX idx_catalog_item_output_type_catalog_item_id ON CatalogItemOutputTypes (catalog_item_id); + + +CREATE TABLE IF NOT EXISTS Routings ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT NOT NULL, + source_microservice_uuid VARCHAR(36), + dest_microservice_uuid VARCHAR(36), + application_id INT, + FOREIGN KEY (source_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (dest_microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (application_id) REFERENCES Flows (id) ON DELETE CASCADE +); + +CREATE INDEX idx_routing_sourceMicroserviceUuid ON Routings (source_microservice_uuid); +CREATE INDEX idx_routing_destMicroserviceUuid ON Routings (dest_microservice_uuid); +CREATE INDEX idx_routing_applicationId ON Routings (application_id); + +CREATE TABLE IF NOT EXISTS Routers ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + is_edge BOOLEAN DEFAULT true, + messaging_port INT DEFAULT 5671, + edge_router_port INT, + inter_router_port INT, + host TEXT, + is_default BOOLEAN DEFAULT false, + iofog_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE + +); + +CREATE INDEX idx_router_iofogUuid ON Routers (iofog_uuid); + + +CREATE TABLE RouterConnections ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + source_router INT, + dest_router INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + FOREIGN KEY (source_router) REFERENCES Routers(id) ON DELETE CASCADE, + FOREIGN KEY (dest_router) REFERENCES Routers(id) ON DELETE CASCADE +); + +CREATE INDEX idx_routerconnections_sourceRouter ON RouterConnections (source_router); +CREATE INDEX idx_routerconnections_destRouter ON RouterConnections (dest_router); + + + +CREATE TABLE IF NOT EXISTS Config ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `key` VARCHAR(255) NOT NULL UNIQUE, + value VARCHAR(255) NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_config_key ON Config (`key`); + + +CREATE TABLE IF NOT EXISTS Tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + value VARCHAR(255) UNIQUE NOT NULL +); + +CREATE TABLE IF NOT EXISTS IofogTags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + fog_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_iofogtags_fog_uuid ON IofogTags (fog_uuid); +CREATE INDEX idx_iofogtags_tag_id ON IofogTags (tag_id); + +CREATE TABLE IF NOT EXISTS EdgeResources ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) NOT NULL, + version TEXT, + description TEXT, + display_name TEXT, + display_color TEXT, + display_icon TEXT, + interface_protocol TEXT, + interface_id INT, + custom TEXT +); + + +CREATE TABLE IF NOT EXISTS AgentEdgeResources ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + fog_uuid VARCHAR(36), + edge_resource_id INT, + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS EdgeResourceOrchestrationTags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + edge_resource_id INT, + tag_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_agentedgeresources_fog_id ON AgentEdgeResources (fog_uuid); +CREATE INDEX idx_agentedgeresources_edge_resource_id ON AgentEdgeResources (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_edge_resource_id ON EdgeResourceOrchestrationTags (edge_resource_id); +CREATE INDEX idx_edgeresourceorchestrationtags_tag_id ON EdgeResourceOrchestrationTags (tag_id); + +CREATE TABLE IF NOT EXISTS HTTPBasedResourceInterfaces ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + edge_resource_id INT, + FOREIGN KEY (edge_resource_id) REFERENCES EdgeResources (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS HTTPBasedResourceInterfaceEndpoints ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + interface_id INT, + name TEXT, + description TEXT, + `method` TEXT, + url TEXT, + requestType TEXT, + responseType TEXT, + requestPayloadExample TEXT, + responsePayloadExample TEXT, + FOREIGN KEY (interface_id) REFERENCES HTTPBasedResourceInterfaces (id) ON DELETE CASCADE +); + +CREATE INDEX idx_httpbasedresourceinterfaces_edge_resource_id ON HTTPBasedResourceInterfaces (edge_resource_id); +CREATE INDEX idx_httpbasedresourceinterfaceendpoints_interface_id ON HTTPBasedResourceInterfaceEndpoints (interface_id); + + +CREATE TABLE IF NOT EXISTS ApplicationTemplates ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL DEFAULT 'new-application', + description VARCHAR(255) DEFAULT '', + schema_version VARCHAR(255) DEFAULT '', + application_json LONGTEXT, + created_at DATETIME, + updated_at DATETIME + +); + + +CREATE TABLE IF NOT EXISTS ApplicationTemplateVariables ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + application_template_id INT NOT NULL, + `key` TEXT, + description VARCHAR(255) DEFAULT '', + default_value VARCHAR(255), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (application_template_id) REFERENCES ApplicationTemplates (id) ON DELETE CASCADE +); + +CREATE INDEX idx_applicationtemplatevariables_application_template_id ON ApplicationTemplateVariables (application_template_id); + +CREATE TABLE IF NOT EXISTS MicroserviceCdiDevices ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + cdi_devices TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_cdiDevices_microserviceUuid ON MicroserviceCdiDevices (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroservicePubTags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS MicroserviceSubTags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + microservice_uuid VARCHAR(36), + tag_id INT, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_microservicepubtags_microservice_uuid ON MicroservicePubTags (microservice_uuid); +CREATE INDEX idx_microservicesubtags_microservice_uuid ON MicroservicesubTags (microservice_uuid); +CREATE INDEX idx_microservicepubtags_tag_id ON MicroservicePubTags (tag_id); +CREATE INDEX idx_microservicesubtags_tag_id ON MicroservicesubTags (tag_id); + +CREATE TABLE IF NOT EXISTS MicroserviceCapAdd ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + cap_add TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capAdd_microserviceUuid ON MicroserviceCapAdd (microservice_uuid); + +CREATE TABLE IF NOT EXISTS MicroserviceCapDrop ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + cap_drop TEXT, + microservice_uuid VARCHAR(36), + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_capDrop_microserviceUuid ON MicroserviceCapDrop (microservice_uuid); + +ALTER TABLE Microservices ADD COLUMN annotations TEXT; + +CREATE TABLE IF NOT EXISTS FogPublicKeys ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + public_key TEXT, + iofog_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_public_keys_iofogUuid ON FogPublicKeys (iofog_uuid); + +CREATE TABLE IF NOT EXISTS FogUsedTokens ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + jti VARCHAR(255) NOT NULL, + iofog_uuid VARCHAR(36), + expiry_time BIGINT NOT NULL, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (iofog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_used_tokens_iofogUuid ON FogUsedTokens (iofog_uuid); + +DROP TABLE IF EXISTS FogAccessTokens; + +ALTER TABLE MicroserviceStatuses ADD COLUMN ip_address TEXT; + +CREATE TABLE IF NOT EXISTS Secrets ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL CHECK (type IN ('Opaque', 'tls')), + data TEXT NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_secrets_name ON Secrets (name); + +CREATE TABLE IF NOT EXISTS Certificates ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT UNIQUE NOT NULL, + subject TEXT NOT NULL, + is_ca BOOLEAN DEFAULT false, + signed_by_id INTEGER, + hosts TEXT, + valid_from DATETIME NOT NULL, + valid_to DATETIME NOT NULL, + serial_number TEXT NOT NULL, + secret_id INTEGER, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (signed_by_id) REFERENCES Certificates (id) ON DELETE SET NULL, + FOREIGN KEY (secret_id) REFERENCES Secrets (id) ON DELETE CASCADE +); + +CREATE INDEX idx_certificates_name ON Certificates (name); +CREATE INDEX idx_certificates_valid_to ON Certificates (valid_to); +CREATE INDEX idx_certificates_is_ca ON Certificates (is_ca); +CREATE INDEX idx_certificates_signed_by_id ON Certificates (signed_by_id); +CREATE INDEX idx_certificates_secret_id ON Certificates (secret_id); + +CREATE TABLE IF NOT EXISTS Services ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT UNIQUE NOT NULL, + type TEXT NOT NULL, + resource TEXT NOT NULL, + target_port INTEGER NOT NULL, + service_port INTEGER, + k8s_type TEXT, + bridge_port INTEGER, + default_bridge TEXT, + service_endpoint TEXT, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_services_id ON Services (id); +CREATE INDEX idx_services_name ON Services (name); + +CREATE TABLE IF NOT EXISTS ServiceTags ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + service_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (service_id) REFERENCES Services (id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags (id) ON DELETE CASCADE +); + +CREATE INDEX idx_service_tags_service_id ON ServiceTags (service_id); +CREATE INDEX idx_service_tags_tag_id ON ServiceTags (tag_id); + +ALTER TABLE Fogs ADD COLUMN container_engine VARCHAR(36); +ALTER TABLE Fogs ADD COLUMN deployment_type VARCHAR(36); + +DROP TABLE IF EXISTS MicroservicePublicPorts; + +ALTER TABLE MicroserviceEnvs ADD COLUMN value_from_secret TEXT; +ALTER TABLE MicroserviceEnvs ADD COLUMN value_from_config_map TEXT; + +CREATE TABLE IF NOT EXISTS ConfigMaps ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL, + immutable BOOLEAN DEFAULT false, + data TEXT NOT NULL, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_config_maps_name ON ConfigMaps (name); + +CREATE TABLE IF NOT EXISTS VolumeMounts ( + uuid VARCHAR(36) PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + config_map_name VARCHAR(255), + secret_name VARCHAR(255), + version INTEGER DEFAULT 1, + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (config_map_name) REFERENCES ConfigMaps (name) ON DELETE CASCADE, + FOREIGN KEY (secret_name) REFERENCES Secrets (name) ON DELETE CASCADE +); + +CREATE INDEX idx_volume_mounts_uuid ON VolumeMounts (uuid); +CREATE INDEX idx_volume_mounts_config_map_name ON VolumeMounts (config_map_name); +CREATE INDEX idx_volume_mounts_secret_name ON VolumeMounts (secret_name); + +CREATE TABLE IF NOT EXISTS FogVolumeMounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + fog_uuid VARCHAR(36), + volume_mount_uuid VARCHAR(36), + FOREIGN KEY (fog_uuid) REFERENCES Fogs (uuid) ON DELETE CASCADE, + FOREIGN KEY (volume_mount_uuid) REFERENCES VolumeMounts (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_fog_volume_mounts_fog_uuid ON FogVolumeMounts (fog_uuid); +CREATE INDEX idx_fog_volume_mounts_volume_mount_uuid ON FogVolumeMounts (volume_mount_uuid); + +ALTER TABLE Fogs ADD COLUMN active_volume_mounts BIGINT DEFAULT 0; +ALTER TABLE Fogs ADD COLUMN volume_mount_last_update BIGINT DEFAULT 0; + +ALTER TABLE ChangeTrackings ADD COLUMN volume_mounts BOOLEAN DEFAULT false; +ALTER TABLE ChangeTrackings ADD COLUMN exec_sessions BOOLEAN DEFAULT false; + +ALTER TABLE Services ADD COLUMN provisioning_status VARCHAR(36) DEFAULT 'pending'; +ALTER TABLE Services ADD COLUMN provisioning_error TEXT; + +ALTER TABLE Fogs ADD COLUMN warning_message TEXT DEFAULT 'HEALTHY'; +ALTER TABLE Fogs ADD COLUMN gps_device VARCHAR(36); +ALTER TABLE Fogs ADD COLUMN gps_scan_frequency INT DEFAULT 60; +ALTER TABLE Fogs ADD COLUMN edge_guard_frequency INT DEFAULT 0; + +ALTER TABLE Microservices ADD COLUMN pid_mode VARCHAR(36); +ALTER TABLE Microservices ADD COLUMN ipc_mode VARCHAR(36); +ALTER TABLE Microservices ADD COLUMN exec_enabled BOOLEAN DEFAULT false; + +ALTER TABLE MicroserviceStatuses ADD COLUMN exec_session_ids TEXT; + +ALTER TABLE Microservices ADD COLUMN schedule INT DEFAULT 50; + +CREATE TABLE IF NOT EXISTS MicroserviceExecStatuses ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + status VARCHAR(255) DEFAULT 'INACTIVE', + exec_session_id VARCHAR(255), + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_exec_status_microservice_uuid ON MicroserviceExecStatuses (microservice_uuid); + +ALTER TABLE Fogs ADD COLUMN gps_status VARCHAR(32); + +ALTER TABLE Microservices ADD COLUMN cpu_set_cpus TEXT; +ALTER TABLE Microservices ADD COLUMN memory_limit FLOAT; + +CREATE TABLE IF NOT EXISTS MicroserviceHealthChecks ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + test TEXT, + interval FLOAT, + timeout FLOAT, + start_period FLOAT, + start_interval FLOAT, + retries INT, + microservice_uuid VARCHAR(36), + created_at DATETIME, + updated_at DATETIME, + FOREIGN KEY (microservice_uuid) REFERENCES Microservices (uuid) ON DELETE CASCADE +); + +CREATE INDEX idx_microservice_health_check_microservice_uuid ON MicroserviceHealthChecks (microservice_uuid); + +ALTER TABLE MicroserviceStatuses ADD COLUMN health_status TEXT; + +ALTER TABLE Microservices ADD COLUMN is_activated BOOLEAN DEFAULT true; + +ALTER TABLE Microservices ADD COLUMN host_network_mode BOOLEAN DEFAULT false; +ALTER TABLE Microservices ADD COLUMN is_privileged BOOLEAN DEFAULT false; + +CREATE TABLE IF NOT EXISTS Events ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + timestamp BIGINT NOT NULL, + event_type VARCHAR(20) NOT NULL, + endpoint_type VARCHAR(10) NOT NULL, + actor_id VARCHAR(255), + method VARCHAR(10), + resource_type VARCHAR(50), + resource_id VARCHAR(255), + endpoint_path TEXT NOT NULL, + ip_address VARCHAR(45), + status VARCHAR(20) NOT NULL, + status_code INTEGER, + status_message TEXT, + request_id VARCHAR(255), + created_at DATETIME, + updated_at DATETIME +); + + +CREATE INDEX IF NOT EXISTS idx_events_timestamp ON Events (timestamp); +CREATE INDEX IF NOT EXISTS idx_events_endpoint_type ON Events (endpoint_type); +CREATE INDEX IF NOT EXISTS idx_events_actor_id ON Events (actor_id); +CREATE INDEX IF NOT EXISTS idx_events_resource_type ON Events (resource_type); +CREATE INDEX IF NOT EXISTS idx_events_status ON Events (status); +CREATE INDEX IF NOT EXISTS idx_events_method ON Events (method); +CREATE INDEX IF NOT EXISTS idx_events_event_type ON Events (event_type); +CREATE INDEX IF NOT EXISTS idx_events_created_at ON Events (created_at); + diff --git a/src/data/models/accesstoken.js b/src/data/models/accesstoken.js deleted file mode 100644 index acfb81fd4..000000000 --- a/src/data/models/accesstoken.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = (sequelize, DataTypes) => { - const AccessToken = sequelize.define('AccessToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - token: { - type: DataTypes.STRING, - field: 'token' - }, - expirationTime: { - type: DataTypes.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - } - }, { - tableName: 'AccessTokens', - timestamps: false, - underscored: true - }) - AccessToken.associate = function (models) { - AccessToken.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user' - }) - } - return AccessToken -} diff --git a/src/data/models/application.js b/src/data/models/application.js index 1de86d5b9..e1fc5504b 100644 --- a/src/data/models/application.js +++ b/src/data/models/application.js @@ -36,15 +36,6 @@ module.exports = (sequelize, DataTypes) => { underscored: true }) Application.associate = function (models) { - Application.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - Application.hasMany(models.Microservice, { foreignKey: { name: 'applicationId', diff --git a/src/data/models/applicationTemplate.js b/src/data/models/applicationTemplate.js index db9e1172c..6cb8b8fe0 100644 --- a/src/data/models/applicationTemplate.js +++ b/src/data/models/applicationTemplate.js @@ -35,14 +35,6 @@ module.exports = (sequelize, DataTypes) => { underscored: true }) ApplicationTemplate.associate = function (models) { - ApplicationTemplate.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) ApplicationTemplate.hasMany(models.ApplicationTemplateVariable, { foreignKey: { name: 'applicationTemplateId', diff --git a/src/data/models/catalogitem.js b/src/data/models/catalogitem.js index 661b78b74..d4fd4c563 100644 --- a/src/data/models/catalogitem.js +++ b/src/data/models/catalogitem.js @@ -76,15 +76,6 @@ module.exports = (sequelize, DataTypes) => { defaultValue: 1 }) - CatalogItem.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - CatalogItem.hasMany(models.CatalogItemImage, { foreignKey: 'catalog_item_id', as: 'images' diff --git a/src/data/models/certificate.js b/src/data/models/certificate.js new file mode 100644 index 000000000..c343332ff --- /dev/null +++ b/src/data/models/certificate.js @@ -0,0 +1,130 @@ +'use strict' + +module.exports = (sequelize, DataTypes) => { + const Certificate = sequelize.define('Certificate', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + field: 'name', + unique: true + }, + subject: { + type: DataTypes.TEXT, + allowNull: false, + field: 'subject' + }, + isCA: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + field: 'is_ca' + }, + signedById: { + type: DataTypes.INTEGER, + allowNull: true, + field: 'signed_by_id', + references: { + model: 'Certificates', + key: 'id' + } + }, + hosts: { + type: DataTypes.TEXT, + allowNull: true, + field: 'hosts' + }, + validFrom: { + type: DataTypes.DATE, + allowNull: false, + field: 'valid_from' + }, + validTo: { + type: DataTypes.DATE, + allowNull: false, + field: 'valid_to' + }, + serialNumber: { + type: DataTypes.TEXT, + allowNull: false, + field: 'serial_number' + }, + secretId: { + type: DataTypes.INTEGER, + allowNull: true, + field: 'secret_id', + references: { + model: 'Secrets', + key: 'id' + } + } + }, { + tableName: 'Certificates', + timestamps: true, + underscored: true, + indexes: [ + { + unique: true, + fields: ['name'] + }, + { + fields: ['valid_to'] + }, + { + fields: ['is_ca'] + }, + { + fields: ['signed_by_id'] + }, + { + fields: ['secret_id'] + } + ] + }) + + Certificate.associate = (models) => { + Certificate.belongsTo(Certificate, { + as: 'signingCA', + foreignKey: 'signed_by_id' + }) + Certificate.hasMany(Certificate, { + as: 'signedCertificates', + foreignKey: 'signed_by_id' + }) + + Certificate.belongsTo(models.Secret, { + foreignKey: 'secret_id', + as: 'secret' + }) + } + + // Add a getter for days remaining until expiration + Certificate.prototype.getDaysUntilExpiration = function () { + const today = new Date() + const expiryDate = new Date(this.validTo) + const diffTime = expiryDate - today + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + return diffDays > 0 ? diffDays : 0 + } + + // Add a method to check if certificate is expired + Certificate.prototype.isExpired = function () { + const today = new Date() + const expiryDate = new Date(this.validTo) + return today > expiryDate + } + + // Add a method to check if certificate is expiring soon + Certificate.prototype.isExpiringSoon = function (days = 30) { + const daysRemaining = this.getDaysUntilExpiration() + return daysRemaining > 0 && daysRemaining <= days + } + + return Certificate +} diff --git a/src/data/models/changetracking.js b/src/data/models/changetracking.js index 6aca2212b..a3932eb99 100644 --- a/src/data/models/changetracking.js +++ b/src/data/models/changetracking.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -91,6 +91,16 @@ module.exports = (sequelize, DataTypes) => { field: 'linked_edge_resources', defaultValue: false }, + volumeMounts: { + type: DataTypes.BOOLEAN, + field: 'volume_mounts', + defaultValue: false + }, + execSessions: { + type: DataTypes.BOOLEAN, + field: 'exec_sessions', + defaultValue: false + }, lastUpdated: { type: DataTypes.STRING, field: 'last_updated', diff --git a/src/data/models/config.js b/src/data/models/config.js index 8935d9227..3419a2acb 100644 --- a/src/data/models/config.js +++ b/src/data/models/config.js @@ -30,8 +30,5 @@ module.exports = (sequelize, DataTypes) => { } ] }) - Config.associate = function (models) { - - } return Config } diff --git a/src/data/models/configMap.js b/src/data/models/configMap.js new file mode 100644 index 000000000..6f0c65415 --- /dev/null +++ b/src/data/models/configMap.js @@ -0,0 +1,77 @@ +'use strict' + +const SecretHelper = require('../../helpers/secret-helper') + +module.exports = (sequelize, DataTypes) => { + const ConfigMap = sequelize.define('ConfigMap', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + field: 'name', + unique: true + }, + immutable: { + type: DataTypes.BOOLEAN, + allowNull: false, + field: 'immutable', + defaultValue: false + }, + data: { + type: DataTypes.TEXT, + allowNull: false, + field: 'data', + defaultValue: '{}', + get () { + const rawValue = this.getDataValue('data') + return rawValue ? JSON.parse(rawValue) : {} + }, + set (value) { + this.setDataValue('data', JSON.stringify(value)) + } + } + }, { + tableName: 'ConfigMaps', + timestamps: true, + underscored: true, + indexes: [ + { + unique: true, + fields: ['name'] + } + ], + hooks: { + beforeSave: async (configMap) => { + if (configMap.changed('data')) { + const encryptedData = await SecretHelper.encryptSecret( + configMap.data, + configMap.name + ) + configMap.data = encryptedData + } + }, + afterFind: async (configMap) => { + if (configMap && configMap.data) { + try { + const decryptedData = await SecretHelper.decryptSecret( + configMap.data, + configMap.name + ) + configMap.data = decryptedData + } catch (error) { + console.error('Error decrypting ConfigMap data:', error) + configMap.data = {} + } + } + } + } + }) + + return ConfigMap +} diff --git a/src/data/models/edgeResource.js b/src/data/models/edgeResource.js index d0e96afdb..63e1bcb02 100644 --- a/src/data/models/edgeResource.js +++ b/src/data/models/edgeResource.js @@ -27,16 +27,6 @@ module.exports = (sequelize, DataTypes) => { EdgeResource.associate = function (models) { EdgeResource.belongsToMany(models.Fog, { through: 'AgentEdgeResources', as: 'agents' }) EdgeResource.belongsToMany(models.Tags, { as: 'orchestrationTags', through: 'EdgeResourceOrchestrationTags' }) - - EdgeResource.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - defaultValue: 0, - onDelete: 'cascade' - }) } return EdgeResource } diff --git a/src/data/models/emailactivationcode.js b/src/data/models/emailactivationcode.js deleted file mode 100644 index 917180a7a..000000000 --- a/src/data/models/emailactivationcode.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = (sequelize, DataTypes) => { - const EmailActivationCode = sequelize.define('EmailActivationCode', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - activationCode: { - type: DataTypes.TEXT, - field: 'activation_code' - }, - expirationTime: { - type: DataTypes.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - } - }, { - tableName: 'EmailActivationCodes', - timestamps: false, - underscored: true - }) - EmailActivationCode.associate = function (models) { - EmailActivationCode.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - } - return EmailActivationCode -} diff --git a/src/data/models/event.js b/src/data/models/event.js new file mode 100644 index 000000000..27a095e7b --- /dev/null +++ b/src/data/models/event.js @@ -0,0 +1,108 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const Event = sequelize.define('Event', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + timestamp: { + type: DataTypes.BIGINT, + allowNull: false, + field: 'timestamp' + }, + eventType: { + type: DataTypes.STRING(20), + allowNull: false, + field: 'event_type' + }, + endpointType: { + type: DataTypes.STRING(10), + allowNull: false, + field: 'endpoint_type' + }, + actorId: { + type: DataTypes.STRING(255), + allowNull: true, + field: 'actor_id' + }, + method: { + type: DataTypes.STRING(10), + allowNull: true, + field: 'method' + }, + resourceType: { + type: DataTypes.STRING(50), + allowNull: true, + field: 'resource_type' + }, + resourceId: { + type: DataTypes.STRING(255), + allowNull: true, + field: 'resource_id' + }, + endpointPath: { + type: DataTypes.TEXT, + allowNull: false, + field: 'endpoint_path' + }, + ipAddress: { + type: DataTypes.STRING(45), + allowNull: true, + field: 'ip_address' + }, + status: { + type: DataTypes.STRING(20), + allowNull: false, + field: 'status' + }, + statusCode: { + type: DataTypes.INTEGER, + allowNull: true, + field: 'status_code' + }, + statusMessage: { + type: DataTypes.TEXT, + allowNull: true, + field: 'status_message' + }, + requestId: { + type: DataTypes.STRING(255), + allowNull: true, + field: 'request_id' + } + }, { + tableName: 'Events', + timestamps: true, + underscored: true, + indexes: [ + { + fields: ['timestamp'] + }, + { + fields: ['endpoint_type'] + }, + { + fields: ['actor_id'] + }, + { + fields: ['resource_type'] + }, + { + fields: ['status'] + }, + { + fields: ['method'] + }, + { + fields: ['event_type'] + }, + { + fields: ['created_at'] + } + ] + }) + return Event +} diff --git a/src/data/models/fog.js b/src/data/models/fog.js index 22e80ab1d..7bd62e3f1 100644 --- a/src/data/models/fog.js +++ b/src/data/models/fog.js @@ -5,7 +5,7 @@ const { convertToInt } = require('../../helpers/app-helper') module.exports = (sequelize, DataTypes) => { const Fog = sequelize.define('Fog', { uuid: { - type: DataTypes.STRING(32), + type: DataTypes.STRING(36), primaryKey: true, allowNull: false, field: 'uuid' @@ -17,12 +17,22 @@ module.exports = (sequelize, DataTypes) => { }, location: { type: DataTypes.TEXT, - field: 'location' + field: 'location', + defaultValue: '' }, gpsMode: { type: DataTypes.TEXT, field: 'gps_mode' }, + gpsDevice: { + type: DataTypes.TEXT, + field: 'gps_device' + }, + gpsScanFrequency: { + type: DataTypes.INTEGER, + field: 'gps_scan_frequency', + defaultValue: 60 + }, latitude: { type: DataTypes.FLOAT, field: 'latitude' @@ -33,7 +43,8 @@ module.exports = (sequelize, DataTypes) => { }, description: { type: DataTypes.TEXT, - field: 'description' + field: 'description', + defaultValue: '' }, lastActive: { type: DataTypes.BIGINT, @@ -44,7 +55,7 @@ module.exports = (sequelize, DataTypes) => { }, daemonStatus: { type: DataTypes.TEXT, - defaultValue: 'UNKNOWN', + defaultValue: 'NOT_PROVISIONED', field: 'daemon_status' }, daemonOperatingDuration: { @@ -94,18 +105,18 @@ module.exports = (sequelize, DataTypes) => { get () { return convertToInt(this.getDataValue('systemAvailableDisk')) }, - field: 'system-available-disk' + field: 'system_available_disk' }, systemAvailableMemory: { type: DataTypes.BIGINT, get () { return convertToInt(this.getDataValue('systemAvailableMemory')) }, - field: 'system-available-memory' + field: 'system_available_memory' }, systemTotalCpu: { type: DataTypes.FLOAT, - field: 'system-total-cpu' + field: 'system_total_cpu' }, securityStatus: { type: DataTypes.TEXT, @@ -185,7 +196,7 @@ module.exports = (sequelize, DataTypes) => { }, networkInterface: { type: DataTypes.TEXT, - defaultValue: 'eth0', + defaultValue: 'dynamic', field: 'network_interface' }, dockerUrl: { @@ -193,6 +204,18 @@ module.exports = (sequelize, DataTypes) => { defaultValue: 'unix:///var/run/docker.sock', field: 'docker_url' }, + containerEngine: { + type: DataTypes.ENUM('docker', 'podman'), + allowNull: false, + field: 'container_engine', + defaultValue: 'docker' + }, + deploymentType: { + type: DataTypes.ENUM('native', 'container'), + allowNull: false, + field: 'deployment_type', + defaultValue: 'native' + }, diskLimit: { type: DataTypes.FLOAT, defaultValue: 50, @@ -277,12 +300,17 @@ module.exports = (sequelize, DataTypes) => { }, watchdogEnabled: { type: DataTypes.BOOLEAN, - defaultValue: true, + defaultValue: false, field: 'isolated_docker_container' }, + edgeGuardFrequency: { + type: DataTypes.INTEGER, + defaultValue: 0, + field: 'edge_guard_frequency' + }, dockerPruningFrequency: { type: DataTypes.INTEGER, - defaultValue: 1, + defaultValue: 0, field: 'docker_pruning_freq' }, availableDiskThreshold: { @@ -308,6 +336,30 @@ module.exports = (sequelize, DataTypes) => { timeZone: { type: DataTypes.TEXT, field: 'time_zone' + }, + activeVolumeMounts: { + type: DataTypes.BIGINT, + defaultValue: 0, + get () { + return convertToInt(this.getDataValue('activeVolumeMounts'), 0) + }, + field: 'active_volume_mounts' + }, + volumeMountLastUpdate: { + type: DataTypes.BIGINT, + get () { + return convertToInt(this.getDataValue('volumeMountLastUpdate'), 0) + }, + field: 'volume_mount_last_update' + }, + warningMessage: { + type: DataTypes.TEXT, + field: 'warning_message', + defaultValue: 'HEALTHY' + }, + gpsStatus: { + type: DataTypes.TEXT, + field: 'gps_status' } }, { tableName: 'Fogs', @@ -324,19 +376,9 @@ module.exports = (sequelize, DataTypes) => { defaultValue: 0 }) - Fog.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - defaultValue: 0, - onDelete: 'cascade' - }) - - Fog.hasOne(models.FogAccessToken, { + Fog.hasOne(models.FogPublicKey, { foreignKey: 'iofog_uuid', - as: 'accessToken' + as: 'publicKey' }) Fog.hasMany(models.Microservice, { @@ -344,6 +386,11 @@ module.exports = (sequelize, DataTypes) => { as: 'microservice' }) + Fog.hasMany(models.FogUsedToken, { + foreignKey: 'iofog_uuid', + as: 'jti' + }) + Fog.hasOne(models.Router, { foreignKey: 'iofog_uuid', as: 'router' @@ -351,6 +398,7 @@ module.exports = (sequelize, DataTypes) => { Fog.belongsToMany(models.Tags, { through: 'IofogTags', as: 'tags' }) Fog.belongsToMany(models.EdgeResource, { through: 'AgentEdgeResources', as: 'edgeResources' }) + Fog.belongsToMany(models.VolumeMount, { through: 'FogVolumeMounts', as: 'volumeMounts' }) } return Fog diff --git a/src/data/models/fogUsedToken.js b/src/data/models/fogUsedToken.js new file mode 100644 index 000000000..6e0b9e685 --- /dev/null +++ b/src/data/models/fogUsedToken.js @@ -0,0 +1,46 @@ +'use strict' + +const { convertToInt } = require('../../helpers/app-helper') + +module.exports = (sequelize, DataTypes) => { + const FogUsedToken = sequelize.define('FogUsedToken', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + jti: { + type: DataTypes.STRING(255), + primaryKey: true, + allowNull: false + }, + expiryTime: { + type: DataTypes.BIGINT, + get () { + return convertToInt(this.getDataValue('expiryTime'), 0) + }, + set (value) { + // Ensure the value is stored as a BIGINT (Unix timestamp) + this.setDataValue('expiryTime', parseInt(value, 10)) + }, + field: 'expiry_time' + } + }, { + tableName: 'FogUsedTokens', + timestamps: true, + underscored: true + }) + FogUsedToken.associate = function (models) { + FogUsedToken.belongsTo(models.Fog, { + foreignKey: { + name: 'iofogUuid', + field: 'iofog_uuid' + }, + as: 'iofog', + onDelete: 'cascade' + }) + } + return FogUsedToken +} diff --git a/src/data/models/fogVolumeMounts.js b/src/data/models/fogVolumeMounts.js new file mode 100644 index 000000000..25566a44c --- /dev/null +++ b/src/data/models/fogVolumeMounts.js @@ -0,0 +1,9 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const FogVolumeMounts = sequelize.define('FogVolumeMounts', {}, { + tableName: 'FogVolumeMounts', + timestamps: false, + underscored: true + }) + return FogVolumeMounts +} diff --git a/src/data/models/fogaccesstoken.js b/src/data/models/fogaccesstoken.js deleted file mode 100644 index a2a4e1b81..000000000 --- a/src/data/models/fogaccesstoken.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = (sequelize, DataTypes) => { - const FogAccessToken = sequelize.define('FogAccessToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: DataTypes.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: DataTypes.TEXT, - field: 'token' - } - }, { - tableName: 'FogAccessTokens', - timestamps: false, - underscored: true - }) - FogAccessToken.associate = function (models) { - FogAccessToken.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - - FogAccessToken.belongsTo(models.Fog, { - foreignKey: { - name: 'iofogUuid', - field: 'iofog_uuid' - }, - as: 'iofog', - onDelete: 'cascade' - }) - } - return FogAccessToken -} diff --git a/src/data/models/fogpublickey.js b/src/data/models/fogpublickey.js new file mode 100644 index 000000000..37413b000 --- /dev/null +++ b/src/data/models/fogpublickey.js @@ -0,0 +1,42 @@ +'use strict' + +module.exports = (sequelize, DataTypes) => { + const FogPublicKey = sequelize.define('FogPublicKey', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + publicKey: { + type: DataTypes.TEXT, + field: 'public_key' + }, + createdAt: { + type: DataTypes.DATE, + field: 'created_at' + }, + updatedAt: { + type: DataTypes.DATE, + field: 'updated_at' + } + }, { + tableName: 'FogPublicKeys', + timestamps: true, + underscored: true + }) + + FogPublicKey.associate = function (models) { + FogPublicKey.belongsTo(models.Fog, { + foreignKey: { + name: 'iofogUuid', + field: 'iofog_uuid' + }, + as: 'iofog', + onDelete: 'cascade' + }) + } + + return FogPublicKey +} diff --git a/src/data/models/index.js b/src/data/models/index.js index d502edc34..4eb51ae1b 100644 --- a/src/data/models/index.js +++ b/src/data/models/index.js @@ -8,28 +8,31 @@ const constants = require('../constants') const basename = path.basename(__filename) const db = {} const config = require('../../config') +const logger = require('../../logger') const databaseProvider = require('../providers/database-factory') -const sequelize = databaseProvider.sequelize -fs - .readdirSync(__dirname) - .filter((file) => { - return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js') - }) - .forEach((file) => { - const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes) - db[model.name] = model - }) +// Initialize models after database is ready +const initializeModels = (sequelize) => { + fs + .readdirSync(__dirname) + .filter((file) => { + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js') + }) + .forEach((file) => { + const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes) + db[model.name] = model + }) -Object.keys(db).forEach((modelName) => { - if (db[modelName].associate) { - db[modelName].associate(db) - } -}) + Object.keys(db).forEach((modelName) => { + if (db[modelName].associate) { + db[modelName].associate(db) + } + }) -db.sequelize = sequelize -db.Sequelize = Sequelize + db.sequelize = sequelize + db.Sequelize = Sequelize +} const configureImage = async (db, name, fogTypes, images) => { const catalogItem = await db.CatalogItem.findOne({ where: { name, isPublic: false } }) @@ -45,16 +48,30 @@ const configureImage = async (db, name, fogTypes, images) => { db.initDB = async (isStart) => { await databaseProvider.initDB(isStart) - const migrationUmzug = databaseProvider.createUmzug(path.resolve(__dirname, '../migrations')) - await migrationUmzug.up() - await databaseProvider.createUmzug(path.resolve(__dirname, '../seeders')).up() + + // Initialize models after database is ready + initializeModels(databaseProvider.sequelize) if (isStart) { + if (databaseProvider instanceof require('../providers/sqlite')) { + const sqliteDbPath = databaseProvider.sequelize.options.storage + logger.info('Running SQLite database migrations and seeders...') + await databaseProvider.runMigrationSQLite(sqliteDbPath) + await databaseProvider.runSeederSQLite(sqliteDbPath) + } else if (databaseProvider instanceof require('../providers/mysql')) { + logger.info('Running MySQL database migrations and seeders...') + await databaseProvider.runMigrationMySQL(databaseProvider.sequelize) + await databaseProvider.runSeederMySQL(databaseProvider.sequelize) + } else if (databaseProvider instanceof require('../providers/postgres')) { + logger.info('Running PostgreSQL database migrations and seeders...') + await databaseProvider.runMigrationPostgres(databaseProvider.sequelize) + await databaseProvider.runSeederPostgres(databaseProvider.sequelize) + } + // Configure system images const fogTypes = await db.FogType.findAll({}) - await configureImage(db, constants.ROUTER_CATALOG_NAME, fogTypes, config.get('SystemImages:Router', {})) - await configureImage(db, constants.PROXY_CATALOG_NAME, fogTypes, config.get('SystemImages:Proxy', {})) - await configureImage(db, constants.PORT_ROUTER_CATALOG_NAME, fogTypes, config.get('SystemImages:PortRouter', {})) + await configureImage(db, constants.ROUTER_CATALOG_NAME, fogTypes, config.get('systemImages.router', {})) + await configureImage(db, constants.DEBUG_CATALOG_NAME, fogTypes, config.get('systemImages.debug', {})) } } diff --git a/src/data/models/kubeletaccesstoken.js b/src/data/models/kubeletaccesstoken.js deleted file mode 100644 index 1355fe0bd..000000000 --- a/src/data/models/kubeletaccesstoken.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = (sequelize, DataTypes) => { - const KubeletAccessToken = sequelize.define('KubeletAccessToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: DataTypes.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: DataTypes.TEXT, - field: 'token' - } - }, { - tableName: 'KubeletAccessTokens', - timestamps: false, - underscored: true - }) - KubeletAccessToken.associate = function (models) { - KubeletAccessToken.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - } - return KubeletAccessToken -} diff --git a/src/data/models/microservice.js b/src/data/models/microservice.js index cd25823db..8278d10e3 100644 --- a/src/data/models/microservice.js +++ b/src/data/models/microservice.js @@ -5,7 +5,7 @@ const { convertToInt } = require('../../helpers/app-helper') module.exports = (sequelize, DataTypes) => { const Microservice = sequelize.define('Microservice', { uuid: { - type: DataTypes.STRING(32), + type: DataTypes.STRING(36), primaryKey: true, allowNull: false, field: 'uuid' @@ -15,6 +15,11 @@ module.exports = (sequelize, DataTypes) => { field: 'config', defaultValue: '{}' }, + annotations: { + type: DataTypes.TEXT, + field: 'annotations', + defaultValue: '{}' + }, name: { type: DataTypes.TEXT, field: 'name', @@ -27,21 +32,36 @@ module.exports = (sequelize, DataTypes) => { }, field: 'config_last_updated' }, - isNetwork: { + rebuild: { type: DataTypes.BOOLEAN, - field: 'is_network', + field: 'rebuild', defaultValue: false }, - rebuild: { + hostNetworkMode: { type: DataTypes.BOOLEAN, - field: 'rebuild', + field: 'host_network_mode', defaultValue: false }, - rootHostAccess: { + isPrivileged: { type: DataTypes.BOOLEAN, - field: 'root_host_access', + field: 'is_privileged', defaultValue: false }, + runAsUser: { + type: DataTypes.TEXT, + field: 'run_as_user', + defaultValue: '' + }, + platform: { + type: DataTypes.TEXT, + field: 'platform', + defaultValue: '' + }, + runtime: { + type: DataTypes.TEXT, + field: 'runtime', + defaultValue: '' + }, logSize: { type: DataTypes.BIGINT, get () { @@ -50,11 +70,40 @@ module.exports = (sequelize, DataTypes) => { field: 'log_size', defaultValue: 0 }, + pidMode: { + type: DataTypes.TEXT, + field: 'pid_mode', + defaultValue: '' + }, + ipcMode: { + type: DataTypes.TEXT, + field: 'ipc_mode', + defaultValue: '' + }, + schedule: { + type: DataTypes.INTEGER, + field: 'schedule', + defaultValue: 50 + }, + cpuSetCpus: { + type: DataTypes.TEXT, + field: 'cpu_set_cpus', + defaultValue: '' + }, + memoryLimit: { + type: DataTypes.FLOAT, + field: 'memory_limit' + }, imageSnapshot: { type: DataTypes.TEXT, field: 'image_snapshot', defaultValue: '' }, + execEnabled: { + type: DataTypes.BOOLEAN, + field: 'exec_enabled', + defaultValue: false + }, delete: { type: DataTypes.BOOLEAN, field: 'delete', @@ -64,6 +113,11 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.BOOLEAN, field: 'delete_with_cleanup', defaultValue: false + }, + isActivated: { + type: DataTypes.BOOLEAN, + field: 'is_activated', + defaultValue: true } }, { tableName: 'Microservices', @@ -108,15 +162,6 @@ module.exports = (sequelize, DataTypes) => { onDelete: 'cascade' }) - Microservice.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - Microservice.hasMany(models.CatalogItemImage, { foreignKey: 'microservice_uuid', as: 'images' @@ -147,6 +192,16 @@ module.exports = (sequelize, DataTypes) => { as: 'microserviceStatus' }) + Microservice.hasOne(models.MicroserviceExecStatus, { + foreignKey: 'microservice_uuid', + as: 'microserviceExecStatus' + }) + + Microservice.hasOne(models.MicroserviceHealthCheck, { + foreignKey: 'microservice_uuid', + as: 'healthCheck' + }) + Microservice.hasMany(models.MicroserviceEnv, { foreignKey: 'microservice_uuid', as: 'env' @@ -157,10 +212,28 @@ module.exports = (sequelize, DataTypes) => { as: 'cmd' }) + Microservice.hasMany(models.MicroserviceCdiDev, { + foreignKey: 'microservice_uuid', + as: 'cdiDevices' + }) + + Microservice.hasMany(models.MicroserviceCapAdd, { + foreignKey: 'microservice_uuid', + as: 'capAdd' + }) + + Microservice.hasMany(models.MicroserviceCapDrop, { + foreignKey: 'microservice_uuid', + as: 'capDrop' + }) + Microservice.hasMany(models.MicroserviceExtraHost, { foreignKey: 'microservice_uuid', as: 'extraHosts' }) + + Microservice.belongsToMany(models.Tags, { as: 'pubTags', through: 'MicroservicePubTags' }) + Microservice.belongsToMany(models.Tags, { as: 'subTags', through: 'MicroserviceSubTags' }) } return Microservice diff --git a/src/data/models/microserviceCapAdd.js b/src/data/models/microserviceCapAdd.js new file mode 100644 index 000000000..6a5ee5fe6 --- /dev/null +++ b/src/data/models/microserviceCapAdd.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceCapAdd = sequelize.define('MicroserviceCapAdd', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + capAdd: { + type: DataTypes.TEXT, + field: 'cap_add' + } + }, { + tableName: 'MicroserviceCapAdd', + timestamps: false, + underscored: true + }) + MicroserviceCapAdd.associate = function (models) { + MicroserviceCapAdd.belongsTo(models.Microservice, { + foreignKey: { + name: 'microserviceUuid', + field: 'microservice_uuid' + }, + as: 'microservice', + onDelete: 'cascade' + }) + } + return MicroserviceCapAdd +} diff --git a/src/data/models/microserviceCapDrop.js b/src/data/models/microserviceCapDrop.js new file mode 100644 index 000000000..288faa659 --- /dev/null +++ b/src/data/models/microserviceCapDrop.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceCapDrop = sequelize.define('MicroserviceCapDrop', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + capDrop: { + type: DataTypes.TEXT, + field: 'cap_drop' + } + }, { + tableName: 'MicroserviceCapDrop', + timestamps: false, + underscored: true + }) + MicroserviceCapDrop.associate = function (models) { + MicroserviceCapDrop.belongsTo(models.Microservice, { + foreignKey: { + name: 'microserviceUuid', + field: 'microservice_uuid' + }, + as: 'microservice', + onDelete: 'cascade' + }) + } + return MicroserviceCapDrop +} diff --git a/src/data/models/microserviceCdiDev.js b/src/data/models/microserviceCdiDev.js new file mode 100644 index 000000000..38f85081b --- /dev/null +++ b/src/data/models/microserviceCdiDev.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceCdiDev = sequelize.define('MicroserviceCdiDev', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + cdiDevices: { + type: DataTypes.TEXT, + field: 'cdi_devices' + } + }, { + tableName: 'MicroserviceCdiDevices', + timestamps: false, + underscored: true + }) + MicroserviceCdiDev.associate = function (models) { + MicroserviceCdiDev.belongsTo(models.Microservice, { + foreignKey: { + name: 'microserviceUuid', + field: 'microservice_uuid' + }, + as: 'microservice', + onDelete: 'cascade' + }) + } + return MicroserviceCdiDev +} diff --git a/src/data/models/microserviceExecStatus.js b/src/data/models/microserviceExecStatus.js new file mode 100644 index 000000000..d9688405a --- /dev/null +++ b/src/data/models/microserviceExecStatus.js @@ -0,0 +1,36 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceExecStatus = sequelize.define('MicroserviceExecStatus', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + status: { + type: DataTypes.TEXT, + field: 'status' + }, + execSessionId: { + type: DataTypes.TEXT, + field: 'exec_session_id' + } + }, { + tableName: 'MicroserviceExecStatuses', + // add the timestamp attributes (updatedAt, createdAt) + timestamps: true, + underscored: true + }) + MicroserviceExecStatus.associate = function (models) { + MicroserviceExecStatus.belongsTo(models.Microservice, { + foreignKey: { + name: 'microserviceUuid', + field: 'microservice_uuid' + }, + as: 'microservice', + onDelete: 'cascade' + }) + } + return MicroserviceExecStatus +} diff --git a/src/data/models/microserviceExtraHost.js b/src/data/models/microserviceExtraHost.js index 676f62ac4..2f7476233 100644 --- a/src/data/models/microserviceExtraHost.js +++ b/src/data/models/microserviceExtraHost.js @@ -14,9 +14,6 @@ module.exports = (sequelize, DataTypes) => { name: { type: DataTypes.TEXT }, - publicPort: { - type: DataTypes.INTEGER - }, // Only if type is Apps template: { type: DataTypes.TEXT }, // Contains the template string diff --git a/src/data/models/microserviceHealthCheck.js b/src/data/models/microserviceHealthCheck.js new file mode 100644 index 000000000..d6f32e698 --- /dev/null +++ b/src/data/models/microserviceHealthCheck.js @@ -0,0 +1,52 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceHealthCheck = sequelize.define('MicroserviceHealthCheck', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + test: { + type: DataTypes.TEXT, + field: 'test' + }, + interval: { + type: DataTypes.FLOAT, + field: 'interval' + }, + timeout: { + type: DataTypes.FLOAT, + field: 'timeout' + }, + startPeriod: { + type: DataTypes.FLOAT, + field: 'start_period' + }, + startInterval: { + type: DataTypes.FLOAT, + field: 'start_interval' + }, + retries: { + type: DataTypes.INTEGER, + field: 'retries' + } + }, { + tableName: 'MicroserviceHealthChecks', + // add the timestamp attributes (updatedAt, createdAt) + timestamps: true, + underscored: true + }) + MicroserviceHealthCheck.associate = function (models) { + MicroserviceHealthCheck.belongsTo(models.Microservice, { + foreignKey: { + name: 'microserviceUuid', + field: 'microservice_uuid' + }, + as: 'microservice', + onDelete: 'cascade' + }) + } + return MicroserviceHealthCheck +} diff --git a/src/data/models/microserviceProxyPort.js b/src/data/models/microserviceProxyPort.js deleted file mode 100644 index 2bf9e8345..000000000 --- a/src/data/models/microserviceProxyPort.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -module.exports = (sequelize, DataTypes) => { - const MicroserviceProxyPort = sequelize.define('MicroserviceProxyPort', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - portId: { - type: DataTypes.INTEGER, - field: 'port_id' - }, - host: { - type: DataTypes.TEXT, - field: 'host' - }, - localProxyId: { - type: DataTypes.TEXT, - field: 'local_proxy_id' - }, - publicPort: { - type: DataTypes.INTEGER, - field: 'public_port' - }, - adminPort: { - type: DataTypes.INTEGER, - field: 'admin_port' - }, - protocol: { - type: DataTypes.TEXT, - field: 'protocol' - }, - proxyToken: { - type: DataTypes.TEXT, - field: 'proxy_token' - }, - portUUID: { - type: DataTypes.TEXT, - field: 'port_uuid' - }, - serverToken: { - type: DataTypes.TEXT, - field: 'server_token' - } - }, { - tableName: 'MicroserviceProxyPorts', - timestamps: true, - underscored: true - }) - MicroserviceProxyPort.associate = function (models) { - MicroserviceProxyPort.belongsTo(models.MicroservicePort, { - foreignKey: { - name: 'portId', - field: 'port_id' - }, - as: 'port', - onDelete: 'cascade' - }) - - MicroserviceProxyPort.hasOne(models.Microservice, { - foreignKey: 'uuid', - as: 'localProxy' - }) - } - - return MicroserviceProxyPort -} diff --git a/src/data/models/microservicePubTags.js b/src/data/models/microservicePubTags.js new file mode 100644 index 000000000..1cd759355 --- /dev/null +++ b/src/data/models/microservicePubTags.js @@ -0,0 +1,9 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroservicePubTags = sequelize.define('MicroservicePubTags', {}, { + tableName: 'MicroservicePubTags', + timestamps: false, + underscored: true + }) + return MicroservicePubTags +} diff --git a/src/data/models/microservicePublicPort.js b/src/data/models/microservicePublicPort.js deleted file mode 100644 index d1f9bc75f..000000000 --- a/src/data/models/microservicePublicPort.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict' - -module.exports = (sequelize, DataTypes) => { - const MicroservicePublicPort = sequelize.define('MicroservicePublicPort', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - portId: { - type: DataTypes.INTEGER, - field: 'port_id' - }, - hostId: { - type: DataTypes.TEXT, - field: 'host_id' - }, - localProxyId: { - type: DataTypes.TEXT, - field: 'local_proxy_id' - }, - remoteProxyId: { - type: DataTypes.TEXT, - field: 'remote_proxy_id' - }, - publicPort: { - type: DataTypes.INTEGER, - field: 'public_port' - }, - queueName: { - type: DataTypes.TEXT, - field: 'queue_name' - }, - schemes: { - type: DataTypes.TEXT, // JSON stringified array of strings - field: 'schemes', - defaultValue: JSON.stringify(['https']) - }, - isTcp: { - type: DataTypes.BOOLEAN, - field: 'is_tcp', - defaultValue: false - }, - protocol: { - type: DataTypes.VIRTUAL, - get () { - return this.getDataValue('isTcp') ? 'tcp' : 'http' - } - } - }, { - tableName: 'MicroservicePublicPorts', - timestamps: true, - underscored: true - }) - MicroservicePublicPort.associate = function (models) { - MicroservicePublicPort.belongsTo(models.MicroservicePort, { - foreignKey: { - name: 'portId', - field: 'port_id' - }, - as: 'port', - onDelete: 'cascade' - }) - - MicroservicePublicPort.belongsTo(models.Fog, { - foreignKey: { - name: 'hostId', - field: 'host_id' - }, - as: 'host', - onDelete: 'cascade' - }) - - MicroservicePublicPort.hasOne(models.Microservice, { - foreignKey: 'uuid', - as: 'localProxy' - }) - - MicroservicePublicPort.hasOne(models.Microservice, { - foreignKey: 'uuid', - as: 'remoteProxy' - }) - } - - return MicroservicePublicPort -} diff --git a/src/data/models/microserviceSubTags.js b/src/data/models/microserviceSubTags.js new file mode 100644 index 000000000..7de19bd48 --- /dev/null +++ b/src/data/models/microserviceSubTags.js @@ -0,0 +1,9 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const MicroserviceSubTags = sequelize.define('MicroserviceSubTags', {}, { + tableName: 'MicroserviceSubTags', + timestamps: false, + underscored: true + }) + return MicroserviceSubTags +} diff --git a/src/data/models/microserviceenv.js b/src/data/models/microserviceenv.js index 837275f5f..977aa5548 100644 --- a/src/data/models/microserviceenv.js +++ b/src/data/models/microserviceenv.js @@ -15,6 +15,14 @@ module.exports = (sequelize, DataTypes) => { value: { type: DataTypes.TEXT, field: 'value' + }, + valueFromSecret: { + type: DataTypes.TEXT, + field: 'value_from_secret' + }, + valueFromConfigMap: { + type: DataTypes.TEXT, + field: 'value_from_config_map' } }, { tableName: 'MicroserviceEnvs', diff --git a/src/data/models/microserviceport.js b/src/data/models/microserviceport.js index b9b2a7bdf..6fbb67f88 100644 --- a/src/data/models/microserviceport.js +++ b/src/data/models/microserviceport.js @@ -19,14 +19,6 @@ module.exports = (sequelize, DataTypes) => { isUdp: { type: DataTypes.BOOLEAN, field: 'is_udp' - }, - isPublic: { - type: DataTypes.BOOLEAN, - field: 'is_public' - }, - isProxy: { - type: DataTypes.BOOLEAN, - field: 'is_proxy' } }, { tableName: 'MicroservicePorts', @@ -42,25 +34,6 @@ module.exports = (sequelize, DataTypes) => { as: 'microservice', onDelete: 'cascade' }) - - MicroservicePort.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - - MicroservicePort.hasOne(models.MicroservicePublicPort, { - foreignKey: 'port_id', - as: 'publicPort' - }) - - MicroservicePort.hasOne(models.MicroserviceProxyPort, { - foreignKey: 'port_id', - as: 'proxyPort' - }) } return MicroservicePort } diff --git a/src/data/models/microservicepublicmode.js b/src/data/models/microservicepublicmode.js deleted file mode 100644 index 0fe2daee1..000000000 --- a/src/data/models/microservicepublicmode.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict' -module.exports = (sequelize, DataTypes) => { - const MicroservicePublicMode = sequelize.define('MicroservicePublicMode', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - } - }, { - tableName: 'MicroservicePublicModes', - timestamps: false, - underscored: true - }) - MicroservicePublicMode.associate = function (models) { - MicroservicePublicMode.belongsTo(models.Microservice, { - foreignKey: { - name: 'microserviceUuid', - field: 'microservice_uuid' - }, - as: 'microservice', - onDelete: 'cascade' - }) - - MicroservicePublicMode.belongsTo(models.Microservice, { - foreignKey: { - name: 'networkMicroserviceUuid', - field: 'network_microservice_uuid' - }, - as: 'networkMicroservice', - onDelete: 'set null' - }) - - MicroservicePublicMode.belongsTo(models.Fog, { - foreignKey: { - name: 'iofogUuid', - field: 'iofog_uuid' - }, - as: 'iofog', - onDelete: 'set null' - }) - - MicroservicePublicMode.belongsTo(models.MicroservicePort, { - foreignKey: { - name: 'microservicePortId', - field: 'microservice_port_id' - }, - as: 'microservicePort', - onDelete: 'set null' - }) - } - return MicroservicePublicMode -} diff --git a/src/data/models/microservicestatus.js b/src/data/models/microservicestatus.js index 7fe3c46ca..b9f7a3cbe 100644 --- a/src/data/models/microservicestatus.js +++ b/src/data/models/microservicestatus.js @@ -60,6 +60,32 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.TEXT, defaultValue: '', field: 'error_message' + }, + ipAddress: { + type: DataTypes.TEXT, + defaultValue: '', + field: 'ip_address' + }, + healthStatus: { + type: DataTypes.TEXT, + defaultValue: '', + field: 'health_status' + }, + execSessionIds: { + type: DataTypes.TEXT, + defaultValue: '[]', + field: 'exec_session_ids', + get () { + const value = this.getDataValue('execSessionIds') + try { + return JSON.parse(value) + } catch (e) { + return [] + } + }, + set (value) { + this.setDataValue('execSessionIds', JSON.stringify(value)) + } } }, { tableName: 'MicroserviceStatuses', diff --git a/src/data/models/registry.js b/src/data/models/registry.js index 3840ecd54..fe169e515 100644 --- a/src/data/models/registry.js +++ b/src/data/models/registry.js @@ -16,18 +16,6 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.BOOLEAN, field: 'is_public' }, - isSecure: { - type: DataTypes.BOOLEAN, - field: 'secure' - }, - certificate: { - type: DataTypes.TEXT, - field: 'certificate' - }, - requiresCert: { - type: DataTypes.BOOLEAN, - field: 'requires_cert' - }, username: { type: DataTypes.TEXT, field: 'user_name' @@ -45,14 +33,5 @@ module.exports = (sequelize, DataTypes) => { timestamps: false, underscored: true }) - Registry.associate = function (models) { - Registry.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user' - }) - } return Registry } diff --git a/src/data/models/router.js b/src/data/models/router.js index a033f1f57..e4cd99801 100644 --- a/src/data/models/router.js +++ b/src/data/models/router.js @@ -17,7 +17,7 @@ module.exports = (sequelize, DataTypes) => { messagingPort: { type: DataTypes.INTEGER, field: 'messaging_port', - defaultValue: 5672 + defaultValue: 5671 }, edgeRouterPort: { type: DataTypes.INTEGER, diff --git a/src/data/models/routing.js b/src/data/models/routing.js index be63e3f09..3f748ae77 100644 --- a/src/data/models/routing.js +++ b/src/data/models/routing.js @@ -12,11 +12,6 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.TEXT, allowNull: false, field: 'name' - }, - isNetworkConnection: { - type: DataTypes.BOOLEAN, - defaultValue: false, - field: 'is_network_connection' } }, { tableName: 'Routings', @@ -42,42 +37,6 @@ module.exports = (sequelize, DataTypes) => { onDelete: 'cascade' }) - Routing.belongsTo(models.Microservice, { - foreignKey: { - name: 'sourceNetworkMicroserviceUuid', - field: 'source_network_microservice_uuid' - }, - as: 'sourceNetworkMicroservice', - onDelete: 'set null' - }) - - Routing.belongsTo(models.Microservice, { - foreignKey: { - name: 'destNetworkMicroserviceUuid', - field: 'dest_network_microservice_uuid' - }, - as: 'destNetworkMicroservice', - onDelete: 'set null' - }) - - Routing.belongsTo(models.Fog, { - foreignKey: { - name: 'sourceIofogUuid', - field: 'source_iofog_uuid' - }, - as: 'sourceIofog', - onDelete: 'set null' - }) - - Routing.belongsTo(models.Fog, { - foreignKey: { - name: 'destIofogUuid', - field: 'dest_iofog_uuid' - }, - as: 'destIofog', - onDelete: 'set null' - }) - Routing.belongsTo(models.Application, { foreignKey: { name: 'applicationId', diff --git a/src/data/models/scheduleraccesstoken.js b/src/data/models/scheduleraccesstoken.js deleted file mode 100644 index 40ca28dc7..000000000 --- a/src/data/models/scheduleraccesstoken.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const { convertToInt } = require('../../helpers/app-helper') - -module.exports = (sequelize, DataTypes) => { - const SchedulerAccessToken = sequelize.define('SchedulerAccessToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id' - }, - expirationTime: { - type: DataTypes.BIGINT, - get () { - return convertToInt(this.getDataValue('expirationTime')) - }, - field: 'expiration_time' - }, - token: { - type: DataTypes.TEXT, - field: 'token' - } - }, { - tableName: 'SchedulerAccessTokens', - timestamps: false, - underscored: true - }) - SchedulerAccessToken.associate = function (models) { - SchedulerAccessToken.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'user', - onDelete: 'cascade' - }) - } - return SchedulerAccessToken -} diff --git a/src/data/models/secret.js b/src/data/models/secret.js new file mode 100644 index 000000000..9b1447aea --- /dev/null +++ b/src/data/models/secret.js @@ -0,0 +1,79 @@ +'use strict' + +const SecretHelper = require('../../helpers/secret-helper') + +module.exports = (sequelize, DataTypes) => { + const Secret = sequelize.define('Secret', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + field: 'name', + unique: true + }, + type: { + type: DataTypes.TEXT, + allowNull: false, + field: 'type', + validate: { + isIn: [['Opaque', 'tls']] + } + }, + data: { + type: DataTypes.TEXT, + allowNull: false, + field: 'data', + defaultValue: '{}', + get () { + const rawValue = this.getDataValue('data') + return rawValue ? JSON.parse(rawValue) : {} + }, + set (value) { + this.setDataValue('data', JSON.stringify(value)) + } + } + }, { + tableName: 'Secrets', + timestamps: true, + underscored: true, + indexes: [ + { + unique: true, + fields: ['name'] + } + ], + hooks: { + beforeSave: async (secret) => { + if (secret.changed('data')) { + const encryptedData = await SecretHelper.encryptSecret( + secret.data, + secret.name + ) + secret.data = encryptedData + } + }, + afterFind: async (secret) => { + if (secret && secret.data) { + try { + const decryptedData = await SecretHelper.decryptSecret( + secret.data, + secret.name + ) + secret.data = decryptedData + } catch (error) { + console.error('Error decrypting secret data:', error) + secret.data = {} + } + } + } + } + }) + + return Secret +} diff --git a/src/data/models/service.js b/src/data/models/service.js new file mode 100644 index 000000000..86570b49f --- /dev/null +++ b/src/data/models/service.js @@ -0,0 +1,93 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const Service = sequelize.define('Service', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id' + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + field: 'name', + unique: true, + index: true + }, + type: { + type: DataTypes.ENUM('microservice', 'k8s', 'agent', 'external'), + allowNull: false, + field: 'type' + }, + resource: { + type: DataTypes.TEXT, + allowNull: false + }, + targetPort: { + type: DataTypes.INTEGER, + allowNull: false, + field: 'target_port' + }, + // protocol: { + // type: DataTypes.ENUM('tcp', 'http'), + // defaultValue: 'tcp', + // allowNull: false + // }, + servicePort: { + type: DataTypes.INTEGER, + allowNull: true, + field: 'service_port' + }, + k8sType: { + type: DataTypes.ENUM('LoadBalancer', 'ClusterIP', 'NodePort'), + allowNull: true, + field: 'k8s_type' + }, + bridgePort: { + type: DataTypes.INTEGER, + allowNull: true, + field: 'bridge_port' + }, + defaultBridge: { + type: DataTypes.TEXT, + allowNull: false, + field: 'default_bridge', + defaultValue: 'default-router' + }, + serviceEndpoint: { + type: DataTypes.TEXT, + field: 'service_endpoint' + }, + provisioningStatus: { + type: DataTypes.ENUM('pending', 'ready', 'failed'), + allowNull: false, + field: 'provisioning_status', + defaultValue: 'pending' + }, + provisioningError: { + type: DataTypes.TEXT, + field: 'provisioning_error' + } + }, { + tableName: 'Services', + timestamps: true, + underscored: true + }) + + Service.associate = function (models) { + Service.belongsTo(models.Microservice, { + foreignKey: { + name: 'resource', + field: 'resource' + }, + as: 'microservice', + // We don't want to enforce this constraint since resource could be various types + constraints: false + }) + // Relationship with tags + Service.belongsToMany(models.Tags, { as: 'tags', through: 'ServiceTags' }) + } + + return Service +} diff --git a/src/data/models/serviceTags.js b/src/data/models/serviceTags.js new file mode 100644 index 000000000..6019d6ea5 --- /dev/null +++ b/src/data/models/serviceTags.js @@ -0,0 +1,9 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const ServiceTags = sequelize.define('ServiceTags', {}, { + tableName: 'ServiceTags', + timestamps: false, + underscored: true + }) + return ServiceTags +} diff --git a/src/data/models/tags.js b/src/data/models/tags.js index 585703a18..9c7957722 100644 --- a/src/data/models/tags.js +++ b/src/data/models/tags.js @@ -22,6 +22,9 @@ module.exports = (sequelize, DataTypes) => { Tags.associate = function (models) { Tags.belongsToMany(models.Fog, { through: 'IofogTags', as: 'iofogs' }) Tags.belongsToMany(models.EdgeResource, { through: 'EdgeResourceOrchestrationTags', as: 'edgeResources' }) + Tags.belongsToMany(models.Microservice, { through: 'MicroservicePubTags', as: 'pubMicroservices' }) + Tags.belongsToMany(models.Microservice, { through: 'MicroserviceSubTags', as: 'subMicroservices' }) + Tags.belongsToMany(models.Service, { through: 'ServiceTags', as: 'services' }) } return Tags } diff --git a/src/data/models/user.js b/src/data/models/user.js deleted file mode 100644 index 48a38a4f1..000000000 --- a/src/data/models/user.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' -module.exports = (sequelize, DataTypes) => { - const User = sequelize.define('User', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, - field: 'id' - }, - firstName: { - /* eslint-disable new-cap */ - type: DataTypes.STRING(100), - field: 'first_name', - defaultValue: '' - }, - lastName: { - /* eslint-disable new-cap */ - type: DataTypes.STRING(100), - field: 'last_name', - defaultValue: '' - }, - email: { - /* eslint-disable new-cap */ - type: DataTypes.STRING(100), - field: 'email', - defaultValue: '' - }, - password: { - /* eslint-disable new-cap */ - type: DataTypes.STRING(100), - field: 'password' - }, - tempPassword: { - /* eslint-disable new-cap */ - type: DataTypes.STRING(100), - field: 'temp_password' - }, - emailActivated: { - type: DataTypes.BOOLEAN, - field: 'email_activated', - defaultValue: false - } - }, { - tableName: 'Users', - timestamps: false, - underscored: true - }) - User.associate = function (models) { - User.hasOne(models.AccessToken, { - foreignKey: 'user_id', - as: 'accessToken' - }) - - User.hasMany(models.Application, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'application' - }) - - User.hasMany(models.Fog, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'fog' - }) - - User.hasMany(models.Microservice, { - foreignKey: { - name: 'userId', - field: 'user_id' - }, - as: 'microservice' - }) - } - return User -} diff --git a/src/data/models/volumeMount.js b/src/data/models/volumeMount.js new file mode 100644 index 000000000..db27b4fae --- /dev/null +++ b/src/data/models/volumeMount.js @@ -0,0 +1,43 @@ +'use strict' + +module.exports = (sequelize, DataTypes) => { + const VolumeMount = sequelize.define('VolumeMount', { + uuid: { + type: DataTypes.STRING(36), + primaryKey: true, + allowNull: false, + field: 'uuid' + }, + name: { + type: DataTypes.STRING, + allowNull: false, + field: 'name' + }, + configMapName: { + type: DataTypes.STRING, + allowNull: true, + field: 'config_map_name' + }, + secretName: { + type: DataTypes.STRING, + allowNull: true, + field: 'secret_name' + }, + version: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1, + field: 'version' + } + }, { + tableName: 'VolumeMounts', + timestamps: true, + underscored: true + }) + + VolumeMount.associate = function (models) { + VolumeMount.belongsToMany(models.Fog, { through: 'FogVolumeMounts', as: 'fogs' }) + } + + return VolumeMount +} diff --git a/src/data/providers/database-factory.js b/src/data/providers/database-factory.js index 12c17b728..3538908b6 100644 --- a/src/data/providers/database-factory.js +++ b/src/data/providers/database-factory.js @@ -1,7 +1,7 @@ const config = require('../../config') function createDatabaseProvider () { - let provider = process.env.DB_PROVIDER || config.get('Database:Provider', 'sqlite') + let provider = process.env.DB_PROVIDER || config.get('database.provider', 'sqlite') if (!provider) { provider = 'sqlite' diff --git a/src/data/providers/database-provider.js b/src/data/providers/database-provider.js index 067946283..134b4bf73 100644 --- a/src/data/providers/database-provider.js +++ b/src/data/providers/database-provider.js @@ -1,36 +1,633 @@ const path = require('path') -const Sequelize = require('sequelize') -const Umzug = require('umzug') +const fs = require('fs') +const sqlite3 = require('sqlite3').verbose() +const logger = require('../../logger') class DatabaseProvider { constructor () { this.basename = path.basename(__filename) } - async initDB () { - throw new Error('Not Implemented') + // Helper method to create database if it doesn't exist + async createDatabaseIfNotExists (db, provider, dbName) { + let checkQuery, createQuery + switch (provider) { + case 'mysql': + checkQuery = `SHOW DATABASES LIKE '${dbName}'` + createQuery = `CREATE DATABASE IF NOT EXISTS \`${dbName}\`` + break + case 'postgres': + checkQuery = `SELECT 1 FROM pg_database WHERE datname = '${dbName}'` + createQuery = `CREATE DATABASE "${dbName}"` + break + default: + return // No need to create database for SQLite + } + + try { + // For MySQL, we need to connect without a database first + if (provider === 'mysql') { + const mysql = require('mysql2/promise') + const config = { ...db.config } + // Remove database from config for initial connection + delete config.database + + const tempConnection = await mysql.createConnection(config) + try { + const [result] = await tempConnection.query(checkQuery) + const databaseExists = result.length > 0 + + if (!databaseExists) { + logger.info(`Creating database ${dbName}...`) + await tempConnection.query(createQuery) + logger.info(`Database ${dbName} created successfully`) + } else { + logger.info(`Database ${dbName} already exists`) + } + } finally { + await tempConnection.end() + } + } else if (provider === 'postgres') { + const { Pool } = require('pg') + const config = { ...db.config } + // Remove database from config for initial connection + delete config.database + + const pool = new Pool(config) + try { + const result = await pool.query(checkQuery) + const databaseExists = result.rows && result.rows.length > 0 + + if (!databaseExists) { + logger.info(`Creating database ${dbName}...`) + await pool.query(createQuery) + logger.info(`Database ${dbName} created successfully`) + } else { + logger.info(`Database ${dbName} already exists`) + } + } finally { + await pool.end() + } + } + } catch (err) { + logger.error(`Error checking/creating database ${dbName}:`, err) + throw err + } + } + + // Common method to check if migration has been run + async checkMigrationVersion (db, provider) { + let query + switch (provider) { + case 'sqlite': + query = 'SELECT migration_version FROM SchemaVersion WHERE migration_version IS NOT NULL ORDER BY id DESC LIMIT 1' + return new Promise((resolve, reject) => { + db.get(query, (err, row) => { + if (err) { + if (err.message.includes('no such table')) { + resolve(null) // Table doesn't exist yet + } else { + reject(err) + } + } else { + resolve(row ? row.migration_version : null) + } + }) + }) + case 'mysql': + query = 'SELECT migration_version FROM SchemaVersion WHERE migration_version IS NOT NULL ORDER BY id DESC LIMIT 1' + break + case 'postgres': + query = 'SELECT migration_version FROM "SchemaVersion" WHERE migration_version IS NOT NULL ORDER BY id DESC LIMIT 1' + break + } + + try { + const [results] = await db.query(query) + return results && results.length > 0 ? results[0].migration_version : null + } catch (err) { + if (err.code === 'ER_NO_SUCH_TABLE' || err.code === '42P01') { + return null // Table doesn't exist yet + } + throw err + } + } + + // Common method to check if seeder has been run + async checkSeederVersion (db, provider) { + let query + switch (provider) { + case 'sqlite': + query = 'SELECT seeder_version FROM SchemaVersion WHERE seeder_version IS NOT NULL ORDER BY id DESC LIMIT 1' + return new Promise((resolve, reject) => { + db.get(query, (err, row) => { + if (err) { + if (err.message.includes('no such table')) { + resolve(null) // Table doesn't exist yet + } else { + reject(err) + } + } else { + resolve(row ? row.seeder_version : null) + } + }) + }) + case 'mysql': + query = 'SELECT seeder_version FROM SchemaVersion WHERE seeder_version IS NOT NULL ORDER BY id DESC LIMIT 1' + break + case 'postgres': + query = 'SELECT seeder_version FROM "SchemaVersion" WHERE seeder_version IS NOT NULL ORDER BY id DESC LIMIT 1' + break + } + + try { + const [results] = await db.query(query) + return results && results.length > 0 ? results[0].seeder_version : null + } catch (err) { + if (err.code === 'ER_NO_SUCH_TABLE' || err.code === '42P01') { + return null // Table doesn't exist yet + } + throw err + } + } + + // Common method to create SchemaVersion table + async createSchemaVersionTable (db, provider) { + let query + switch (provider) { + case 'sqlite': + query = ` + CREATE TABLE IF NOT EXISTS SchemaVersion ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + migration_version TEXT NOT NULL, + seeder_version TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + ` + return new Promise((resolve, reject) => { + db.run(query, (err) => { + if (err) reject(err) + else resolve() + }) + }) + case 'mysql': + query = ` + CREATE TABLE IF NOT EXISTS SchemaVersion ( + id INT AUTO_INCREMENT PRIMARY KEY, + migration_version VARCHAR(255) NOT NULL, + seeder_version VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) + ` + break + case 'postgres': + query = ` + CREATE TABLE IF NOT EXISTS "SchemaVersion" ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + migration_version VARCHAR(255) NOT NULL, + seeder_version VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ` + break + } + + try { + await db.query(query) + } catch (err) { + throw err + } + } + + // Common method to update migration version + async updateMigrationVersion (db, version, provider) { + let query + switch (provider) { + case 'sqlite': + query = 'INSERT INTO SchemaVersion (migration_version) VALUES (?)' + return new Promise((resolve, reject) => { + db.run(query, [version], (err) => { + if (err) reject(err) + else resolve() + }) + }) + case 'mysql': + query = 'INSERT INTO SchemaVersion (migration_version) VALUES (?)' + await db.query(query, { replacements: [version] }) + break + case 'postgres': + query = 'INSERT INTO "SchemaVersion" (migration_version) VALUES ($1)' + await db.query(query, { bind: [version] }) + break + } } - createUmzug (path) { - if (!this.sequelize) { - throw new Error('Sequelize is not initialized') + // Common method to update seeder version + async updateSeederVersion (db, version, provider) { + switch (provider) { + case 'sqlite': + const sqliteQuery = 'UPDATE SchemaVersion SET seeder_version = ?, updated_at = CURRENT_TIMESTAMP WHERE id = (SELECT MAX(id) FROM SchemaVersion)' + return new Promise((resolve, reject) => { + db.run(sqliteQuery, [version], (err) => { + if (err) reject(err) + else resolve() + }) + }) + case 'mysql': + const [result] = await db.query('SELECT MAX(id) as maxId FROM SchemaVersion') + const maxId = result[0].maxId + const mysqlQuery = 'UPDATE SchemaVersion SET seeder_version = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?' + await db.query(mysqlQuery, { replacements: [version, maxId] }) + break + case 'postgres': + const postgresQuery = 'UPDATE "SchemaVersion" SET seeder_version = $1, updated_at = CURRENT_TIMESTAMP WHERE id = (SELECT MAX(id) FROM "SchemaVersion")' + await db.query(postgresQuery, { bind: [version] }) + break + } + } + + // SQLite migration + async runMigrationSQLite (dbName) { + const migrationSqlPath = path.resolve(__dirname, '../migrations/sqlite/db_migration_sqlite_v1.0.6.sql') + const migrationVersion = '1.0.6' + + if (!fs.existsSync(migrationSqlPath)) { + logger.error(`Migration file not found: ${migrationSqlPath}`) + throw new Error('Migration file not found') } - return new Umzug({ - storage: 'sequelize', - storageOptions: { - sequelize: this.sequelize - }, - logging: false, - migrations: { - params: [ - this.sequelize.getQueryInterface(), - Sequelize - ], - path, - pattern: /\.js$/ + const migrationSql = fs.readFileSync(migrationSqlPath).toString() + const dataArr = migrationSql.split(';') + + let db = new sqlite3.Database(dbName, (err) => { + if (err) { + logger.error(err.message) + throw err } + logger.info('Connected to the SQLite database for migration.') }) + + try { + await this.createSchemaVersionTable(db, 'sqlite') + const currentVersion = await this.checkMigrationVersion(db, 'sqlite') + + if (currentVersion === migrationVersion) { + logger.info('Migration already up to date, skipping...') + return + } + + db.serialize(() => { + db.run('PRAGMA foreign_keys=OFF;') + db.run('BEGIN TRANSACTION;') + }) + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + await new Promise((resolve, reject) => { + db.run(query, (err) => { + if (err) { + if (err.message.includes('already exists') || err.message.includes('duplicate')) { + logger.warn(`Ignored error: ${err.message}`) + resolve() + } else { + db.run('ROLLBACK;') + reject(err) + } + } else { + resolve() + } + }) + }) + } + } + + await this.updateMigrationVersion(db, migrationVersion, 'sqlite') + db.run('COMMIT;') + logger.info('Migration completed successfully.') + } catch (err) { + logger.error('Migration failed:', err) + throw err + } finally { + db.close((err) => { + if (err) { + logger.error('Error closing database connection:', err.message) + } else { + logger.info('Database connection closed after migration.') + } + }) + } + } + + // MySQL migration + async runMigrationMySQL (db) { + const migrationSqlPath = path.resolve(__dirname, '../migrations/mysql/db_migration_mysql_v1.0.6.sql') + const migrationVersion = '1.0.6' + + if (!fs.existsSync(migrationSqlPath)) { + logger.error(`Migration file not found: ${migrationSqlPath}`) + throw new Error('Migration file not found') + } + + const migrationSql = fs.readFileSync(migrationSqlPath).toString() + const dataArr = migrationSql.split(';') + + try { + await this.createSchemaVersionTable(db, 'mysql') + const currentVersion = await this.checkMigrationVersion(db, 'mysql') + + if (currentVersion === migrationVersion) { + logger.info('Migration already up to date, skipping...') + return + } + + await db.query('START TRANSACTION') + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + try { + await db.query(query) + } catch (err) { + // Check both the error and its parent (for Sequelize errors) + const errorToCheck = err.parent || err + if (errorToCheck.code === 'ER_TABLE_EXISTS_ERROR' || + errorToCheck.code === 'ER_DUP_FIELDNAME' || + errorToCheck.code === 'ER_DUP_KEYNAME' || + errorToCheck.code === 'ER_BLOB_KEY_WITHOUT_LENGTH' || + errorToCheck.code === 'ER_CANT_DROP_FIELD_OR_KEY' || + errorToCheck.code === 'duplicate_key' || + errorToCheck.code === 'already_exists' || + errorToCheck.errno === 1091 || + errorToCheck.errno === 1061 || + errorToCheck.errno === 1170) { + logger.warn(`Ignored MySQL error: ${errorToCheck.message}`) + } else { + await db.query('ROLLBACK') + throw err + } + } + } + } + + await this.updateMigrationVersion(db, migrationVersion, 'mysql') + await db.query('COMMIT') + logger.info('Migration completed successfully.') + } catch (err) { + await db.query('ROLLBACK') + logger.error('Migration failed:', err) + throw err + } + } + + // PostgreSQL migration + async runMigrationPostgres (db) { + const migrationSqlPath = path.resolve(__dirname, '../migrations/postgres/db_migration_pg_v1.0.6.sql') + const migrationVersion = '1.0.6' + + if (!fs.existsSync(migrationSqlPath)) { + logger.error(`Migration file not found: ${migrationSqlPath}`) + throw new Error('Migration file not found') + } + + const migrationSql = fs.readFileSync(migrationSqlPath).toString() + const dataArr = migrationSql.split(';') + + try { + await this.createSchemaVersionTable(db, 'postgres') + const currentVersion = await this.checkMigrationVersion(db, 'postgres') + + if (currentVersion === migrationVersion) { + logger.info('Migration already up to date, skipping...') + return + } + + await db.query('BEGIN') + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + try { + await db.query(query) + } catch (err) { + // Check both the error and its parent (for Sequelize errors) + const errorToCheck = err.parent || err + + // If transaction is aborted, rollback and start new transaction + if (errorToCheck.code === '25P02') { + logger.warn('Transaction aborted, rolling back and starting new transaction...') + await db.query('ROLLBACK') + await db.query('BEGIN') + continue + } + + if (errorToCheck.code === '42P07' || // duplicate_table + errorToCheck.code === '42701' || // duplicate_column + errorToCheck.code === '42P06' || // duplicate_schema + errorToCheck.code === '23505' || // unique_violation + errorToCheck.code === '23503' || // foreign_key_violation + errorToCheck.code === '42P01' || // undefined_table + errorToCheck.code === '42703' || // undefined_column + errorToCheck.code === '42P16' || // invalid_table_definition + errorToCheck.code === '42P17' || // invalid_table_definition + errorToCheck.code === '42P18' || // invalid_table_definition + (errorToCheck.message && ( + errorToCheck.message.includes('already exists') || + errorToCheck.message.includes('duplicate key') || + errorToCheck.message.includes('relation') + ))) { + logger.warn(`Ignored PostgreSQL error: ${errorToCheck.message}`) + } else { + await db.query('ROLLBACK') + throw err + } + } + } + } + + await this.updateMigrationVersion(db, migrationVersion, 'postgres') + await db.query('COMMIT') + logger.info('Migration completed successfully.') + } catch (err) { + await db.query('ROLLBACK') + logger.error('Migration failed:', err) + throw err + } + } + + // SQLite seeder + async runSeederSQLite (dbName) { + const seederSqlPath = path.resolve(__dirname, '../seeders/sqlite/db_seeder_sqlite_v1.0.2.sql') + const seederVersion = '1.0.2' + + if (!fs.existsSync(seederSqlPath)) { + logger.error(`Seeder file not found: ${seederSqlPath}`) + throw new Error('Seeder file not found') + } + + const seederSql = fs.readFileSync(seederSqlPath).toString() + const dataArr = seederSql.split(';') + + let db = new sqlite3.Database(dbName, (err) => { + if (err) { + logger.error(err.message) + throw err + } + logger.info('Connected to the SQLite database for seeding.') + }) + + try { + const currentVersion = await this.checkSeederVersion(db, 'sqlite') + + if (currentVersion === seederVersion) { + logger.info('Seeder already up to date, skipping...') + return + } + + db.serialize(() => { + db.run('PRAGMA foreign_keys=OFF;') + db.run('BEGIN TRANSACTION;') + }) + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + await new Promise((resolve, reject) => { + db.run(query, (err) => { + if (err) { + if (err.message.includes('already exists') || err.message.includes('duplicate')) { + logger.warn(`Ignored error: ${err.message}`) + resolve() + } else { + db.run('ROLLBACK;') + reject(err) + } + } else { + resolve() + } + }) + }) + } + } + + await this.updateSeederVersion(db, seederVersion, 'sqlite') + db.run('COMMIT;') + logger.info('Seeding completed successfully.') + } catch (err) { + logger.error('Seeding failed:', err) + throw err + } finally { + db.close((err) => { + if (err) { + logger.error('Error closing database connection:', err.message) + } else { + logger.info('Database connection closed after seeding.') + } + }) + } + } + + // MySQL seeder + async runSeederMySQL (db) { + const seederSqlPath = path.resolve(__dirname, '../seeders/mysql/db_seeder_mysql_v1.0.2.sql') + const seederVersion = '1.0.2' + + if (!fs.existsSync(seederSqlPath)) { + logger.error(`Seeder file not found: ${seederSqlPath}`) + throw new Error('Seeder file not found') + } + + const seederSql = fs.readFileSync(seederSqlPath).toString() + const dataArr = seederSql.split(';') + + try { + const currentVersion = await this.checkSeederVersion(db, 'mysql') + + if (currentVersion === seederVersion) { + logger.info('Seeder already up to date, skipping...') + return + } + + await db.query('START TRANSACTION') + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + try { + await db.query(query) + } catch (err) { + if (err.code === 'ER_DUP_ENTRY' || + err.code === 'ER_DUP_KEY') { + logger.warn(`Ignored MySQL error: ${err.message}`) + } else { + await db.query('ROLLBACK') + throw err + } + } + } + } + + await this.updateSeederVersion(db, seederVersion, 'mysql') + await db.query('COMMIT') + logger.info('Seeding completed successfully.') + } catch (err) { + await db.query('ROLLBACK') + logger.error('Seeding failed:', err) + throw err + } + } + + // PostgreSQL seeder + async runSeederPostgres (db) { + const seederSqlPath = path.resolve(__dirname, '../seeders/postgres/db_seeder_pg_v1.0.2.sql') + const seederVersion = '1.0.2' + + if (!fs.existsSync(seederSqlPath)) { + logger.error(`Seeder file not found: ${seederSqlPath}`) + throw new Error('Seeder file not found') + } + + const seederSql = fs.readFileSync(seederSqlPath).toString() + const dataArr = seederSql.split(';') + + try { + const currentVersion = await this.checkSeederVersion(db, 'postgres') + + if (currentVersion === seederVersion) { + logger.info('Seeder already up to date, skipping...') + return + } + + await db.query('BEGIN') + + for (let query of dataArr) { + if (query.trim()) { + query = query.trim() + ';' + try { + await db.query(query) + } catch (err) { + if (err.code === '23505' || // unique_violation + err.code === '23503') { // foreign_key_violation + logger.warn(`Ignored PostgreSQL error: ${err.message}`) + } else { + await db.query('ROLLBACK') + throw err + } + } + } + } + + await this.updateSeederVersion(db, seederVersion, 'postgres') + await db.query('COMMIT') + logger.info('Seeding completed successfully.') + } catch (err) { + await db.query('ROLLBACK') + logger.error('Seeding failed:', err) + throw err + } } } diff --git a/src/data/providers/mysql.js b/src/data/providers/mysql.js new file mode 100644 index 000000000..a899ea965 --- /dev/null +++ b/src/data/providers/mysql.js @@ -0,0 +1,103 @@ +const Sequelize = require('sequelize') +const config = require('../../config') +const DatabaseProvider = require('./database-provider') +const logger = require('../../logger') +const mysql = require('mysql2/promise') + +class MySqlDatabaseProvider extends DatabaseProvider { + constructor () { + super() + + // Get MySQL configuration from config or environment variables + const mysqlConfig = config.get('database.mysql', {}) + + // Base MySQL connection options + const connectionOptions = { + host: process.env.DB_HOST || mysqlConfig.host, + port: process.env.DB_PORT || mysqlConfig.port, + user: process.env.DB_USERNAME || mysqlConfig.username, + password: process.env.DB_PASSWORD || mysqlConfig.password, + database: process.env.DB_NAME || mysqlConfig.databaseName, + connectTimeout: 10000 + } + + // Configure SSL if enabled + const useSSL = process.env.DB_USE_SSL === 'true' || mysqlConfig.useSsl === true + if (useSSL) { + const caBase64 = process.env.DB_SSL_CA_B64 + const sslOptions = caBase64 + ? { + ca: Buffer.from(caBase64, 'base64').toString('utf-8'), + rejectUnauthorized: true + } + : { rejectUnauthorized: false } + + connectionOptions.ssl = sslOptions + } + + // Sequelize configuration + const sequelizeConfig = { + dialect: 'mysql', + host: connectionOptions.host, + port: connectionOptions.port, + username: connectionOptions.user, + password: connectionOptions.password, + database: connectionOptions.database, + dialectOptions: { + connectTimeout: connectionOptions.connectTimeout + }, + logging: false + } + + // Add SSL configuration to Sequelize if enabled + if (useSSL) { + const caBase64 = process.env.DB_SSL_CA_B64 + sequelizeConfig.dialectOptions.ssl = caBase64 + ? { + ca: Buffer.from(caBase64, 'base64').toString('utf-8'), + rejectUnauthorized: true + } + : { rejectUnauthorized: false } + } + + this.sequelize = new Sequelize(sequelizeConfig) + this.connectionOptions = connectionOptions + } + + async initDB () { + try { + // First try to connect to the database directly + const connection = await mysql.createConnection(this.connectionOptions) + await connection.end() + } catch (err) { + // Check both the error and its parent (for Sequelize errors) + const errorToCheck = err.parent || err + + if (errorToCheck.code === 'ER_BAD_DB_ERROR') { + // Database doesn't exist, try to create it + logger.info('Database does not exist, attempting to create it...') + const { database, ...connectionConfig } = this.connectionOptions + const tempConnection = await mysql.createConnection(connectionConfig) + try { + await tempConnection.query(`CREATE DATABASE IF NOT EXISTS \`${database}\``) + logger.info(`Database ${database} created successfully`) + } finally { + await tempConnection.end() + } + } else if (errorToCheck.code === 'ER_DUP_KEYNAME' || + errorToCheck.errno === 1061 || + (errorToCheck.message && ( + errorToCheck.message.includes('Error 1050') || // Table already exists + errorToCheck.message.includes('Error 1060') || // Duplicate column name + errorToCheck.message.includes('Error 1054') || // Unknown column + errorToCheck.message.includes('Error 1061') // Duplicate key name + ))) { + logger.info(`Ignoring known MySQL error: ${errorToCheck.message}`) + } else { + throw err + } + } + } +} + +module.exports = MySqlDatabaseProvider diff --git a/src/data/providers/postgres.js b/src/data/providers/postgres.js new file mode 100644 index 000000000..bfbe071cd --- /dev/null +++ b/src/data/providers/postgres.js @@ -0,0 +1,95 @@ +const Sequelize = require('sequelize') +const config = require('../../config') +const DatabaseProvider = require('./database-provider') +const logger = require('../../logger') +const { Pool } = require('pg') + +class PostgresDatabaseProvider extends DatabaseProvider { + constructor () { + super() + + // Get PostgreSQL configuration from config or environment variables + const postgresConfig = config.get('database.postgres', {}) + + // Base PostgreSQL connection options + const connectionOptions = { + host: process.env.DB_HOST || postgresConfig.host, + port: process.env.DB_PORT || postgresConfig.port, + user: process.env.DB_USERNAME || postgresConfig.username, + password: process.env.DB_PASSWORD || postgresConfig.password, + database: process.env.DB_NAME || postgresConfig.databaseName, + connectTimeout: 10000 + } + + // Configure SSL if enabled + const useSSL = process.env.DB_USE_SSL === 'true' || postgresConfig.useSsl === true + if (useSSL) { + const caBase64 = process.env.DB_SSL_CA_B64 + const sslOptions = caBase64 + ? { + ca: Buffer.from(caBase64, 'base64').toString('utf-8'), + rejectUnauthorized: true + } + : { rejectUnauthorized: false } + + connectionOptions.ssl = sslOptions + } + + // Sequelize configuration + const sequelizeConfig = { + dialect: 'postgres', + host: connectionOptions.host, + port: connectionOptions.port, + username: connectionOptions.user, + password: connectionOptions.password, + database: connectionOptions.database, + dialectOptions: { + connectTimeout: connectionOptions.connectTimeout + }, + logging: false + } + // Add SSL configuration to Sequelize if enabled + if (useSSL) { + const caBase64 = process.env.DB_SSL_CA_B64 + sequelizeConfig.dialectOptions.ssl = caBase64 + ? { + ca: Buffer.from(caBase64, 'base64').toString('utf-8'), + rejectUnauthorized: true + } + : { rejectUnauthorized: false } + } + + this.sequelize = new Sequelize(sequelizeConfig) + this.connectionOptions = connectionOptions + } + + async initDB () { + try { + // First try to connect to the database directly + const pool = new Pool(this.connectionOptions) + await pool.query('SELECT 1') + await pool.end() + } catch (err) { + if (err.code === '3D000') { // PostgreSQL error code for database doesn't exist + // Database doesn't exist, try to create it + logger.info('Database does not exist, attempting to create it...') + const { database, ...connectionConfig } = this.connectionOptions + // Connect to the default 'postgres' database to create the target database + const pool = new Pool({ + ...connectionConfig, + database: 'postgres' + }) + try { + await pool.query(`CREATE DATABASE "${database}"`) + logger.info(`Database ${database} created successfully`) + } finally { + await pool.end() + } + } else { + throw err + } + } + } +} + +module.exports = PostgresDatabaseProvider diff --git a/src/data/providers/sqlite.js b/src/data/providers/sqlite.js index a34da5ee3..15312e325 100644 --- a/src/data/providers/sqlite.js +++ b/src/data/providers/sqlite.js @@ -3,13 +3,13 @@ const fs = require('fs') const Sequelize = require('sequelize') const config = require('../../config') -const DatabaseProvider = require(`./database-provider`) +const DatabaseProvider = require('./database-provider') class SqliteDatabaseProvider extends DatabaseProvider { constructor () { super() - const sqliteConfig = config.get('Database:Config', {}) + const sqliteConfig = config.get('database.sqlite', {}) sqliteConfig.dialect = 'sqlite' sqliteConfig.databaseName = process.env.DB_NAME || sqliteConfig.databaseName if (!sqliteConfig.databaseName.endsWith('.sqlite')) { diff --git a/src/data/seeders/20180928110125-insert-registry.js b/src/data/seeders/20180928110125-insert-registry.js deleted file mode 100644 index 87b33f0f8..000000000 --- a/src/data/seeders/20180928110125-insert-registry.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('Registries', [ - { - url: 'registry.hub.docker.com', - is_public: true, - secure: true, - certificate: '', - requires_cert: false, - user_name: '', - password: '', - user_email: '', - user_id: null - }, - { - url: 'from_cache', - is_public: true, - secure: true, - certificate: '', - requires_cert: false, - user_name: '', - password: '', - user_email: '', - user_id: null - } - ]) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('Registries', null, {}) - } -} diff --git a/src/data/seeders/20180928111532-insert-catalog-item.js b/src/data/seeders/20180928111532-insert-catalog-item.js deleted file mode 100644 index 8e0c8e157..000000000 --- a/src/data/seeders/20180928111532-insert-catalog-item.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('CatalogItems', [ - { - name: 'Networking Tool', - description: 'The built-in networking tool for Eclipse ioFog.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }, - { - name: 'RESTBlue', - description: 'REST API for Bluetooth Low Energy layer.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }, - { - name: 'HAL', - description: 'REST API for Hardware Abstraction layer.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }, - { - name: 'Diagnostics', - description: '0', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/580.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Hello Web Demo', - description: 'A simple web server to test Eclipse ioFog.', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/4.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Open Weather Map Data', - description: 'A stream of data from the Open Weather Map API in JSON format', - category: 'SENSORS', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/8.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'JSON REST API', - description: 'A configurable REST API that gives JSON output', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/49.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Temperature Converter', - description: 'A simple temperature format converter', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/58.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'JSON Sub-Select', - description: 'Performs sub-selection and transform operations on any JSON messages', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/59.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Humidity Sensor Simulator', - description: 'Humidity Sensor Simulator for Eclipse ioFog', - category: 'SIMULATOR', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/simulator.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Seismic Sensor Simulator', - description: 'Seismic Sensor Simulator for Eclipse ioFog', - category: 'SIMULATOR', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/simulator.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - }, - { - name: 'Temperature Sensor Simulator', - description: 'Temperature Sensor Simulator for Eclipse ioFog', - category: 'SIMULATOR', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'images/build/simulator.png', - config_example: null, - is_public: true, - registry_id: 1, - user_id: null - } - ]) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('CatalogItems', null, {}) - } -} diff --git a/src/data/seeders/20180928112152-insert-iofog-type.js b/src/data/seeders/20180928112152-insert-iofog-type.js deleted file mode 100644 index df1f994cd..000000000 --- a/src/data/seeders/20180928112152-insert-iofog-type.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('FogTypes', [ - { - id: 0, - name: 'Unspecified', - image: 'iointegrator0.png', - description: 'Unspecified device. Fog Type will be selected on provision', - network_catalog_item_id: 1, - hal_catalog_item_id: 3, - bluetooth_catalog_item_id: 2 - }, - { - id: 1, - name: 'Standard Linux (x86)', - image: 'iointegrator1.png', - description: 'A standard Linux server of at least moderate processing power and capacity. ' + - 'Compatible with common Linux types such as Ubuntu, Red Hat, and CentOS.', - network_catalog_item_id: 1, - hal_catalog_item_id: 3, - bluetooth_catalog_item_id: 2 - }, - { - id: 2, - name: 'ARM Linux', - image: 'iointegrator2.png', - description: 'A version of ioFog meant to run on Linux systems with ARM processors. ' + - 'Microservices for this ioFog type will be tailored to ARM systems.', - network_catalog_item_id: 1, - hal_catalog_item_id: 3, - bluetooth_catalog_item_id: 2 - } - ]).then(() => { - return queryInterface.bulkUpdate('Fogs', - { - fog_type_id: 0 - }, - { - fog_type_id: null - } - ) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('FogTypes', null, {}) - } -} diff --git a/src/data/seeders/20180928121334-insert-catalog-item-image.js b/src/data/seeders/20180928121334-insert-catalog-item-image.js deleted file mode 100644 index e75a67cb9..000000000 --- a/src/data/seeders/20180928121334-insert-catalog-item-image.js +++ /dev/null @@ -1,132 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('CatalogItemImages', [ - { - catalog_item_id: 1, - fog_type_id: 1, - container_image: 'iofog/core-networking' - }, - { - catalog_item_id: 1, - fog_type_id: 2, - container_image: 'iofog/core-networking-arm' - }, - { - catalog_item_id: 2, - fog_type_id: 1, - container_image: 'iofog/restblue' - }, - { - catalog_item_id: 2, - fog_type_id: 2, - container_image: 'iofog/restblue-arm' - }, - { - catalog_item_id: 3, - fog_type_id: 1, - container_image: 'iofog/hal' - }, - { - catalog_item_id: 3, - fog_type_id: 2, - container_image: 'iofog/hal-arm' - }, - { - catalog_item_id: 4, - fog_type_id: 1, - container_image: 'iofog/diagnostics' - }, - { - catalog_item_id: 4, - fog_type_id: 2, - container_image: 'iofog/diagnostics-arm' - }, - { - catalog_item_id: 5, - fog_type_id: 1, - container_image: 'iofog/hello-web' - }, - { - catalog_item_id: 5, - fog_type_id: 2, - container_image: 'iofog/hello-web-arm' - }, - { - catalog_item_id: 6, - fog_type_id: 1, - container_image: 'iofog/open-weather-map' - }, - { - catalog_item_id: 6, - fog_type_id: 2, - container_image: 'iofog/open-weather-map-arm' - }, - { - catalog_item_id: 7, - fog_type_id: 1, - container_image: 'iofog/json-rest-api' - }, - { - catalog_item_id: 7, - fog_type_id: 2, - container_image: 'iofog/json-rest-api-arm' - }, - { - catalog_item_id: 8, - fog_type_id: 1, - container_image: 'iofog/temperature-conversion' - }, - { - catalog_item_id: 8, - fog_type_id: 2, - container_image: 'iofog/temperature-conversion-arm' - }, - { - catalog_item_id: 9, - fog_type_id: 1, - container_image: 'iofog/json-subselect' - }, - { - catalog_item_id: 9, - fog_type_id: 2, - container_image: 'iofog/json-subselect-arm' - }, - { - catalog_item_id: 10, - fog_type_id: 1, - container_image: 'iofog/humidity-sensor-simulator' - }, - { - catalog_item_id: 10, - fog_type_id: 2, - container_image: 'iofog/humidity-sensor-simulator-arm' - }, - { - catalog_item_id: 11, - fog_type_id: 1, - container_image: 'iofog/seismic-sensor-simulator' - }, - { - catalog_item_id: 11, - fog_type_id: 2, - container_image: 'iofog/seismic-sensor-simulator-arm' - }, - { - catalog_item_id: 12, - fog_type_id: 1, - container_image: 'iofog/temperature-sensor-simulator' - }, - { - catalog_item_id: 12, - fog_type_id: 2, - container_image: 'iofog/temperature-sensor-simulator-arm' - } - ]) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('CatalogItemImages', null, {}) - } -} diff --git a/src/data/seeders/20190130112616-insert-logging-catalog-item.js b/src/data/seeders/20190130112616-insert-logging-catalog-item.js deleted file mode 100644 index 4a2d97769..000000000 --- a/src/data/seeders/20190130112616-insert-logging-catalog-item.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('CatalogItems', [ - { - name: 'Common Logging', - description: 'Container which gathers logs and provides REST API' + - ' for adding and querying logs from containers', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: '{"access_tokens": ["Some_Access_Token"], "cleanfrequency": "1h40m", "ttl": "24h"}', - is_public: false, - registry_id: 1, - user_id: null - }] - ).then(() => { - return queryInterface.bulkInsert('CatalogItemImages', [ - { - catalog_item_id: 13, - fog_type_id: 1, - container_image: 'iofog/common-logging' - }, - { - catalog_item_id: 13, - fog_type_id: 2, - container_image: 'iofog/common-logging-arm' - } - ] - ) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('CatalogItems', { ID: 13 }, {}).then(() => { - return queryInterface.bulkDelete('CatalogItemImages', { catalog_item_id: 13 }) - }) - } -} diff --git a/src/data/seeders/20190131111441-insert-json-generator-catalog-item.js b/src/data/seeders/20190131111441-insert-json-generator-catalog-item.js deleted file mode 100644 index ae454d2bf..000000000 --- a/src/data/seeders/20190131111441-insert-json-generator-catalog-item.js +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkInsert('CatalogItems', [ - { - name: 'JSON Generator', - description: 'Container generates ioMessages with contentdata as complex JSON object.', - category: 'UTILITIES', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: '{}', - is_public: true, - registry_id: 1, - user_id: null - }] - ).then(() => { - return queryInterface.bulkInsert('CatalogItemImages', [ - { - catalog_item_id: 14, - fog_type_id: 1, - container_image: 'iofog/json-generator' - }, - { - catalog_item_id: 14, - fog_type_id: 2, - container_image: 'iofog/json-generator-arm' - } - ]) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkDelete('CatalogItems', { ID: 14 }, {}).then(() => { - return queryInterface.bulkDelete('CatalogItemImages', { catalog_item_id: 14 }) - }) - } -} diff --git a/src/data/seeders/20190218103641-adding-default-configs.js b/src/data/seeders/20190218103641-adding-default-configs.js deleted file mode 100644 index 824d8b8db..000000000 --- a/src/data/seeders/20190218103641-adding-default-configs.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{"citycode":"5391997","apikey":"6141811a6136148a00133488eadff0fb","frequency":1000}' - }, - { - name: 'Open Weather Map Data' - } - ).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{"buffersize":3,"contentdataencoding":"utf8","contextdataencoding":"utf8",' + - 'outputfields":{"publisher":"source","contentdata":"temperature","timestamp":"time"}}' - }, - { - name: 'JSON REST API' - } - ) - }).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{}' - }, - { - name: 'JSON Sub-Select' - } - ) - }).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - is_public: true - }, - { - name: 'Common Logging' - } - ) - }) - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{}' - }, - { - name: 'Open Weather Map Data' - } - ).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{}' - }, - { - name: 'JSON REST API' - } - ) - }).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - config_example: '{}' - }, - { - name: 'JSON Sub-Select' - } - ) - }).then(() => { - return queryInterface.bulkUpdate('CatalogItems', - { - is_public: false - }, - { - name: 'Common Logging' - } - ) - }) - } -} diff --git a/src/data/seeders/20200122200231-insert-router-catalog-item.js b/src/data/seeders/20200122200231-insert-router-catalog-item.js deleted file mode 100644 index df6353ece..000000000 --- a/src/data/seeders/20200122200231-insert-router-catalog-item.js +++ /dev/null @@ -1,87 +0,0 @@ -const constants = require('../constants') -const CatalogItemManager = require('../managers/catalog-item-manager') - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.bulkInsert('CatalogItems', [ - { - name: constants.ROUTER_CATALOG_NAME, - description: 'The built-in router for Eclipse ioFog.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }, - { - name: constants.PROXY_CATALOG_NAME, - description: 'The built-in proxy for Eclipse ioFog.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }] - ) - - // const sequelize = queryInterface.sequelize - // const transaction = await sequelize.transaction() - const transaction = { fakeTransaction: true } - - const router = await CatalogItemManager.findOne({ name: constants.ROUTER_CATALOG_NAME, isPublic: false }, transaction) - const proxy = await CatalogItemManager.findOne({ name: constants.PROXY_CATALOG_NAME, isPublic: false }, transaction) - - if (!router || !proxy) { - throw new Error('Unable to update the database. Could not find proxy or router catalog item') - } - - await queryInterface.bulkInsert('CatalogItemImages', [ - { - catalog_item_id: router.id, - fog_type_id: 1, - container_image: 'quay.io/interconnectedcloud/qdrouterd:latest' - }, - { - catalog_item_id: router.id, - fog_type_id: 2, - container_image: 'iofog/qdrouterd-arm:latest' - }, - { - catalog_item_id: proxy.id, - fog_type_id: 1, - container_image: 'iofog/proxy:latest' - }, - { - catalog_item_id: proxy.id, - fog_type_id: 2, - container_image: 'iofog/proxy-arm:latest' - }]) - }, - - down: async (queryInterface, Sequelize) => { - // const sequelize = queryInterface.sequelize - - // const transaction = await sequelize.transaction() - const transaction = { fakeTransaction: true } - - const router = await CatalogItemManager.findOne({ name: constants.ROUTER_CATALOG_NAME, isPublic: false }, transaction) - const proxy = await CatalogItemManager.findOne({ name: constants.PROXY_CATALOG_NAME, isPublic: false }, transaction) - if (router) { - await queryInterface.bulkDelete('CatalogItems', { id: router.id }, {}) - await queryInterface.bulkDelete('CatalogItemImages', { catalog_item_id: router.id }) - } - - if (proxy) { - await queryInterface.bulkDelete('CatalogItems', { id: proxy.id }, {}) - await queryInterface.bulkDelete('CatalogItemImages', { catalog_item_id: proxy.id }) - } - } -} diff --git a/src/data/seeders/20200313163114-update-router-catalog-item.js b/src/data/seeders/20200313163114-update-router-catalog-item.js deleted file mode 100644 index e1d9d0e72..000000000 --- a/src/data/seeders/20200313163114-update-router-catalog-item.js +++ /dev/null @@ -1,58 +0,0 @@ -const constants = require('../constants') -const CatalogItemManager = require('../managers/catalog-item-manager') - -module.exports = { - up: async (queryInterface, Sequelize) => { - const transaction = { fakeTransaction: true } - - const router = await CatalogItemManager.findOne({ name: constants.ROUTER_CATALOG_NAME, isPublic: false }, transaction) - - if (!router) { - throw new Error('Unable to update the database. Could not find router catalog item') - } - - await queryInterface.bulkUpdate('CatalogItemImages', - { - container_image: 'iofog/router:latest' - }, - { - catalog_item_id: router.id, - fog_type_id: 1 - }) - await queryInterface.bulkUpdate('CatalogItemImages', - { - container_image: 'iofog/router-arm:latest' - }, - { - catalog_item_id: router.id, - fog_type_id: 2 - }) - }, - - down: async (queryInterface, Sequelize) => { - const transaction = { fakeTransaction: true } - - const router = await CatalogItemManager.findOne({ name: constants.ROUTER_CATALOG_NAME, isPublic: false }, transaction) - - if (!router) { - throw new Error('Unable to update the database. Could not find router catalog item') - } - - await queryInterface.bulkUpdate('CatalogItemImages', - { - container_image: 'quay.io/interconnectedcloud/qdrouterd:latest' - }, - { - catalog_item_id: router.id, - fog_type_id: 1 - }) - await queryInterface.bulkUpdate('CatalogItemImages', - { - container_image: 'iofog/qdrouterd-arm:latest' - }, - { - catalog_item_id: router.id, - fog_type_id: 2 - }) - } -} diff --git a/src/data/seeders/20221019163114-insert-port-router-catalog-item.js b/src/data/seeders/20221019163114-insert-port-router-catalog-item.js deleted file mode 100644 index bfa750cc1..000000000 --- a/src/data/seeders/20221019163114-insert-port-router-catalog-item.js +++ /dev/null @@ -1,57 +0,0 @@ -const constants = require('../constants') -const CatalogItemManager = require('../managers/catalog-item-manager') - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.bulkInsert('CatalogItems', [ - { - name: constants.PORT_ROUTER_CATALOG_NAME, - description: 'The built-in port-router for Eclipse ioFog.', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - disk_required: 0, - ram_required: 0, - picture: 'none.png', - config_example: null, - is_public: false, - registry_id: 1, - user_id: null - }] - ) - - // const sequelize = queryInterface.sequelize - // const transaction = await sequelize.transaction() - const transaction = { fakeTransaction: true } - - const portRouter = await CatalogItemManager.findOne({ name: constants.PORT_ROUTER_CATALOG_NAME, isPublic: false }, transaction) - - if (!portRouter) { - throw new Error('Unable to update the database. Could not find port-router catalog item') - } - - await queryInterface.bulkInsert('CatalogItemImages', [ - { - catalog_item_id: portRouter.id, - fog_type_id: 1, - container_image: 'iofog/port-router:latest' - }, - { - catalog_item_id: portRouter.id, - fog_type_id: 2, - container_image: 'iofog/port-router:latest' - }]) - }, - - down: async (queryInterface, Sequelize) => { - // const sequelize = queryInterface.sequelize - - // const transaction = await sequelize.transaction() - const transaction = { fakeTransaction: true } - - const portRouter = await CatalogItemManager.findOne({ name: constants.PORT_ROUTER_CATALOG_NAME, isPublic: false }, transaction) - if (portRouter) { - await queryInterface.bulkDelete('CatalogItems', { id: portRouter.id }, {}) - await queryInterface.bulkDelete('CatalogItemImages', { catalog_item_id: portRouter.id }) - } - } -} diff --git a/src/data/seeders/mysql/db_seeder_mysql_v1.0.2.sql b/src/data/seeders/mysql/db_seeder_mysql_v1.0.2.sql new file mode 100644 index 000000000..d73d393ee --- /dev/null +++ b/src/data/seeders/mysql/db_seeder_mysql_v1.0.2.sql @@ -0,0 +1,40 @@ +START TRANSACTION; + +INSERT INTO `Registries` (url, is_public, user_name, password, user_email) +VALUES + ('registry.hub.docker.com', true, '', '', ''), + ('from_cache', true, '', '', ''); + +INSERT INTO `CatalogItems` (name, description, category, publisher, disk_required, ram_required, picture, config_example, is_public, registry_id) +VALUES + ('Router', 'The built-in router for Datasance PoT.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('RESTBlue', 'REST API for Bluetooth Low Energy layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('HAL', 'REST API for Hardware Abstraction layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('Debug', 'The built-in debugger for Datasance PoT IoFog Agent.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('NATs', 'NATs server microservice for Datasance PoT', 'UTILITIES', 'Datasance', 0, 0, 'none.png', NULL, true, 1); + +INSERT INTO `FogTypes` (id, name, image, description, network_catalog_item_id, hal_catalog_item_id, bluetooth_catalog_item_id) +VALUES + (0, 'Unspecified', 'iointegrator0.png', 'Unspecified device. Fog Type will be selected on provision', 1, 3, 2), + (1, 'Standard Linux (x86)', 'iointegrator1.png', 'A standard Linux server of at least moderate processing power and capacity. Compatible with common Linux types such as Ubuntu, Red Hat, and CentOS.', 1, 3, 2), + (2, 'ARM Linux', 'iointegrator2.png', 'A version of ioFog meant to run on Linux systems with ARM processors. Microservices for this ioFog type will be tailored to ARM systems.', 1, 3, 2); + +UPDATE `Fogs` +SET fog_type_id = 0 +WHERE fog_type_id IS NULL; + +INSERT INTO `CatalogItemImages` (catalog_item_id, fog_type_id, container_image) +VALUES + (1, 1, 'ghcr.io/datasance/router:latest'), + (1, 2, 'ghcr.io/datasance/router:latest'), + (2, 1, 'ghcr.io/datasance/restblue:latest'), + (2, 2, 'ghcr.io/datasance/restblue:latest'), + (3, 1, 'ghcr.io/datasance/hal:latest'), + (3, 2, 'ghcr.io/datasance/hal:latest'), + (4, 1, 'ghcr.io/datasance/node-debugger:latest'), + (4, 2, 'ghcr.io/datasance/node-debugger:latest'), + (5, 1, 'ghcr.io/datasance/nats:latest'), + (5, 2, 'ghcr.io/datasance/nats:latest'); + + +COMMIT; \ No newline at end of file diff --git a/src/data/seeders/postgres/db_seeder_pg_v1.0.2.sql b/src/data/seeders/postgres/db_seeder_pg_v1.0.2.sql new file mode 100644 index 000000000..ca8d08218 --- /dev/null +++ b/src/data/seeders/postgres/db_seeder_pg_v1.0.2.sql @@ -0,0 +1,39 @@ +START TRANSACTION; + +INSERT INTO "Registries" (url, is_public, user_name, password, user_email) +VALUES + ('registry.hub.docker.com', true, '', '', ''), + ('from_cache', true, '', '', ''); + +INSERT INTO "CatalogItems" (name, description, category, publisher, disk_required, ram_required, picture, config_example, is_public, registry_id) +VALUES + ('Router', 'The built-in router for Datasance PoT.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('RESTBlue', 'REST API for Bluetooth Low Energy layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('HAL', 'REST API for Hardware Abstraction layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('Debug', 'The built-in debugger for Datasance PoT IoFog Agent.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('NATs', 'NATs server microservice for Datasance PoT', 'UTILITIES', 'Datasance', 0, 0, 'none.png', NULL, true, 1); + +INSERT INTO "FogTypes" (id, name, image, description, network_catalog_item_id, hal_catalog_item_id, bluetooth_catalog_item_id) +VALUES + (0, 'Unspecified', 'iointegrator0.png', 'Unspecified device. Fog Type will be selected on provision', 1, 3, 2), + (1, 'Standard Linux (x86)', 'iointegrator1.png', 'A standard Linux server of at least moderate processing power and capacity. Compatible with common Linux types such as Ubuntu, Red Hat, and CentOS.', 1, 3, 2), + (2, 'ARM Linux', 'iointegrator2.png', 'A version of ioFog meant to run on Linux systems with ARM processors. Microservices for this ioFog type will be tailored to ARM systems.', 1, 3, 2); + +UPDATE "Fogs" +SET fog_type_id = 0 +WHERE fog_type_id IS NULL; + +INSERT INTO "CatalogItemImages" (catalog_item_id, fog_type_id, container_image) +VALUES + (1, 1, 'ghcr.io/datasance/router:latest'), + (1, 2, 'ghcr.io/datasance/router:latest'), + (2, 1, 'ghcr.io/datasance/restblue:latest'), + (2, 2, 'ghcr.io/datasance/restblue:latest'), + (3, 1, 'ghcr.io/datasance/hal:latest'), + (3, 2, 'ghcr.io/datasance/hal:latest'), + (4, 1, 'ghcr.io/datasance/node-debugger:latest'), + (4, 2, 'ghcr.io/datasance/node-debugger:latest'), + (5, 1, 'ghcr.io/datasance/nats:latest'), + (5, 2, 'ghcr.io/datasance/nats:latest'); + +COMMIT; \ No newline at end of file diff --git a/src/data/seeders/sqlite/db_seeder_sqlite_v1.0.2.sql b/src/data/seeders/sqlite/db_seeder_sqlite_v1.0.2.sql new file mode 100644 index 000000000..80d4ee2ce --- /dev/null +++ b/src/data/seeders/sqlite/db_seeder_sqlite_v1.0.2.sql @@ -0,0 +1,35 @@ +INSERT INTO `Registries` (url, is_public, user_name, password, user_email) +VALUES + ('registry.hub.docker.com', true, '', '', ''), + ('from_cache', true, '', '', ''); + +INSERT INTO `CatalogItems` (name, description, category, publisher, disk_required, ram_required, picture, config_example, is_public, registry_id) +VALUES + ('Router', 'The built-in router for Datasance PoT.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('RESTBlue', 'REST API for Bluetooth Low Energy layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('HAL', 'REST API for Hardware Abstraction layer.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, true, 1), + ('Debug', 'The built-in debugger for Datasance PoT IoFog Agent.', 'SYSTEM', 'Datasance', 0, 0, 'none.png', NULL, false, 1), + ('NATs', 'NATs server microservice for Datasance PoT', 'UTILITIES', 'Datasance', 0, 0, 'none.png', NULL, true, 1); + +INSERT INTO `FogTypes` (id, name, image, description, network_catalog_item_id, hal_catalog_item_id, bluetooth_catalog_item_id) +VALUES + (0, 'Unspecified', 'iointegrator0.png', 'Unspecified device. Fog Type will be selected on provision', 1, 3, 2), + (1, 'Standard Linux (x86)', 'iointegrator1.png', 'A standard Linux server of at least moderate processing power and capacity. Compatible with common Linux types such as Ubuntu, Red Hat, and CentOS.', 1, 3, 2), + (2, 'ARM Linux', 'iointegrator2.png', 'A version of ioFog meant to run on Linux systems with ARM processors. Microservices for this ioFog type will be tailored to ARM systems.', 1, 3, 2); + +UPDATE `Fogs` +SET fog_type_id = 0 +WHERE fog_type_id IS NULL; + +INSERT INTO `CatalogItemImages` (catalog_item_id, fog_type_id, container_image) +VALUES + (1, 1, 'ghcr.io/datasance/router:latest'), + (1, 2, 'ghcr.io/datasance/router:latest'), + (2, 1, 'ghcr.io/datasance/restblue:latest'), + (2, 2, 'ghcr.io/datasance/restblue:latest'), + (3, 1, 'ghcr.io/datasance/hal:latest'), + (3, 2, 'ghcr.io/datasance/hal:latest'), + (4, 1, 'ghcr.io/datasance/node-debugger:latest'), + (4, 2, 'ghcr.io/datasance/node-debugger:latest'), + (5, 1, 'ghcr.io/datasance/nats:latest'), + (5, 2, 'ghcr.io/datasance/nats:latest'); diff --git a/src/decorators/authorization-decorator.js b/src/decorators/authorization-decorator.js index 732003908..9003b0b67 100644 --- a/src/decorators/authorization-decorator.js +++ b/src/decorators/authorization-decorator.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,74 +11,80 @@ * */ const logger = require('../logger') -const config = require('../config') -const UserManager = require('../data/managers/user-manager') -const AccessTokenManager = require('../data/managers/access-token-manager') const FogManager = require('../data/managers/iofog-manager') -const FogAccessTokenManager = require('../data/managers/iofog-access-token-manager') +const FogKeyService = require('../services/iofog-key-service') const Errors = require('../helpers/errors') const { isTest } = require('../helpers/app-helper') -function checkAuthToken (f) { +function checkFogToken (f) { return async function (...fArgs) { if (isTest()) { return f.apply(this, fArgs) } const req = fArgs[0] - const token = req.headers.authorization - - const user = await UserManager.checkAuthentication(token) + const authHeader = req.headers.authorization - if (!user) { - logger.error('token ' + token + ' incorrect') + if (!authHeader) { + logger.error('No authorization token provided') throw new Errors.AuthenticationError('authorization failed') } - if (Date.now() > user.accessToken.expirationTime) { - logger.error('token ' + token + ' expired') - throw new Errors.AuthenticationError('token expired') + + // Extract token from Bearer scheme + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() !== 'bearer' || !token) { + logger.error('Invalid authorization scheme') + throw new Errors.AuthenticationError('authorization failed') } - fArgs.push(user) - AccessTokenManager.updateExpirationTime(user.accessToken.id, user.accessToken.expirationTime + - config.get('Settings:UserTokenExpirationIntervalSeconds') * 1000) - return f.apply(this, fArgs) - } -} + try { + // Debug log for JWT + logger.debug({ token }, 'Received JWT') -function checkFogToken (f) { - return async function (...fArgs) { - if (isTest()) { - return f.apply(this, fArgs) - } + // First, decode the JWT without verification to get the fog UUID + const tokenParts = token.split('.') + if (tokenParts.length !== 3) { + logger.error('Invalid JWT format') + throw new Errors.AuthenticationError('authorization failed') + } - const req = fArgs[0] - const token = req.headers.authorization + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + const fogUuid = payload.sub + logger.debug({ payload }, 'JWT payload') + logger.info({ iofogUUID: payload.sub }) - const fog = await FogManager.checkToken(token) + if (!fogUuid) { + logger.error('JWT missing subject claim') + throw new Errors.AuthenticationError('authorization failed') + } - if (!fog) { - logger.error('token ' + token + ' incorrect') - throw new Errors.AuthenticationError('authorization failed') - } - if (Date.now() > fog.accessToken.expirationTime) { - logger.error('token ' + token + ' expired') - throw new Errors.AuthenticationError('token expired') - } + // Get the fog with transaction + const fog = await FogManager.findOne({ + uuid: fogUuid + }, { fakeTransaction: true }) - fArgs.push(fog) + if (!fog) { + logger.error(`Fog with UUID ${fogUuid} not found`) + throw new Errors.AuthenticationError('authorization failed') + } - FogAccessTokenManager.updateExpirationTime(fog.accessToken.id, fog.accessToken.expirationTime + - config.get('Settings:FogTokenExpirationIntervalSeconds') * 1000) + // Verify the JWT with transaction + await FogKeyService.verifyJWT(token, fogUuid, { fakeTransaction: true }) - const timestamp = Date.now() - await FogManager.updateLastActive(fog.uuid, timestamp) + // Update last active timestamp with transaction + const timestamp = Date.now() + await FogManager.updateLastActive(fog.uuid, timestamp, { fakeTransaction: true }) - return f.apply(this, fArgs) + fArgs.push(fog) + + return f.apply(this, fArgs) + } catch (error) { + logger.error(`JWT verification failed: ${error.message}`) + throw new Errors.AuthenticationError('authorization failed') + } } } module.exports = { - checkAuthToken: checkAuthToken, checkFogToken: checkFogToken } diff --git a/src/decorators/cli-decorator.js b/src/decorators/cli-decorator.js deleted file mode 100644 index 4606e578f..000000000 --- a/src/decorators/cli-decorator.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const logger = require('../logger') -const UserManager = require('../data/managers/user-manager') -const Errors = require('../helpers/errors') -const { isTest } = require('../helpers/app-helper') - -function prepareUserById (f) { - return async function (...args) { - if (isTest()) { - return f.apply(this, args) - } - - const fArgs = Array.prototype.slice.call(args) - const obj = fArgs[0] - const userId = obj.userId - - const user = await UserManager.findById(userId) - if (!user) { - throw new Errors.AuthenticationError('user id does not exist') - } - - delete obj.userId - fArgs.push(user) - - return f.apply(this, fArgs) - } -} - -function prepareUserByEmail (f) { - return async function (...args) { - if (isTest()) { - return f.apply(this, args) - } - - const fArgs = Array.prototype.slice.call(args) - const obj = fArgs[0] - const email = obj.email - - const user = await UserManager.findByEmail(email) - - if (!user) { - logger.error('user email ' + email + ' incorrect') - throw new Errors.AuthenticationError('user email does not exist') - } - - delete obj.email - fArgs.push(user) - - return f.apply(this, fArgs) - } -} - -module.exports = { - prepareUserById: prepareUserById, - prepareUserByEmail: prepareUserByEmail - -} diff --git a/src/decorators/response-decorator.js b/src/decorators/response-decorator.js index 61f5a6359..a2c7d461c 100644 --- a/src/decorators/response-decorator.js +++ b/src/decorators/response-decorator.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/decorators/transaction-decorator.js b/src/decorators/transaction-decorator.js index a5d33ddce..4940cccc4 100644 --- a/src/decorators/transaction-decorator.js +++ b/src/decorators/transaction-decorator.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/enums/fog-state.js b/src/enums/fog-state.js index 396a57e47..2c7d712af 100644 --- a/src/enums/fog-state.js +++ b/src/enums/fog-state.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,7 +13,14 @@ const fogState = { UNKNOWN: 'UNKNOWN', - RUNNING: 'RUNNING' + RUNNING: 'RUNNING', + STOPPED: 'STOPPED', + WAITING: 'WAITING', + WARNING: 'WARNING', + DEBUGGING: 'DEBUGGING', + DEPROVISIONED: 'DEPROVISIONED', + ERROR: 'ERROR', + NOT_PROVISIONED: 'NOT_PROVISIONED' } module.exports = fogState diff --git a/src/enums/microservice-state.js b/src/enums/microservice-state.js index 86e79f44c..ac985f44c 100644 --- a/src/enums/microservice-state.js +++ b/src/enums/microservice-state.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -30,4 +30,10 @@ const microserviceState = { CREATING: 'CREATING' } -module.exports = microserviceState +const microserviceExecState = { + PENDING: 'PENDING', + ACTIVE: 'ACTIVE', + INACTIVE: 'INACTIVE' +} + +module.exports = { microserviceState, microserviceExecState } diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index ffaab8b65..070b97e02 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,6 +13,7 @@ const crypto = require('crypto') const Errors = require('./errors') +const { v4: uuidv4 } = require('uuid') const logger = require('../logger') const fs = require('fs') @@ -60,6 +61,10 @@ function generateRandomString (size) { return randString } +function generateUUID () { + return uuidv4() +} + // Checks the status of a single port // returns 'closed' if port is available // returns 'open' if port is not available @@ -70,7 +75,7 @@ async function checkPortAvailability (port) { } const findAvailablePort = async function (hostname) { - let portRange = Config.get('Tunnel:PortRange') + let portRange = Config.get('tunnel.portRange') if (!portRange) { logger.warn('Port range was\'n specified in config. Default range (2000-10000) will be used') portRange = '2000-10000' @@ -194,6 +199,7 @@ module.exports = { encryptText, decryptText, generateRandomString, + generateUUID, isFileExists, isValidPort, isValidDomain, diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 0b4d7a5d3..c7f13218a 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 3351c7bff..c12f37b59 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -25,6 +25,7 @@ module.exports = { INVALID_IOFOG_UUID: 'Invalid ioFog UUID \'{}\'', INVALID_USER_EMAIL: 'Invalid user email', INVALID_MICROSERVICE_UUID: 'Invalid microservice UUID \'{}\'', + INVALID_MICROSERVICE_NAME: 'Invalid microservice NAME \'{}\'', INVALID_SOURCE_MICROSERVICE_UUID: 'Invalid source microservice UUID \'{}\'', INVALID_DEST_MICROSERVICE_UUID: 'Invalid destination microservice UUID \'{}\'', INVALID_SOURCE_MICROSERVICE_NAME: 'Invalid source microservice name \'{}\'', @@ -56,6 +57,7 @@ module.exports = { 'List of invalid microservices:\n', INVALID_MICROSERVICE_CONFIG: 'Can\'t create network microservice without appropriate configuration.', INVALID_MICROSERVICE_USER: 'Invalid microservice user or UUID', + APPLICATION_NOT_ACTIVATED: 'Application {} is not activated', ROUTE_NOT_FOUND: 'Route not found', IMAGE_SNAPSHOT_WITHOUT_FOG: 'Can not run image snapshot for microservice without ioFog.', IMAGE_SNAPSHOT_NOT_AVAILABLE: 'Image snapshot is not available for this microservice.', @@ -77,6 +79,8 @@ module.exports = { REGISTRY_NOT_FOUND: 'Registry not found', USER_ALREADY_ACTIVATED: 'User is already activated.', USER_NOT_ACTIVATED_YET: 'User is not activated yet.', + REGISTRY_IS_SYSTEM: 'Registry is system and can\'t be updated or deleted', + REGISTRY_IS_IN_USE: 'Registry is in use by microservices and can\'t be deleted', CLI: { INVALID_PORT_MAPPING: 'Port mapping parsing error. Please provide valid port mapping.', INVALID_VOLUME_MAPPING: 'Volume mapping parsing error. Please provide valid volume mapping.', @@ -102,6 +106,7 @@ module.exports = { PORT_RESERVED: 'Port \'{}\' is reserved for internal use', INVALID_HOST_TEMPLATE: '{} is not a valid host template', NOT_FOUND_HOST_TEMPLATE: 'Could not find {} host template', + NOT_FOUND_APPS_TEMPLATE: 'The microservice you would like to add as an extra host is not in the same fog as the microservice you are adding it to', MISSING_IMAGE: 'Microservice {} does not have a valid image for its Agent type', DUPLICATE_RESOURCE_NAME_VERSION: 'Resource {} version {} already exists', NOT_FOUND_RESOURCE_NAME_VERSION: 'Could not find resource {} version {}', @@ -110,5 +115,34 @@ module.exports = { INVALID_APPLICATION_TEMPLATE_NAME: 'Could not find Application Template {}', APPLICATION_TEMPLATE_INVALID: 'Application Tempalte {} is invalid', WRONG_PUBLIC_LINK_PROTOCOL: 'Public port {} has a scheme of {} and cannot use protocol {}', - NO_AVAILABLE_PUBLIC_PORT: 'No public port available in range for {}' + NO_AVAILABLE_PUBLIC_PORT: 'No public port available in range for {}', + INVALID_MICROSERVICE_PUB_TAG: 'Invalid microservice Pub Tag \'{}\'', + INVALID_MICROSERVICE_SUB_TAG: 'Invalid microservice Sub Tag \'{}\'', + NOTFOUND_MICROSERVICE_PUB_TAG: 'No microservice found for Pub Tag \'{}\'', + NOTFOUND_MICROSERVICE_SUB_TAG: 'No microservice found for Sub Tag \'{}\'', + SECRET_ALREADY_EXISTS: 'Secret with name "{}" already exists', + SECRET_NOT_FOUND: 'Secret with name "{}" not found', + // Certificate related error messages + CA_ALREADY_EXISTS: 'CA with name %s already exists', + CA_NOT_FOUND: 'CA with name %s not found', + CERTIFICATE_ALREADY_EXISTS: 'Certificate with name %s already exists', + CERTIFICATE_NOT_FOUND: 'Certificate with name %s not found', + INVALID_CERTIFICATE: 'Invalid certificate: %s', + INVALID_CA: 'Invalid CA: %s', + NOT_KUBERNETES_ENV: 'Controller is not running in Kubernetes environment', + K8S_SECRET_NOT_ALLOWED: 'Kubernetes secret type is not allowed in non-Kubernetes environment', + INVALID_DEFAULT_BRIDGE: 'Invalid default bridge, If service type is not microservice, defaultBridge must be default-router\'{}\'', + INVALID_ROUTER_CONNECTION: 'Invalid router connection, router {} is not connected to {}', + NO_AVAILABLE_BRIDGE_PORT: 'No bridge port available in range for {}', + CONFIGMAP_ALREADY_EXISTS: 'ConfigMap with name {} already exists', + CONFIGMAP_NOT_FOUND: 'ConfigMap with name {} not found', + INVALID_SECRET_REFERENCE: 'Invalid secret reference: {}', + INVALID_CONFIGMAP_REFERENCE: 'Invalid configmap reference: {}', + SECRET_KEY_NOT_FOUND: 'Secret key {} not found in secret {}', + CONFIGMAP_KEY_NOT_FOUND: 'Configmap key {} not found in configmap {}', + SECRET_KEY_NOT_FOUND_IN_VOLUME_MOUNT: 'Secret key {} not found in secret {} in volume mount {}', + CONFIGMAP_KEY_NOT_FOUND_IN_VOLUME_MOUNT: 'Configmap key {} not found in configmap {} in volume mount {}', + CONFIGMAP_IMMUTABLE: 'Configmap {} is immutable and cannot be updated. If you want to update it, please delete it and create a new configmap.', + VOLUME_MOUNT_NOT_FOUND: 'Volume mount with name {} not found', + INVALID_VOLUME_MOUNT_REFERENCE_FOR_VOLUME_MAPPING: 'Invalid volume mount reference for volume mapping: {}' } diff --git a/src/helpers/errors.js b/src/helpers/errors.js index 17493ba61..3d4f8c1cd 100644 --- a/src/helpers/errors.js +++ b/src/helpers/errors.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -78,15 +78,6 @@ class FtpError extends Error { } } -class EmailActivationSetupError extends Error { - constructor () { - const message = 'Email activation is not configured on Controller' - super(message) - this.message = message - this.name = 'EmailActivationSetupError' - } -} - class InvalidArgumentError extends Error { constructor (message) { super(message) @@ -111,6 +102,14 @@ class CLIArgsNotProvidedError extends Error { } } +class ConflictError extends Error { + constructor (message) { + super(message) + this.name = 'ConflictError' + this.status = 409 + } +} + module.exports = { AuthenticationError: AuthenticationError, TransactionError: TransactionError, @@ -120,8 +119,8 @@ module.exports = { ModelNotFoundError: ModelNotFoundError, DuplicatePropertyError: DuplicatePropertyError, FtpError: FtpError, - EmailActivationSetupError: EmailActivationSetupError, InvalidArgumentError: InvalidArgumentError, InvalidArgumentTypeError: InvalidArgumentTypeError, - CLIArgsNotProvidedError: CLIArgsNotProvidedError + CLIArgsNotProvidedError: CLIArgsNotProvidedError, + ConflictError: ConflictError } diff --git a/src/helpers/proxy-broker-client.js b/src/helpers/proxy-broker-client.js deleted file mode 100644 index 053837489..000000000 --- a/src/helpers/proxy-broker-client.js +++ /dev/null @@ -1,49 +0,0 @@ -const axios = require('axios') - -const controllerConfig = require('../config') - -const brokerUrl = process.env.PROXY_BROKER_URL || controllerConfig.get('PublicPorts:ProxyBrokerUrl', '') -const brokerToken = process.env.PROXY_BROKER_TOKEN || controllerConfig.get('PublicPorts:ProxyBrokerToken', '') - -function allocatePort (serverToken) { - var options = { - method: 'POST', - url: `${brokerUrl}/port`, - headers: { 'X-Api-Key': brokerToken }, - data: { token: serverToken } - } - - return axios(options) - .then(response => { - return response.data - }) - .catch(err => { - return err - }) -} - -function deallocatePort (portUUID) { - var options = { - method: 'DELETE', - url: `${brokerUrl}/port/${portUUID}`, - headers: { 'X-Api-Key': brokerToken } - } - - return axios(options) -} - -function revokeServerToken (token) { - var options = { - method: 'DELETE', - url: `${brokerUrl}/server-token/${token}`, - headers: { 'X-Api-Key': brokerToken } - } - - return axios(options) -} - -module.exports = { - allocatePort: allocatePort, - deallocatePort: deallocatePort, - revokeServerToken: revokeServerToken -} diff --git a/src/helpers/secret-helper.js b/src/helpers/secret-helper.js new file mode 100644 index 000000000..6e17595da --- /dev/null +++ b/src/helpers/secret-helper.js @@ -0,0 +1,59 @@ +const crypto = require('crypto') + +class SecretHelper { + constructor () { + this.ALGORITHM = 'aes-256-gcm' + this.IV_LENGTH = 12 + this.SALT_LENGTH = 16 + this.TAG_LENGTH = 16 + this.KEY_LENGTH = 32 + this.ITERATIONS = 100000 + } + + async encryptSecret (secretData, secretName) { + const salt = crypto.randomBytes(this.SALT_LENGTH) + const key = await this._deriveKey(secretName, salt) + const iv = crypto.randomBytes(this.IV_LENGTH) + const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv) + const encrypted = Buffer.concat([ + cipher.update(JSON.stringify(secretData), 'utf8'), + cipher.final() + ]) + const tag = cipher.getAuthTag() + return Buffer.concat([salt, iv, tag, encrypted]).toString('base64') + } + + async decryptSecret (encryptedData, secretName) { + const buffer = Buffer.from(encryptedData, 'base64') + const salt = buffer.subarray(0, this.SALT_LENGTH) + const iv = buffer.subarray(this.SALT_LENGTH, this.SALT_LENGTH + this.IV_LENGTH) + const tag = buffer.subarray(this.SALT_LENGTH + this.IV_LENGTH, this.SALT_LENGTH + this.IV_LENGTH + this.TAG_LENGTH) + const encrypted = buffer.subarray(this.SALT_LENGTH + this.IV_LENGTH + this.TAG_LENGTH) + const key = await this._deriveKey(secretName, salt) + const decipher = crypto.createDecipheriv(this.ALGORITHM, key, iv) + decipher.setAuthTag(tag) + const decrypted = Buffer.concat([ + decipher.update(encrypted), + decipher.final() + ]) + return JSON.parse(decrypted.toString('utf8')) + } + + async _deriveKey (secretName, salt) { + return new Promise((resolve, reject) => { + crypto.pbkdf2( + secretName, + salt, + this.ITERATIONS, + this.KEY_LENGTH, + 'sha256', + (err, key) => { + if (err) reject(err) + else resolve(key) + } + ) + }) + } +} + +module.exports = new SecretHelper() diff --git a/src/helpers/template-helper.js b/src/helpers/template-helper.js index b11750bec..a75ba093e 100755 --- a/src/helpers/template-helper.js +++ b/src/helpers/template-helper.js @@ -1,198 +1,188 @@ -/* - * Software Name : eclipse-iofog/Controller - * Version: 2.0.x - * SPDX-FileCopyrightText: Copyright (c) 2020-2020 Orange - * SPDX-License-Identifier: EPL-2.0 - * - * This software is distributed under the , - * the text of which is available at http://www.eclipse.org/legal/epl-2.0 - * or see the "license.txt" file for more details. - * - * Author: Franck Roudet - */ -const UserManager = require('../data/managers/user-manager') -const ApplicationManager = require('../data/managers/application-manager.js') // Using manager instead of service to avoid dependency loop -const FogService = require('../services/iofog-service') -const MicroservicesService = require('../services/microservices-service') -const EdgeResourceService = require('../services/edge-resource-service') - -// ninja2 like template engine -const { Liquid } = require('../lib/liquidjs/liquid.node.cjs') -const templateEngine = new Liquid() - -/** - * Add filter findAgent to template engine. - * Syntaxe {{ microservice | findMicroserviceAgent }} - */ - -function findMicroserviceAgentHandler (microservice) { - const user = this.context.environments._user - if (!user) { - return undefined - } - const result = FogService.getFogEndPoint({ uuid: microservice.iofogUuid }, user, false) - return result -} - -async function findEdgeResourcehandler (name, version) { - const key = `${name}/${version}` - const user = this.context.environments._user - if (!user) { - return undefined - } - if (this.context.environments._edgeResourcesByName && this.context.environments._edgeResourcesByName[key]) { - return this.context.environments._edgeResourcesByName[key] - } - const result = await EdgeResourceService.getEdgeResource({ name, version }, user) - - if (result && this.context.environments._edgeResourcesByName) { - this.context.environments._edgeResourcesByName[key] = result - } - return result -} - -async function findApplicationHandler (name) { - const user = this.context.environments._user - if (!user) { - return undefined - } - if (this.context.environments._applicationsByName && this.context.environments._applicationsByName[name]) { - return this.context.environments._applicationsByName[name] - } - - const result = await ApplicationManager.findOnePopulated({ name, userId: user.id }, { exclude: ['created_at', 'updated_at'] }, { fakeTransaction: true }) // TODO: Get a proper DB transaction - if (result) { - result.microservices = (await MicroservicesService.listMicroservicesEndPoint({ applicationName: name }, user, false)).microservices - if (this.context.environments._applicationsByName) { - this.context.environments._applicationsByName[name] = result - } - } - return result -} - -async function findAgentHandler (name) { - const user = this.context.environments._user - if (!user) { - return undefined - } - if (name === '') { - const { fogs: result } = await FogService.getFogListEndPoint([], user, false, false) - if (result && this.context.environments._agentsByName) { - result.forEach(agent => { - this.context.environments._agentsByName[agent.name] = agent - }) - } - return result - } - if (this.context.environments._agentsByName && this.context.environments._agentsByName[name]) { - return this.context.environments._agentsByName[name] - } - const result = await FogService.getFogEndPoint({ name }, user, false) - if (result && this.context.environments._agentsByName) { - this.context.environments._agentsByName[result.name] = result - } - return result -} - -async function JSONParser (variable) { - try { - console.log({ variable }) - return JSON.parse(variable) - } catch (e) { - return variable - } -} - -function toStringParser (variable) { - try { - if (typeof variable === 'string') { - return variable - } - if (variable.toString) { - return variable.toString() - } - return JSON.stringify(variable) - } catch (e) { - return variable - } -} -/** - * Add filter findEdgeRessource to template engine. - * user is in liquid context _user - * Syntaxe {{ name findEdgeRessource: version }} - */ -templateEngine.registerFilter('findEdgeResource', findEdgeResourcehandler) -templateEngine.registerFilter('findApplication', findApplicationHandler) -templateEngine.registerFilter('findAgent', findAgentHandler) -templateEngine.registerFilter('findMicroserviceAgent', findMicroserviceAgentHandler) -templateEngine.registerFilter('toNumber', JSONParser) -templateEngine.registerFilter('toBoolean', JSONParser) -templateEngine.registerFilter('toString', toStringParser) - -/** - * Object in depth traversal and right value templateEngine rendering - * @param {*} subjects - * @param {*} templateContext - */ -const rvaluesVarSubstition = async (subjects, templateContext, user) => { - let context = templateContext - // Due to the recursive nature of this function, user will only be defined on the first iteration - if (user) { - context = { - ...templateContext, - // Private context - _user: user // need by edge resource and every on demand request - } - } - - // Create local cache for filters if they do not exists - context._agentsByName = context._agentsByName || {} - context._edgeResourcesByName = context._edgeResourcesByName || {} - context._applicationsByName = context._applicationsByName || {} - - for (let key in subjects) { - try { - if (typeof subjects[key] === 'object') { - await rvaluesVarSubstition(subjects[key], context, null) - } else if (typeof subjects[key] === 'string') { - const result = await templateEngine.parseAndRender(subjects[key], context, { keepOutputType: true }) - subjects[key] = result - } - } catch (e) { - // Trace error in rendering - console.log({ e }) - subjects[key] = e.toString() - } - } - return subjects -} - -const substitutionMiddleware = async (req, res, next) => { - if (['POST', 'PUT', 'PATCH'].indexOf(req.method) > -1) { - const token = req.headers.authorization - let user - if (token) { - try { - user = await UserManager.checkAuthentication(token) - } catch (e) { - // Nothing to do, suppose the token has no permission to access. The is the case of agent - } - } - let tmplContext = { - self: req.body, - // Private context - _user: user // need by edge resource and every on demand request - } - try { - await rvaluesVarSubstition(req.body, tmplContext, user) - } catch (e) { - next(e) - } - } - next() -} - -module.exports = { - rvaluesVarSubstition, - substitutionMiddleware -} +/* + * Software Name : eclipse-iofog/Controller + * Version: 2.0.x + * SPDX-FileCopyrightText: Copyright (c) 2020-2020 Orange + * SPDX-License-Identifier: EPL-2.0 + * + * This software is distributed under the , + * the text of which is available at http://www.eclipse.org/legal/epl-2.0 + * or see the "license.txt" file for more details. + * + * Author: Franck Roudet + */ + +const ApplicationManager = require('../data/managers/application-manager.js') // Using manager instead of service to avoid dependency loop +const FogService = require('../services/iofog-service') +const MicroservicesService = require('../services/microservices-service') +const EdgeResourceService = require('../services/edge-resource-service') + +// ninja2 like template engine +const { Liquid } = require('../lib/liquidjs/liquid.node.cjs') +const templateEngine = new Liquid() + +/** + * Add filter findAgent to template engine. + * Syntaxe {{ microservice | findMicroserviceAgent }} + */ + +function findMicroserviceAgentHandler (microservice) { + // const user = this.context.environments._user + // if (!user) { + // return undefined + // } + const result = FogService.getFogEndPoint({ uuid: microservice.iofogUuid }, false) + return result +} + +async function findEdgeResourcehandler (name, version) { + const key = `${name}/${version}` + // const user = this.context.environments._user + // if (!user) { + // return undefined + // } + if (this.context.environments._edgeResourcesByName && this.context.environments._edgeResourcesByName[key]) { + return this.context.environments._edgeResourcesByName[key] + } + const result = await EdgeResourceService.getEdgeResource({ name, version }) + + if (result && this.context.environments._edgeResourcesByName) { + this.context.environments._edgeResourcesByName[key] = result + } + return result +} + +async function findApplicationHandler (name) { + // const user = this.context.environments._user + // if (!user) { + // return undefined + // } + if (this.context.environments._applicationsByName && this.context.environments._applicationsByName[name]) { + return this.context.environments._applicationsByName[name] + } + + const result = await ApplicationManager.findOnePopulated({ exclude: ['created_at', 'updated_at'] }, { fakeTransaction: true }) // TODO: Get a proper DB transaction + if (result) { + result.microservices = (await MicroservicesService.listMicroservicesEndPoint({ applicationName: name }, false)).microservices + if (this.context.environments._applicationsByName) { + this.context.environments._applicationsByName[name] = result + } + } + return result +} + +async function findAgentHandler (name) { + // const user = this.context.environments._user + // if (!user) { + // return undefined + // } + if (name === '') { + const { fogs: result } = await FogService.getFogListEndPoint([], false, false) + if (result && this.context.environments._agentsByName) { + result.forEach(agent => { + this.context.environments._agentsByName[agent.name] = agent + }) + } + return result + } + if (this.context.environments._agentsByName && this.context.environments._agentsByName[name]) { + return this.context.environments._agentsByName[name] + } + const result = await FogService.getFogEndPoint({ name }, false) + if (result && this.context.environments._agentsByName) { + this.context.environments._agentsByName[result.name] = result + } + return result +} + +async function JSONParser (variable) { + try { + console.log({ variable }) + return JSON.parse(variable) + } catch (e) { + return variable + } +} + +function toStringParser (variable) { + try { + if (typeof variable === 'string') { + return variable + } + if (variable.toString) { + return variable.toString() + } + return JSON.stringify(variable) + } catch (e) { + return variable + } +} +/** + * Add filter findEdgeRessource to template engine. + * user is in liquid context _user + * Syntaxe {{ name findEdgeRessource: version }} + */ +templateEngine.registerFilter('findEdgeResource', findEdgeResourcehandler) +templateEngine.registerFilter('findApplication', findApplicationHandler) +templateEngine.registerFilter('findAgent', findAgentHandler) +templateEngine.registerFilter('findMicroserviceAgent', findMicroserviceAgentHandler) +templateEngine.registerFilter('toNumber', JSONParser) +templateEngine.registerFilter('toBoolean', JSONParser) +templateEngine.registerFilter('toString', toStringParser) + +/** + * Object in depth traversal and right value templateEngine rendering + * @param {*} subjects + * @param {*} templateContext + */ +const rvaluesVarSubstition = async (subjects, templateContext) => { + let context = templateContext + // Due to the recursive nature of this function, user will only be defined on the first iteration + context = { + ...templateContext + // Private context + // _user: user // need by edge resource and every on demand request + } + + // Create local cache for filters if they do not exists + context._agentsByName = context._agentsByName || {} + context._edgeResourcesByName = context._edgeResourcesByName || {} + context._applicationsByName = context._applicationsByName || {} + + for (let key in subjects) { + try { + if (typeof subjects[key] === 'object') { + await rvaluesVarSubstition(subjects[key], context, null) + } else if (typeof subjects[key] === 'string') { + const result = await templateEngine.parseAndRender(subjects[key], context, { keepOutputType: true }) + subjects[key] = result + } + } catch (e) { + // Trace error in rendering + console.log({ e }) + subjects[key] = e.toString() + } + } + return subjects +} + +const substitutionMiddleware = async (req, res, next) => { + if (['POST', 'PUT', 'PATCH'].indexOf(req.method) > -1) { + // let user + let tmplContext = { + self: req.body + // Private context + // _user: user // need by edge resource and every on demand request + } + try { + await rvaluesVarSubstition(req.body, tmplContext) + } catch (e) { + next(e) + } + } + next() +} + +module.exports = { + rvaluesVarSubstition, + substitutionMiddleware +} diff --git a/src/init.js b/src/init.js new file mode 100644 index 000000000..2da8d0260 --- /dev/null +++ b/src/init.js @@ -0,0 +1,46 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +// Load configuration first +require('./config') + +// Initialize logger with configuration +const logger = require('./logger') +const { startTelemetry } = require('./config/telemetry') +const db = require('./data/models') + +async function initialize () { + try { + // Log initial steps using console since logger might not be ready + console.log('Configuration loaded') + console.log('Logger initialized with configuration') + + // Now we can use logger for the rest of initialization + logger.info('Initializing OpenTelemetry...') + startTelemetry() + + logger.info('Initializing database...') + await db.initDB(true) + + logger.info('Initialization completed successfully') + return true + } catch (error) { + // Use console.error here since logger might not be initialized + console.error('Initialization failed:', error) + process.exit(1) + } +} + +module.exports = { + initialize +} diff --git a/src/jobs/event-cleanup-job.js b/src/jobs/event-cleanup-job.js new file mode 100644 index 000000000..7ff0b74e5 --- /dev/null +++ b/src/jobs/event-cleanup-job.js @@ -0,0 +1,74 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const EventManager = require('../data/managers/event-manager') +const EventService = require('../services/event-service') +const Config = require('../config') +const logger = require('../logger') + +async function run () { + try { + await cleanupOldEvents() + } catch (error) { + logger.error('Error during event cleanup:', error) + } finally { + // Schedule next run with current interval (may have changed via env var) + const currentInterval = process.env.EVENT_CLEANUP_INTERVAL || Config.get('settings.eventCleanupInterval', 86400) + setTimeout(run, currentInterval * 1000) + } +} + +async function cleanupOldEvents () { + try { + // Read retention days from config + const retentionDays = process.env.EVENT_RETENTION_DAYS || Config.get('settings.eventRetentionDays', 7) + + logger.debug(`Starting cleanup of events older than ${retentionDays} days`) + const count = await EventManager.deleteEventsOlderThanDays(retentionDays, { fakeTransaction: true }) + logger.info(`Cleaned up ${count} events older than ${retentionDays} days`) + + // Create audit trail for automated cleanup (non-blocking) + // This allows admins to distinguish between manual deletions and automated cleanup + if (count > 0) { + setImmediate(async () => { + try { + await EventService.createEvent({ + timestamp: Date.now(), + eventType: 'HTTP', + endpointType: 'user', + actorId: 'SYSTEM_CLEANUP', + method: 'DELETE', + resourceType: 'event', + resourceId: null, + endpointPath: '/api/v3/events', + ipAddress: null, + status: 'SUCCESS', + statusCode: 200, + statusMessage: `Automated cleanup: Deleted ${count} events older than ${retentionDays} days`, + requestId: null + }, { fakeTransaction: true }).catch(err => { + logger.error('Failed to create cleanup job audit record (non-blocking):', err) + }) + } catch (error) { + logger.error('Error creating cleanup job audit record (non-blocking):', error) + } + }) + } + } catch (error) { + logger.error('Error during event cleanup:', error) + } +} + +module.exports = { + run +} diff --git a/src/jobs/fog-status-job.js b/src/jobs/fog-status-job.js index da3020a84..2d1f167c7 100644 --- a/src/jobs/fog-status-job.js +++ b/src/jobs/fog-status-job.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -16,19 +16,21 @@ const TransactionDecorator = require('../decorators/transaction-decorator') const FogManager = require('../data/managers/iofog-manager') const MicroserviceManager = require('../data/managers/microservice-manager') const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') const MicroserviceService = require('../services/microservices-service') -const MicroserviceStates = require('../enums/microservice-state') +const { microserviceState, microserviceExecState } = require('../enums/microservice-state') const FogStates = require('../enums/fog-state') const Config = require('../config') +const logger = require('../logger') -const scheduleTime = Config.get('Settings:FogStatusUpdateIntervalSeconds') * 1000 +const scheduleTime = Config.get('settings.fogStatusUpdateInterval') * 1000 async function run () { try { const _updateFogsConnectionStatus = TransactionDecorator.generateTransaction(updateFogsConnectionStatus) await _updateFogsConnectionStatus() } catch (error) { - console.error(error) + logger.error('Error during fog status update:', error) } finally { setTimeout(run, scheduleTime) } @@ -41,8 +43,11 @@ async function updateFogsConnectionStatus (transaction) { } async function _updateFogStatus (transaction) { - const statusUpdateTolerance = Config.get('Settings:FogStatusUpdateTolerance') - const fogs = await FogManager.findAll({ daemonStatus: FogStates.RUNNING }, transaction) + const statusUpdateTolerance = Config.get('settings.fogStatusUpdateTolerance') + const fogs = [ + ...await FogManager.findAll({ daemonStatus: FogStates.RUNNING }, transaction), + ...await FogManager.findAll({ daemonStatus: FogStates.WARNING }, transaction) + ] const unknownFogUuids = fogs .filter((fog) => { const statusUpdateToleranceMs = fog.statusFrequency * 1000 * statusUpdateTolerance @@ -58,10 +63,38 @@ async function _updateFogStatus (transaction) { async function _updateMicroserviceStatus (unknownFogUuids, transaction) { const microservices = await MicroserviceManager.findAllWithStatuses({ iofogUuid: unknownFogUuids }, transaction) + + // Filter out inactive microservices that are already STOPPED - they should not be updated to UNKNOWN const microserviceStatusIds = microservices - .filter((microservice) => microservice.microserviceStatus) + .filter((microservice) => { + // Skip if no microservice status + if (!microservice.microserviceStatus) { + return false + } + // Skip if microservice is inactive and already STOPPED + if (!microservice.isActivated && microservice.microserviceStatus.status === microserviceState.STOPPED) { + return false + } + return true + }) .map((microservice) => microservice.microserviceStatus.id) - await MicroserviceStatusManager.update({ id: microserviceStatusIds }, { status: MicroserviceStates.UNKNOWN }, transaction) + + const microserviceExecStatusIds = microservices + .filter((microservice) => { + // Skip if no exec status + if (!microservice.microserviceExecStatus) { + return false + } + // Skip if microservice is inactive and already STOPPED + if (!microservice.isActivated && microservice.microserviceStatus && microservice.microserviceStatus.status === microserviceState.STOPPED) { + return false + } + return true + }) + .map((microservice) => microservice.microserviceExecStatus.id) + + await MicroserviceStatusManager.update({ id: microserviceStatusIds }, { status: microserviceState.UNKNOWN }, transaction) + await MicroserviceExecStatusManager.update({ id: microserviceExecStatusIds }, { execSesssionId: '', status: microserviceExecState.INACTIVE }, transaction) return microservices } diff --git a/src/jobs/fog-token-cleanup-job.js b/src/jobs/fog-token-cleanup-job.js new file mode 100644 index 000000000..1cce94595 --- /dev/null +++ b/src/jobs/fog-token-cleanup-job.js @@ -0,0 +1,42 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const FogUsedTokenManager = require('../data/managers/fog-used-token-manager') +const Config = require('../config') +const logger = require('../logger') + +const scheduleTime = Config.get('settings.fogExpiredTokenCleanupInterval') * 1000 + +async function run () { + try { + await cleanupExpiredTokens() + } catch (error) { + logger.error('Error during JTI cleanup:', error) + } finally { + setTimeout(run, scheduleTime) + } +} + +async function cleanupExpiredTokens () { + try { + logger.debug('Starting cleanup of expired JTIs') + const count = await FogUsedTokenManager.cleanupExpiredJtis() + logger.debug(`Cleaned up ${count} expired JTIs`) + } catch (error) { + logger.error('Error during JTI cleanup:', error) + } +} + +module.exports = { + run +} diff --git a/src/jobs/stopped-app-status-job.js b/src/jobs/stopped-app-status-job.js index 336ce609f..ea07f2bda 100644 --- a/src/jobs/stopped-app-status-job.js +++ b/src/jobs/stopped-app-status-job.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,37 +15,87 @@ const TransactionDecorator = require('../decorators/transaction-decorator') const MicroserviceManager = require('../data/managers/microservice-manager') const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') -const MicroserviceStates = require('../enums/microservice-state') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') +const { microserviceState, microserviceExecState } = require('../enums/microservice-state') + const Config = require('../config') const ApplicationManager = require('../data/managers/application-manager') +const logger = require('../logger') -const scheduleTime = Config.get('Settings:FogStatusUpdateIntervalSeconds') * 1000 +const scheduleTime = Config.get('settings.fogStatusUpdateInterval') * 1000 async function run () { try { const _updateStoppedApplicationMicroserviceStatus = TransactionDecorator.generateTransaction(updateStoppedApplicationMicroserviceStatus) + const _updateStoppedMicroserviceStatus = TransactionDecorator.generateTransaction(updateStoppedMicroserviceStatus) + + // Handle microservices from deactivated applications await _updateStoppedApplicationMicroserviceStatus() + // Handle individually deactivated microservices + await _updateStoppedMicroserviceStatus() } catch (error) { - console.error(error) + logger.error('Error during stopped application status update:', error) } finally { setTimeout(run, scheduleTime) } } async function updateStoppedApplicationMicroserviceStatus (transaction) { - const stoppedMicroservices = await ApplicationManager.findApplicationMicroservices({ isActivated: false }, transaction) + // Get all deactivated applications + const stoppedApplications = await ApplicationManager.findAllWithAttributes({ isActivated: false }, ['id'], transaction) + + if (stoppedApplications.length === 0) { + return + } + + // Get all microservices from these applications + const applicationIds = stoppedApplications.map(app => app.id) + const { Op } = require('sequelize') + const stoppedMicroservices = await MicroserviceManager.findAllWithStatuses({ applicationId: { [Op.in]: applicationIds } }, transaction) + + await _updateMicroserviceStatusStopped(stoppedMicroservices, transaction) +} + +async function updateStoppedMicroserviceStatus (transaction) { + // Get all individually deactivated microservices (where microservice isActivated = false but parent application is still active) + const { Op } = require('sequelize') + + // First get all active applications + const activeApplications = await ApplicationManager.findAllWithAttributes({ isActivated: true }, ['id'], transaction) + if (activeApplications.length === 0) { + return + } + + // Then get microservices that are individually deactivated but belong to active applications + const activeApplicationIds = activeApplications.map(app => app.id) + const stoppedMicroservices = await MicroserviceManager.findAllWithStatuses({ + isActivated: false, + applicationId: { [Op.in]: activeApplicationIds } + }, transaction) + + if (stoppedMicroservices.length === 0) { + return + } + await _updateMicroserviceStatusStopped(stoppedMicroservices, transaction) } async function _updateMicroserviceStatusStopped (stoppedMicroservices, transaction) { - const microserviceUuids = stoppedMicroservices.map((microservice) => microservice.uuid) - const microservices = await MicroserviceManager.findAllWithStatuses({ uuid: microserviceUuids }, transaction) - const microserviceStatusIds = microservices - .filter((microservice) => microservice.microserviceStatus && (microservice.microserviceStatus.status === MicroserviceStates.DELETED || - microservice.microserviceStatus.status === MicroserviceStates.DELETING)) + const microserviceStatusIds = stoppedMicroservices + .filter((microservice) => microservice.microserviceStatus && (microservice.microserviceStatus.status === microserviceState.DELETED || + microservice.microserviceStatus.status === microserviceState.DELETING)) .map((microservice) => microservice.microserviceStatus.id) - await MicroserviceStatusManager.update({ id: microserviceStatusIds }, { status: MicroserviceStates.STOPPED }, transaction) - return microservices + const microserviceExecStatusIds = stoppedMicroservices + .filter((microservice) => + microservice.microserviceStatus && + (microservice.microserviceStatus.status === microserviceState.DELETED || + microservice.microserviceStatus.status === microserviceState.DELETING) && + microservice.microserviceExecStatus + ) + .map((microservice) => microservice.microserviceExecStatus.id) + await MicroserviceStatusManager.update({ id: microserviceStatusIds }, { status: microserviceState.STOPPED }, transaction) + await MicroserviceExecStatusManager.update({ id: microserviceExecStatusIds }, { execSesssionId: '', status: microserviceExecState.INACTIVE }, transaction) + return stoppedMicroservices } module.exports = { diff --git a/src/keycloak.json b/src/keycloak.json new file mode 100644 index 000000000..cfc7f6b33 --- /dev/null +++ b/src/keycloak.json @@ -0,0 +1,14 @@ +{ + "realm": "datasance", + "realm-public-key": "", + "auth-server-url": "", + "ssl-required": "", + "resource": "pot-controller", + "bearer-only":true, + "verify-token-audience": true, + "credentials": { + "secret": "" + }, + "use-resource-role-mappings": true, + "confidential-port": 0 +} \ No newline at end of file diff --git a/src/logger/index.js b/src/logger/index.js index dd40884e0..db7461a89 100644 --- a/src/logger/index.js +++ b/src/logger/index.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,13 +11,47 @@ * */ -const fs = require('fs') -const path = require('path') const pino = require('pino') -const serializer = require('pino-std-serializers') +const path = require('path') +const fs = require('fs') const config = require('../config') +const serializer = require('pino-std-serializers') +const zlib = require('zlib') + +// Get log directory and settings from environment or config +const dirName = process.env.LOG_DIRECTORY || config.get('log.directory') + +const maxFileSize = process.env.LOG_FILE_SIZE ? parseInt(process.env.LOG_FILE_SIZE) * 1024 * 1024 * 1024 : config.get('log.fileSize') + +const maxFiles = process.env.LOG_FILE_COUNT ? parseInt(process.env.LOG_FILE_COUNT) : config.get('log.fileCount') + +// Validate required values +if (!dirName) { + throw new Error('Log directory is not configured. Please set LOG_DIRECTORY environment variable or log.directory in config.') +} +if (!maxFileSize) { + throw new Error('Log file size is not configured. Please set LOG_FILE_SIZE environment variable or log.fileSize in config.') +} +if (!maxFiles) { + throw new Error('Log file count is not configured. Please set LOG_FILE_COUNT environment variable or log.fileCount in config.') +} + +const baseFileName = 'iofog-controller' +const logFileName = `${baseFileName}.log` -const dirName = config.get('Service:LogsDirectory') +console.log('Log directory:', dirName) +console.log('Max file size:', maxFileSize) +console.log('Max files:', maxFiles) + +// Default log level from environment variable, fallback to config, then 'info' if not set +let defaultLogLevel = process.env.LOG_LEVEL || config.get('log.level') || 'info' + +// Validate log level +const validLogLevels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silly'] +if (!validLogLevels.includes(defaultLogLevel)) { + console.error(`Invalid LOG_LEVEL: ${defaultLogLevel}. Using default level: info`) + defaultLogLevel = 'info' +} const levels = { error: 100, @@ -26,6 +60,8 @@ const levels = { cliRes: 70, apiReq: 60, apiRes: 50, + service: 45, + db: 40, info: 40, verbose: 30, debug: 20, @@ -33,12 +69,18 @@ const levels = { } const defaultFormat = { - level: 'info', + level: defaultLogLevel, + timestamp: () => `,"time":"${new Date().toISOString()}"`, customLevels: levels, useOnlyCustomLevels: true, - redact: ['headers.authorization'], + redact: { + paths: ['headers.authorization', 'token', 'password', 'apiKey', 'secret', 'privateKey'], + censor: '[REDACTED]' + }, formatters: { - level: (level) => ({ level }), + level: (label) => { + return { level: label } + }, log: (log) => { if (!log.req && !log.res) { return log @@ -47,19 +89,57 @@ const defaultFormat = { let result = {} if (log.req) { + // Create base request info result = Object.assign( result, serializer.req(log.req), { params: log.req.params, query: log.req.query, - body: log.req.body + body: log.req.body, + username: log.req.kauth && log.req.kauth.grant && log.req.kauth.grant.access_token && log.req.kauth.grant.access_token.content && log.req.kauth.grant.access_token.content.preferred_username } ) + // Filter request headers + if (result.headers) { + const allowedHeaders = ['content-type', 'content-length', 'user-agent'] + const filteredHeaders = {} + for (const header of allowedHeaders) { + if (result.headers[header]) { + filteredHeaders[header] = result.headers[header] + } + } + result.headers = filteredHeaders + } } if (log.res) { - result = Object.assign(result, serializer.res(log.res)) + // Get serialized response + const serializedRes = serializer.res(log.res) + // Find status code + let statusCode = null + if (log.statusCode !== undefined) { + statusCode = log.statusCode + } else if (log.res.statusCode !== undefined) { + statusCode = log.res.statusCode + } else if (serializedRes.statusCode !== undefined) { + statusCode = serializedRes.statusCode + } + // Filter response headers + if (serializedRes.headers) { + const allowedHeaders = ['content-type', 'content-length', 'x-timestamp', 'etag'] + const filteredHeaders = {} + for (const header of allowedHeaders) { + if (serializedRes.headers[header]) { + filteredHeaders[header] = serializedRes.headers[header] + } + } + serializedRes.headers = filteredHeaders + } + // Add filtered response to result + result = Object.assign(result, serializedRes, { statusCode }) + // Remove body for privacy + delete result.body } return result @@ -67,26 +147,139 @@ const defaultFormat = { } } -const consoleLogger = pino(defaultFormat) - let fileLogger = null -try { - // Create the log directory if it does not exist - if (!fs.existsSync(dirName)) { - fs.mkdirSync(dirName) +let consoleLogger = null + +async function compressFile (sourcePath, targetPath) { + return new Promise((resolve, reject) => { + const gzip = zlib.createGzip() + const input = fs.createReadStream(sourcePath) + const output = fs.createWriteStream(targetPath) + + input.pipe(gzip).pipe(output) + + output.on('finish', () => { + fs.unlink(sourcePath, (err) => { + if (err) reject(err) + else resolve() + }) + }) + + output.on('error', reject) + }) +} + +async function rotateLogFile (isStartup = false) { + try { + const logFile = path.join(dirName, logFileName) + + // On startup, rotate if file exists and has content + // During runtime, rotate if size limit is reached + const shouldRotate = isStartup + ? (fs.existsSync(logFile) && fs.statSync(logFile).size > 0) + : (fs.existsSync(logFile) && fs.statSync(logFile).size >= maxFileSize) + + if (shouldRotate) { + console.log(isStartup ? 'Rotating log file on startup...' : 'Log file size exceeded, rotating...') + + // Find the next available compressed file number + let nextFileNumber = 1 + while (fs.existsSync(path.join(dirName, `${baseFileName}${nextFileNumber}.log.gz`))) { + nextFileNumber++ + } + + // If we've reached max files, remove the oldest compressed file + if (nextFileNumber > maxFiles) { + const oldestFile = path.join(dirName, `${baseFileName}1.log.gz`) + if (fs.existsSync(oldestFile)) { + console.log('Removing oldest compressed log file:', oldestFile) + await fs.promises.unlink(oldestFile) + } + nextFileNumber = maxFiles + } + + // Compress the current log file to the numbered target + const compressedFile = path.join(dirName, `${baseFileName}${nextFileNumber}.log.gz`) + console.log(`Compressing current log file to: ${compressedFile}`) + await compressFile(logFile, compressedFile) + + // Create new empty log file + await fs.promises.writeFile(logFile, '') + console.log('Log rotation completed') + } + } catch (err) { + console.error('Error during log rotation:', err) } +} + +function getLogger () { + if (!fileLogger) { + try { + // Create the log directory if it does not exist + if (!fs.existsSync(dirName)) { + console.log('Creating log directory:', dirName) + fs.mkdirSync(dirName, { recursive: true }) + } + + const logFile = path.join(dirName, logFileName) + console.log('Log file path:', logFile) + + // Perform initial rotation if needed + rotateLogFile(true).catch(err => { + console.error('Error during initial rotation:', err) + }) - const logDestination = pino.destination(path.resolve(dirName, 'iofog-controller.log')) - fileLogger = pino( - { + const logDestination = pino.destination({ + dest: logFile, + sync: true, + mkdir: true + }) + + // Check rotation before each write + const originalWrite = logDestination.write + logDestination.write = function (chunk) { + rotateLogFile(false).catch(err => { + console.error('Error during rotation check:', err) + }) + return originalWrite.call(this, chunk) + } + + fileLogger = pino( + { + ...defaultFormat, + level: defaultLogLevel + }, + logDestination + ) + + // Test write to ensure file is writable + fileLogger.info('Logger initialized successfully') + console.log('File logger initialized and tested') + } catch (err) { + console.error('Error initializing file logger:', err) + return getConsoleLogger() + } + } + return fileLogger +} + +function getConsoleLogger () { + if (!consoleLogger) { + consoleLogger = pino({ ...defaultFormat, - level: 'warn' - }, - logDestination) - process.on('SIGHUP', () => logDestination.reopen()) -} catch (e) {} + level: defaultLogLevel + }) + } + return consoleLogger +} + +// Initialize file logger immediately +getLogger() -module.exports = {} +module.exports = { + getLogger, + getConsoleLogger +} for (const level of Object.keys(levels)) { module.exports[level] = (...log) => { @@ -101,9 +294,9 @@ for (const level of Object.keys(levels)) { if (log[0] instanceof Error) { log = serializer.err(...log) } - consoleLogger[level](...log) + getConsoleLogger()[level](...log) if (fileLogger !== null) { - fileLogger[level](...log) + getLogger()[level](...log) } } } diff --git a/src/main.js b/src/main.js index cefe8e687..bb7ec0cf9 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,7 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -17,23 +17,25 @@ const Cli = require('./cli') const daemon = require('./daemon') const config = require('./config') const isElevated = require('is-elevated') -const request = require('request-promise') +const fetch = require('node-fetch-npm') const isHTTPS = () => { - const sslKey = config.get('Server:SslKey', '') - const devMode = config.get('Server:DevMode', false) - const sslCert = config.get('Server:SslCert', '') - const intermedKey = config.get('Server:IntermediateCert', '') - return !devMode && sslKey && sslCert && intermedKey + const sslKey = config.get('server.ssl.path.key', '') + const devMode = config.get('server.devMode', false) + const sslCert = config.get('server.ssl.path.cert', '') + return !devMode && sslKey && sslCert } -const getJSONFromURL = async (uri) => request({ - uri, - json: true -}) +const getJSONFromURL = async (uri) => { + const response = await fetch(uri) + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`) + } + return response.json() +} -const apiPort = +(config.get('Server:Port', 51121)) -const viewerPort = +(process.env.VIEWER_PORT || config.get('Viewer:Port', 80)) +const apiPort = +(config.get('server.port', 51121)) +const viewerPort = +(process.env.VIEWER_PORT || config.get('viewer.port', 8008)) const isDaemonElevated = async () => { // If it is running and you can see it, you have enough permission to move forward diff --git a/src/middlewares/event-audit-middleware.js b/src/middlewares/event-audit-middleware.js new file mode 100644 index 000000000..af1a98926 --- /dev/null +++ b/src/middlewares/event-audit-middleware.js @@ -0,0 +1,73 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const EventService = require('../services/event-service') +const config = require('../config') +const logger = require('../logger') + +/** + * Event audit middleware for HTTP requests + * Tracks all non-GET API operations (POST, PATCH, DELETE, PUT) + * CRITICAL: This middleware is fully async and non-blocking. + * Event logging failures will never affect request processing. + */ +function eventAuditMiddleware (req, res, next) { + // Only track non-GET methods + if (req.method === 'GET') { + return next() + } + + // Don't audit DELETE on events endpoint (to avoid recursion and noise) + // The DELETE endpoint controller will explicitly create an event AFTER successful deletion + if (req.method === 'DELETE' && req.path === '/api/v3/events') { + return next() + } + + // Check if auditing is enabled (reads from YAML or env var) + // Use config.get() which properly parses boolean strings from env vars + const auditEnabled = config.get('settings.eventAuditEnabled', true) + if (!auditEnabled) { + return next() + } + + // Capture request start time + const startTime = Date.now() + + // Store original end function + const originalEnd = res.end + + // Wrap response.end to capture status + res.end = function (...args) { + // Call original end first + originalEnd.apply(this, args) + + // Defer event logging to next tick - NEVER AWAIT + setImmediate(async () => { + try { + // Fire and forget - never await + EventService.createHttpEvent(req, res, startTime).catch(err => { + // Silent error handling - never throw + logger.error('Event logging failed (non-blocking):', err) + }) + } catch (error) { + // Catch any synchronous errors + logger.error('Event logging setup failed (non-blocking):', error) + // Don't throw - request already completed + } + }) + } + + next() +} + +module.exports = eventAuditMiddleware diff --git a/src/public/datasance-logo-white.png b/src/public/datasance-logo-white.png new file mode 100644 index 000000000..759c70e2a Binary files /dev/null and b/src/public/datasance-logo-white.png differ diff --git a/src/routes/agent.js b/src/routes/agent.js index f79ae274d..8aca966f4 100644 --- a/src/routes/agent.js +++ b/src/routes/agent.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,7 +14,7 @@ const constants = require('../helpers/constants') const AgentController = require('../controllers/agent-controller') const ResponseDecorator = require('../decorators/response-decorator') - +const WebSocketServer = require('../websocket/server') const Errors = require('../helpers/errors') const logger = require('../logger') @@ -48,7 +48,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -77,7 +77,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -101,7 +101,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -130,7 +130,36 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) + } + }, + { + method: 'patch', + path: '/api/v3/agent/config/gps', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + const updateAgentGpsEndPoint = ResponseDecorator.handleErrors(AgentController.updateAgentGpsEndPoint, + successCode, errorCodes) + const responseObject = await updateAgentGpsEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -159,7 +188,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -188,7 +217,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -217,7 +246,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -242,7 +271,32 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) + } + }, + { + method: 'get', + path: '/api/v3/agent/volumeMounts', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + const getAgentLinkedVolumeMountsEndpoint = ResponseDecorator.handleErrors(AgentController.getAgentLinkedVolumeMountsEndpoint, + successCode, errorCodes) + const responseObject = await getAgentLinkedVolumeMountsEndpoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -267,7 +321,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -296,7 +350,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -321,7 +375,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -350,7 +404,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -379,7 +433,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -412,7 +466,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -441,7 +495,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -470,7 +524,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -499,7 +553,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -524,7 +578,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -553,7 +607,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -582,7 +636,79 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) + } + }, + { + method: 'get', + path: '/api/v3/agent/cert', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + const getControllerCAEndPoint = ResponseDecorator.handleErrors(AgentController.getControllerCAEndPoint, + successCode, errorCodes) + const responseObject = await getControllerCAEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: res, responseObject: responseObject }) + } + }, + { + method: 'ws', + path: '/api/v3/agent/exec/:microserviceUuid', + middleware: async (ws, req) => { + logger.apiReq(req) + try { + const token = req.headers.authorization + if (!token) { + logger.error('WebSocket connection failed: Missing authentication token') + try { + ws.close(1008, 'Missing authentication token') + } catch (error) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: error.message, + originalError: 'Missing authentication token' + })) + } + return + } + + // Initialize WebSocket connection for agent + const wsServer = WebSocketServer.getInstance() + await wsServer.handleConnection(ws, req) + } catch (error) { + logger.error('Error in agent WebSocket connection:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url, + microserviceUuid: req.params.microserviceUuid + })) + try { + if (ws.readyState === ws.OPEN) { + ws.close(1008, error.message || 'Authentication failed') + } + } catch (closeError) { + logger.error('Error closing agent WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } } } ] diff --git a/src/routes/application.js b/src/routes/application.js index f5cb3e663..3feb7c945 100644 --- a/src/routes/application.js +++ b/src/routes/application.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const ApplicationController = require('../controllers/application-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -32,13 +33,45 @@ module.exports = [ ] const getApplicationsByUserEndPoint = ResponseDecorator.handleErrors(ApplicationController.getApplicationsByUserEndPoint, successCode, errorCodes) - const responseObject = await getApplicationsByUserEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await getApplicationsByUserEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/application/system', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + const getApplicationsBySystemEndPoint = ResponseDecorator.handleErrors(ApplicationController.getApplicationsBySystemEndPoint, successCode, errorCodes) + + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE'])(req, res, async () => { + const responseObject = await getApplicationsBySystemEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -61,13 +94,17 @@ module.exports = [ ] const createApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.createApplicationEndPoint, successCode, errorCodes) - const responseObject = await createApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await createApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -90,13 +127,17 @@ module.exports = [ ] const createApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.createApplicationYAMLEndPoint, successCode, errorCodes) - const responseObject = await createApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await createApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -118,14 +159,51 @@ module.exports = [ ] const getApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.getApplicationEndPoint, successCode, errorCodes) - const responseObject = await getApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await getApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + return null + }) + } + }, + { + method: 'get', + path: '/api/v3/application/system/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + const getSystemApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.getSystemApplicationEndPoint, successCode, errorCodes) + + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await getSystemApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) - return null + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + return null + }) } }, { @@ -152,13 +230,17 @@ module.exports = [ ] const updateApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.patchApplicationEndPoint, successCode, errorCodes) - const responseObject = await updateApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -185,13 +267,17 @@ module.exports = [ ] const updateApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.updateApplicationYAMLEndPoint, successCode, errorCodes) - const responseObject = await updateApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -218,13 +304,17 @@ module.exports = [ ] const updateApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.updateApplicationEndPoint, successCode, errorCodes) - const responseObject = await updateApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -246,13 +336,49 @@ module.exports = [ ] const deleteApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.deleteApplicationEndPoint, successCode, errorCodes) - const responseObject = await deleteApplicationEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await deleteApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/application/system/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + const deleteSystemApplicationEndPoint = ResponseDecorator.handleErrors(ApplicationController.deleteSystemApplicationEndPoint, successCode, errorCodes) + + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE'])(req, res, async () => { + const responseObject = await deleteSystemApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/applicationTemplate.js b/src/routes/applicationTemplate.js index 432a20885..f0bae7b23 100644 --- a/src/routes/applicationTemplate.js +++ b/src/routes/applicationTemplate.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const ApplicationTemplateController = require('../controllers/application-templa const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -32,13 +33,17 @@ module.exports = [ ] const getApplicationTemplatesByUserEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.getApplicationTemplatesByUserEndPoint, successCode, errorCodes) - const responseObject = await getApplicationTemplatesByUserEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await getApplicationTemplatesByUserEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -60,13 +65,17 @@ module.exports = [ ] const createApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.createApplicationTemplateEndPoint, successCode, errorCodes) - const responseObject = await createApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await createApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -89,13 +98,17 @@ module.exports = [ ] const createApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.createApplicationTemplateYAMLEndPoint, successCode, errorCodes) - const responseObject = await createApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await createApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -117,13 +130,17 @@ module.exports = [ ] const getApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.getApplicationTemplateEndPoint, successCode, errorCodes) - const responseObject = await getApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await getApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -149,13 +166,17 @@ module.exports = [ ] const patchApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.patchApplicationTemplateEndPoint, successCode, errorCodes) - const responseObject = await patchApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await patchApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -182,13 +203,17 @@ module.exports = [ ] const updateApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.updateApplicationTemplateYAMLEndPoint, successCode, errorCodes) - const responseObject = await updateApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -214,13 +239,17 @@ module.exports = [ ] const updateApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.updateApplicationTemplateEndPoint, successCode, errorCodes) - const responseObject = await updateApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -242,13 +271,17 @@ module.exports = [ ] const deleteApplicationTemplateEndPoint = ResponseDecorator.handleErrors(ApplicationTemplateController.deleteApplicationTemplateEndPoint, successCode, errorCodes) - const responseObject = await deleteApplicationTemplateEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await deleteApplicationTemplateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/capabilities.js b/src/routes/capabilities.js index f7317e0df..f8b533e70 100644 --- a/src/routes/capabilities.js +++ b/src/routes/capabilities.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,6 +11,7 @@ * */ const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -18,7 +19,11 @@ module.exports = [ path: '/api/v3/capabilities/edgeResources', middleware: async (req, res) => { logger.apiReq(req) - res.sendStatus(204) + + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + res.sendStatus(204) + }) } }, { @@ -26,7 +31,11 @@ module.exports = [ path: '/api/v3/capabilities/applicationTemplates', middleware: async (req, res) => { logger.apiReq(req) - res.sendStatus(204) + + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + res.sendStatus(204) + }) } } ] diff --git a/src/routes/catalog.js b/src/routes/catalog.js index d3b8d3e3a..66580b17a 100644 --- a/src/routes/catalog.js +++ b/src/routes/catalog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const CatalogController = require('../controllers/catalog-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -36,13 +37,17 @@ module.exports = [ successCode, errorCodes ) - const responseObject = await listCatalogItemsEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await listCatalogItemsEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -73,13 +78,17 @@ module.exports = [ successCode, errorCodes ) - const responseObject = await createCatalogItemEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await createCatalogItemEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -105,13 +114,17 @@ module.exports = [ successCode, errorCodes ) - const responseObject = await listCatalogItemEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const responseObject = await listCatalogItemEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -146,13 +159,17 @@ module.exports = [ successCode, errorCodes ) - const responseObject = await updateCatalogItemEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE and Developer + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await updateCatalogItemEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -178,13 +195,18 @@ module.exports = [ successCode, errorCodes ) - const responseObject = await deleteCatalogItemEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const responseObject = await deleteCatalogItemEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req, user: user, res: res, responseObject: responseObject }) + }) } } + ] diff --git a/src/routes/certificate.js b/src/routes/certificate.js new file mode 100644 index 000000000..2f30cfbcd --- /dev/null +++ b/src/routes/certificate.js @@ -0,0 +1,356 @@ +const constants = require('../helpers/constants') +const CertificateController = require('../controllers/certificate-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'post', + path: '/api/v3/certificates/ca', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createCAEndpoint = ResponseDecorator.handleErrors(CertificateController.createCAEndpoint, successCode, errorCodes) + const responseObject = await createCAEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/certificates/ca/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getCAEndpoint = ResponseDecorator.handleErrors(CertificateController.getCAEndpoint, successCode, errorCodes) + const responseObject = await getCAEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/certificates/ca', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listCAEndpoint = ResponseDecorator.handleErrors(CertificateController.listCAEndpoint, successCode, errorCodes) + const responseObject = await listCAEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/certificates/ca/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteCAEndpoint = ResponseDecorator.handleErrors(CertificateController.deleteCAEndpoint, successCode, errorCodes) + const responseObject = await deleteCAEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/certificates', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createCertificateEndpoint = ResponseDecorator.handleErrors(CertificateController.createCertificateEndpoint, successCode, errorCodes) + const responseObject = await createCertificateEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/certificates/expiring', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listExpiringCertificatesEndpoint = ResponseDecorator.handleErrors(CertificateController.listExpiringCertificatesEndpoint, successCode, errorCodes) + const responseObject = await listExpiringCertificatesEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/certificates/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getCertificateEndpoint = ResponseDecorator.handleErrors(CertificateController.getCertificateEndpoint, successCode, errorCodes) + const responseObject = await getCertificateEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/certificates', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listCertificatesEndpoint = ResponseDecorator.handleErrors(CertificateController.listCertificatesEndpoint, successCode, errorCodes) + const responseObject = await listCertificatesEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/certificates/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteCertificateEndpoint = ResponseDecorator.handleErrors(CertificateController.deleteCertificateEndpoint, successCode, errorCodes) + const responseObject = await deleteCertificateEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/certificates/:name/renew', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const renewCertificateEndpoint = ResponseDecorator.handleErrors(CertificateController.renewCertificateEndpoint, successCode, errorCodes) + const responseObject = await renewCertificateEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/certificates/yaml', + fileInput: 'certificate', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createCertificateFromYamlEndpoint = ResponseDecorator.handleErrors(CertificateController.createCertificateFromYamlEndpoint, successCode, errorCodes) + const responseObject = await createCertificateFromYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/routes/config.js b/src/routes/config.js index a1a605c1c..60d6b347a 100644 --- a/src/routes/config.js +++ b/src/routes/config.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const ConfigController = require('../controllers/config-controller') const ResponseDecorator = require('../decorators/response-decorator') const logger = require('../logger') const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -30,14 +31,18 @@ module.exports = [ errors: [Errors.AuthenticationError] } ] - const getConfigEndpoint = ResponseDecorator.handleErrors(ConfigController.listConfigEndpoint, successCode, errorCodes) - const responseObject = await getConfigEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getConfigEndpoint = ResponseDecorator.handleErrors(ConfigController.listConfigEndpoint, successCode, errorCodes) + const responseObject = await getConfigEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -58,14 +63,17 @@ module.exports = [ } ] - const getConfigEndpoint = ResponseDecorator.handleErrors(ConfigController.getConfigEndpoint, successCode, errorCodes) - const responseObject = await getConfigEndpoint(req) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getConfigEndpoint = ResponseDecorator.handleErrors(ConfigController.getConfigEndpoint, successCode, errorCodes) + const responseObject = await getConfigEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -86,14 +94,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const upsertConfigElementEndpoint = ResponseDecorator.handleErrors(ConfigController.upsertConfigElementEndpoint, successCode, errorCodes) - const responseObject = await upsertConfigElementEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE'])(req, res, async () => { + const upsertConfigElementEndpoint = ResponseDecorator.handleErrors(ConfigController.upsertConfigElementEndpoint, successCode, errorCodes) + const responseObject = await upsertConfigElementEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/configMap.js b/src/routes/configMap.js new file mode 100644 index 000000000..4d6484196 --- /dev/null +++ b/src/routes/configMap.js @@ -0,0 +1,246 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const constants = require('../helpers/constants') +const ConfigMapController = require('../controllers/config-map-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'post', + path: '/api/v3/configmaps', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createConfigMapEndpoint = ResponseDecorator.handleErrors(ConfigMapController.createConfigMapEndpoint, successCode, errorCodes) + const responseObject = await createConfigMapEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/configmaps/yaml', + fileInput: 'configMap', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createConfigMapFromYamlEndpoint = ResponseDecorator.handleErrors(ConfigMapController.createConfigMapFromYamlEndpoint, successCode, errorCodes) + const responseObject = await createConfigMapFromYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/configmaps/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateConfigMapEndpoint = ResponseDecorator.handleErrors(ConfigMapController.updateConfigMapEndpoint, successCode, errorCodes) + const responseObject = await updateConfigMapEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/configmaps/yaml/:name', + fileInput: 'configMap', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateConfigMapFromYamlEndpoint = ResponseDecorator.handleErrors(ConfigMapController.updateConfigMapFromYamlEndpoint, successCode, errorCodes) + const responseObject = await updateConfigMapFromYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/configmaps/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getConfigMapEndpoint = ResponseDecorator.handleErrors(ConfigMapController.getConfigMapEndpoint, successCode, errorCodes) + const responseObject = await getConfigMapEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/configmaps', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listConfigMapsEndpoint = ResponseDecorator.handleErrors(ConfigMapController.listConfigMapsEndpoint, successCode, errorCodes) + const responseObject = await listConfigMapsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/configmaps/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteConfigMapEndpoint = ResponseDecorator.handleErrors(ConfigMapController.deleteConfigMapEndpoint, successCode, errorCodes) + const responseObject = await deleteConfigMapEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/routes/controller.js b/src/routes/controller.js index ad980bb3a..36e9fb8f7 100644 --- a/src/routes/controller.js +++ b/src/routes/controller.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -31,25 +31,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/email-activation', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [] - const emailActivationEndPoint = ResponseDecorator.handleErrors(Controller.emailActivationEndPoint, successCode, errorCodes) - const responseObject = await emailActivationEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, { @@ -67,7 +49,7 @@ module.exports = [ .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, res: res, responseObject: responseObject }) } } ] diff --git a/src/routes/diagnostics.js b/src/routes/diagnostics.js index 65fa6a1a5..49e83a33e 100644 --- a/src/routes/diagnostics.js +++ b/src/routes/diagnostics.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -16,6 +16,7 @@ const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const fs = require('fs') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -36,18 +37,21 @@ module.exports = [ } ] - const createMicroserviceImageSnapshotEndPoint = ResponseDecorator.handleErrors( - DiagnosticController.createMicroserviceImageSnapshotEndPoint, - successCode, - errorCodes - ) - const responseObject = await createMicroserviceImageSnapshotEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroserviceImageSnapshotEndPoint = ResponseDecorator.handleErrors( + DiagnosticController.createMicroserviceImageSnapshotEndPoint, + successCode, + errorCodes + ) + const responseObject = await createMicroserviceImageSnapshotEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -68,26 +72,30 @@ module.exports = [ } ] - const getMicroserviceImageSnapshotEndPoint = ResponseDecorator.handleErrors( - DiagnosticController.getMicroserviceImageSnapshotEndPoint, - successCode, - errorCodes - ) - const responseObject = await getMicroserviceImageSnapshotEndPoint(req) - if (responseObject.code !== successCode) { - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } else { - res.writeHead(successCode, { - 'Content-Length': responseObject.body['Content-Length'], - 'Content-Type': responseObject.body['Content-Type'], - 'Content-Disposition': 'attachment; filename=' + responseObject.body.fileName - }) - fs.createReadStream(responseObject.body.filePath).pipe(res) - } + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const getMicroserviceImageSnapshotEndPoint = ResponseDecorator.handleErrors( + DiagnosticController.getMicroserviceImageSnapshotEndPoint, + successCode, + errorCodes + ) + const responseObject = await getMicroserviceImageSnapshotEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + if (responseObject.code !== successCode) { + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + } else { + res.writeHead(successCode, { + 'Content-Length': responseObject.body['Content-Length'], + 'Content-Type': responseObject.body['Content-Type'], + 'Content-Disposition': 'attachment; filename=' + responseObject.body.fileName + }) + fs.createReadStream(responseObject.body.filePath).pipe(res) + } + }) } }, { @@ -112,18 +120,21 @@ module.exports = [ } ] - const changeMicroserviceStraceStateEndPoint = ResponseDecorator.handleErrors( - DiagnosticController.changeMicroserviceStraceStateEndPoint, - successCode, - errorCodes - ) - const responseObject = await changeMicroserviceStraceStateEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const changeMicroserviceStraceStateEndPoint = ResponseDecorator.handleErrors( + DiagnosticController.changeMicroserviceStraceStateEndPoint, + successCode, + errorCodes + ) + const responseObject = await changeMicroserviceStraceStateEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -144,18 +155,21 @@ module.exports = [ } ] - const getMicroserviceStraceDataEndPoint = ResponseDecorator.handleErrors( - DiagnosticController.getMicroserviceStraceDataEndPoint, - successCode, - errorCodes - ) - const responseObject = await getMicroserviceStraceDataEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const getMicroserviceStraceDataEndPoint = ResponseDecorator.handleErrors( + DiagnosticController.getMicroserviceStraceDataEndPoint, + successCode, + errorCodes + ) + const responseObject = await getMicroserviceStraceDataEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -184,18 +198,21 @@ module.exports = [ } ] - const postMicroserviceStraceDataToFtpEndPoint = ResponseDecorator.handleErrors( - DiagnosticController.postMicroserviceStraceDataToFtpEndPoint, - successCode, - errorCodes - ) - const responseObject = await postMicroserviceStraceDataToFtpEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const postMicroserviceStraceDataToFtpEndPoint = ResponseDecorator.handleErrors( + DiagnosticController.postMicroserviceStraceDataToFtpEndPoint, + successCode, + errorCodes + ) + const responseObject = await postMicroserviceStraceDataToFtpEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/edgeResource.js b/src/routes/edgeResource.js index f11037d84..0f55c3e38 100644 --- a/src/routes/edgeResource.js +++ b/src/routes/edgeResource.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const EdgeResourceController = require('../controllers/edge-resource-controller' const ResponseDecorator = require('../decorators/response-decorator') const logger = require('../logger') const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -30,14 +31,18 @@ module.exports = [ errors: [Errors.AuthenticationError] } ] - const getEdgeResourcesEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.listEdgeResourcesEndpoint, successCode, errorCodes) - const responseObject = await getEdgeResourcesEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getEdgeResourcesEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.listEdgeResourcesEndpoint, successCode, errorCodes) + const responseObject = await getEdgeResourcesEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -58,14 +63,17 @@ module.exports = [ } ] - const getEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.getEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await getEdgeResourceEndpoint(req) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.getEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await getEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -86,14 +94,17 @@ module.exports = [ } ] - const getEdgeResourceAllVersionsEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.getEdgeResourceAllVersionsEndpoint, successCode, errorCodes) - const responseObject = await getEdgeResourceAllVersionsEndpoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getEdgeResourceAllVersionsEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.getEdgeResourceAllVersionsEndpoint, successCode, errorCodes) + const responseObject = await getEdgeResourceAllVersionsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -118,14 +129,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const updateEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.updateEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await updateEdgeResourceEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const updateEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.updateEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await updateEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -149,14 +164,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const deleteEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.deleteEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await deleteEdgeResourceEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.deleteEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await deleteEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -177,14 +196,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const createEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.createEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await createEdgeResourceEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const createEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.createEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await createEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -204,14 +227,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const linkEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.linkEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await linkEdgeResourceEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const linkEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.linkEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await linkEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -231,14 +258,18 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const unlinkEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.unlinkEdgeResourceEndpoint, successCode, errorCodes) - const responseObject = await unlinkEdgeResourceEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const unlinkEdgeResourceEndpoint = ResponseDecorator.handleErrors(EdgeResourceController.unlinkEdgeResourceEndpoint, successCode, errorCodes) + const responseObject = await unlinkEdgeResourceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/event.js b/src/routes/event.js new file mode 100644 index 000000000..d95886746 --- /dev/null +++ b/src/routes/event.js @@ -0,0 +1,88 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const constants = require('../helpers/constants') +const EventController = require('../controllers/event-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'get', + path: '/api/v3/events', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Protected with Keycloak (SRE role only) + await keycloak.protect(['SRE'])(req, res, async () => { + const listEventsEndpoint = ResponseDecorator.handleErrors(EventController.listEventsEndpoint, successCode, errorCodes) + const responseObject = await listEventsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/events', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_FORBIDDEN, + errors: [Errors.AuthorizationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Protected with Keycloak (SRE role only) + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteOldEventsEndpoint = ResponseDecorator.handleErrors(EventController.deleteOldEventsEndpoint, successCode, errorCodes) + const responseObject = await deleteOldEventsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/routes/flow.js b/src/routes/flow.js index 0b169d4b4..e9397c047 100644 --- a/src/routes/flow.js +++ b/src/routes/flow.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const FlowController = require('../controllers/application-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -31,14 +32,17 @@ module.exports = [ } ] - const getFlowsByUserEndPoint = ResponseDecorator.handleErrors(FlowController.getApplicationsByUserEndPoint, successCode, errorCodes) - const responseObject = await getFlowsByUserEndPoint(req) - - res - .status(responseObject.code) - .send({ flows: responseObject.body.applications }) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getFlowsByUserEndPoint = ResponseDecorator.handleErrors(FlowController.getApplicationsByUserEndPoint, successCode, errorCodes) + const responseObject = await getFlowsByUserEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send({ flows: responseObject.body.applications }) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -59,14 +63,17 @@ module.exports = [ } ] - const createFlowEndPoint = ResponseDecorator.handleErrors(FlowController.createApplicationEndPoint, successCode, errorCodes) - const responseObject = await createFlowEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createFlowEndPoint = ResponseDecorator.handleErrors(FlowController.createApplicationEndPoint, successCode, errorCodes) + const responseObject = await createFlowEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -87,14 +94,17 @@ module.exports = [ } ] - const getFlowEndPoint = ResponseDecorator.handleErrors(FlowController.getApplicationByIdEndPoint, successCode, errorCodes) - const responseObject = await getFlowEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getFlowEndPoint = ResponseDecorator.handleErrors(FlowController.getApplicationByIdEndPoint, successCode, errorCodes) + const responseObject = await getFlowEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -119,14 +129,17 @@ module.exports = [ } ] - const updateFlowEndPoint = ResponseDecorator.handleErrors(FlowController.patchApplicationByIdEndPoint, successCode, errorCodes) - const responseObject = await updateFlowEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateFlowEndPoint = ResponseDecorator.handleErrors(FlowController.patchApplicationByIdEndPoint, successCode, errorCodes) + const responseObject = await updateFlowEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -147,14 +160,17 @@ module.exports = [ } ] - const deleteFlowEndPoint = ResponseDecorator.handleErrors(FlowController.deleteApplicationByIdEndPoint, successCode, errorCodes) - const responseObject = await deleteFlowEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteFlowEndPoint = ResponseDecorator.handleErrors(FlowController.deleteApplicationByIdEndPoint, successCode, errorCodes) + const responseObject = await deleteFlowEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/iofog.js b/src/routes/iofog.js index 6a6ddaffb..aae10cab6 100644 --- a/src/routes/iofog.js +++ b/src/routes/iofog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const FogController = require('../controllers/iofog-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -35,14 +36,17 @@ module.exports = [ } ] - const getFogList = ResponseDecorator.handleErrors(FogController.getFogListEndPoint, successCode, errCodes) - const responseObject = await getFogList(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Add keycloak.protect() middleware to protect the route for SRE, Developer, and Viewer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getFogList = ResponseDecorator.handleErrors(FogController.getFogListEndPoint, successCode, errCodes) + const responseObject = await getFogList(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -64,14 +68,17 @@ module.exports = [ } ] - const createFog = ResponseDecorator.handleErrors(FogController.createFogEndPoint, successCode, errCodes) - const responseObject = await createFog(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protect the route with SRE access control + await keycloak.protect('SRE')(req, res, async () => { + const createFog = ResponseDecorator.handleErrors(FogController.createFogEndPoint, successCode, errCodes) + const responseObject = await createFog(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -97,14 +104,17 @@ module.exports = [ } ] - const updateFog = ResponseDecorator.handleErrors(FogController.updateFogEndPoint, successCode, errCodes) - const responseObject = await updateFog(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protect the route with SRE access control + await keycloak.protect('SRE')(req, res, async () => { + const updateFog = ResponseDecorator.handleErrors(FogController.updateFogEndPoint, successCode, errCodes) + const responseObject = await updateFog(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -125,14 +135,17 @@ module.exports = [ } ] - const deleteFog = ResponseDecorator.handleErrors(FogController.deleteFogEndPoint, successCode, errCodes) - const responseObject = await deleteFog(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protect the route with SRE access control + await keycloak.protect('SRE')(req, res, async () => { + const deleteFog = ResponseDecorator.handleErrors(FogController.deleteFogEndPoint, successCode, errCodes) + const responseObject = await deleteFog(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -153,14 +166,17 @@ module.exports = [ } ] - const getFog = ResponseDecorator.handleErrors(FogController.getFogEndPoint, successCode, errCodes) - const responseObject = await getFog(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protect the route with SRE, Developer, and Viewer access control + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getFog = ResponseDecorator.handleErrors(FogController.getFogEndPoint, successCode, errCodes) + const responseObject = await getFog(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -181,15 +197,17 @@ module.exports = [ } ] - const generateFogProvisioningKey = ResponseDecorator.handleErrors(FogController.generateProvisioningKeyEndPoint, - successCode, errCodes) - const responseObject = await generateFogProvisioningKey(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const generateFogProvisioningKey = ResponseDecorator.handleErrors(FogController.generateProvisioningKeyEndPoint, + successCode, errCodes) + const responseObject = await generateFogProvisioningKey(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -214,15 +232,17 @@ module.exports = [ } ] - const setFogVersionCommand = ResponseDecorator.handleErrors(FogController.setFogVersionCommandEndPoint, - successCode, errCodes) - const responseObject = await setFogVersionCommand(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const setFogVersionCommand = ResponseDecorator.handleErrors(FogController.setFogVersionCommandEndPoint, + successCode, errCodes) + const responseObject = await setFogVersionCommand(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -247,15 +267,17 @@ module.exports = [ } ] - const setFogRebootCommand = ResponseDecorator.handleErrors(FogController.setFogRebootCommandEndPoint, - successCode, errCodes) - const responseObject = await setFogRebootCommand(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const setFogRebootCommand = ResponseDecorator.handleErrors(FogController.setFogRebootCommandEndPoint, + successCode, errCodes) + const responseObject = await setFogRebootCommand(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -276,15 +298,17 @@ module.exports = [ } ] - const getHalHardwareInfo = ResponseDecorator.handleErrors(FogController.getHalHardwareInfoEndPoint, - successCode, errCodes) - const responseObject = await getHalHardwareInfo(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getHalHardwareInfo = ResponseDecorator.handleErrors(FogController.getHalHardwareInfoEndPoint, + successCode, errCodes) + const responseObject = await getHalHardwareInfo(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -305,14 +329,16 @@ module.exports = [ } ] - const getHalUsbInfo = ResponseDecorator.handleErrors(FogController.getHalUsbInfoEndPoint, successCode, errCodes) - const responseObject = await getHalUsbInfo(req) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getHalUsbInfo = ResponseDecorator.handleErrors(FogController.getHalUsbInfoEndPoint, successCode, errCodes) + const responseObject = await getHalUsbInfo(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -337,15 +363,87 @@ module.exports = [ } ] - const setFogPruneCommand = ResponseDecorator.handleErrors(FogController.setFogPruneCommandEndPoint, - successCode, errCodes) - const responseObject = await setFogPruneCommand(req) + await keycloak.protect(['SRE'])(req, res, async () => { + const setFogPruneCommand = ResponseDecorator.handleErrors(FogController.setFogPruneCommandEndPoint, + successCode, errCodes) + const responseObject = await setFogPruneCommand(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/iofog/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_NO_CONTENT + const errCodes = [ + { + code: 400, + errors: [Errors.ValidationError] + }, + { + code: 401, + errors: [Errors.AuthenticationError] + }, + { + code: 404, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const enableNodeExecEndPoint = ResponseDecorator.handleErrors(FogController.enableNodeExecEndPoint, + successCode, errCodes) + const responseObject = await enableNodeExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/iofog/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errCodes = [ + { + code: 400, + errors: [Errors.ValidationError] + }, + { + code: 401, + errors: [Errors.AuthenticationError] + }, + { + code: 404, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const disableNodeExecEndPoint = ResponseDecorator.handleErrors(FogController.disableNodeExecEndPoint, + successCode, errCodes) + const responseObject = await disableNodeExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/kubelet.js b/src/routes/kubelet.js deleted file mode 100644 index 1666f7091..000000000 --- a/src/routes/kubelet.js +++ /dev/null @@ -1,434 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const constants = require('../helpers/constants') -const Errors = require('../helpers/errors') -const KubeletController = require('../controllers/kubelet-controller') -const logger = require('../logger') -const ResponseDecorator = require('../decorators/response-decorator') - -module.exports = [ - { - method: 'post', - path: '/api/v3/k8s/createPod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletCreatePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletCreatePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletCreatePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'put', - path: '/api/v3/k8s/updatePod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletUpdatePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletUpdatePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletUpdatePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'delete', - path: '/api/v3/k8s/deletePod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletDeletePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletDeletePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletDeletePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/getPod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetPodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/getContainerLogs', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetContainerLogsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetContainerLogsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetContainerLogsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/getPodStatus', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetPodStatusEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodStatusEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodStatusEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/getPods', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetPodsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/capacity', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetCapacityEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetCapacityEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetCapacityEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/allocatable', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetAllocatableEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetAllocatableEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetAllocatableEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/nodeConditions', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetNodeConditionsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetNodeConditionsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetNodeConditionsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/nodeAddresses', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const kubeletGetNodeAddressesEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetNodeAddressesEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetNodeAddressesEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/vk-token', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.AuthenticationError] - } - ] - - const kubeletGetVkTokenEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetVkTokenEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetVkTokenEndPoint() - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/k8s/scheduler-token', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.AuthenticationError] - } - ] - - const kubeletGetSchedulerTokenEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetSchedulerTokenEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetSchedulerTokenEndPoint() - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - } -] diff --git a/src/routes/microservices.js b/src/routes/microservices.js index e817e2108..8927db9bb 100644 --- a/src/routes/microservices.js +++ b/src/routes/microservices.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,11 +15,13 @@ const MicroservicesController = require('../controllers/microservices-controller const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() +const WebSocketServer = require('../websocket/server') module.exports = [ { method: 'get', - path: '/api/v3/microservices/public-ports', + path: '/api/v3/microservices/', middleware: async (req, res) => { logger.apiReq(req) @@ -31,23 +33,22 @@ module.exports = [ } ] - const listAllPublicPortsEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.listAllPublicPortsEndPoint, - successCode, - errorCodes - ) - const responseObject = await listAllPublicPortsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getMicroservicesByApplicationEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getMicroservicesByApplicationEndPoint, + successCode, errorCodes) + const responseObject = await getMicroservicesByApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { method: 'get', - path: '/api/v3/microservices/', + path: '/api/v3/microservices/system', middleware: async (req, res) => { logger.apiReq(req) @@ -59,15 +60,17 @@ module.exports = [ } ] - const getMicroservicesByApplicationEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getMicroservicesByApplicationEndPoint, - successCode, errorCodes) - const responseObject = await getMicroservicesByApplicationEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getSystemMicroservicesByApplicationEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getSystemMicroservicesByApplicationEndPoint, + successCode, errorCodes) + const responseObject = await getSystemMicroservicesByApplicationEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -89,15 +92,17 @@ module.exports = [ } ] - const createMicroservicesOnFogEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.createMicroserviceOnFogEndPoint, successCode, errorCodes) - const responseObject = await createMicroservicesOnFogEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroservicesOnFogEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroserviceOnFogEndPoint, successCode, errorCodes) + const responseObject = await createMicroservicesOnFogEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -120,20 +125,53 @@ module.exports = [ } ] - const createMicroservicesYAMLEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.createMicroserviceYAMLEndPoint, successCode, errorCodes) - const responseObject = await createMicroservicesYAMLEndPoint(req) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroservicesYAMLEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroserviceYAMLEndPoint, successCode, errorCodes) + const responseObject = await createMicroservicesYAMLEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/microservices/:uuid', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await getMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { method: 'get', - path: '/api/v3/microservices/:uuid', + path: '/api/v3/microservices/system/:uuid', middleware: async (req, res) => { logger.apiReq(req) @@ -149,18 +187,81 @@ module.exports = [ } ] - const getMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getMicroserviceEndPoint, - successCode, errorCodes) - const responseObject = await getMicroserviceEndPoint(req) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getSystemMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getSystemMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await getSystemMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/microservices/pub/:tag', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listMicroserviceByPubTagEndPoint = ResponseDecorator.handleErrors(MicroservicesController.listMicroserviceByPubTagEndPoint, + successCode, errorCodes) + const responseObject = await listMicroserviceByPubTagEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, + { + method: 'get', + path: '/api/v3/microservices/sub/:tag', + middleware: async (req, res) => { + logger.apiReq(req) + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listMicroserviceBySubTagEndPoint = ResponseDecorator.handleErrors(MicroservicesController.listMicroserviceBySubTagEndPoint, + successCode, errorCodes) + const responseObject = await listMicroserviceBySubTagEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, { method: 'patch', path: '/api/v3/microservices/:uuid', @@ -184,23 +285,23 @@ module.exports = [ } ] - const updateMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateMicroserviceEndPoint, - successCode, errorCodes) - const responseObject = await updateMicroserviceEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await updateMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, - { method: 'patch', - path: '/api/v3/microservices/yaml/:uuid', + path: '/api/v3/microservices/system/:uuid', supportSubstitution: true, - fileInput: 'microservice', middleware: async (req, res) => { logger.apiReq(req) @@ -220,25 +321,66 @@ module.exports = [ } ] - const updateMicroserviceYAMLEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateMicroserviceYAMLEndPoint, - successCode, errorCodes) - const responseObject = await updateMicroserviceYAMLEndPoint(req) + await keycloak.protect(['SRE'])(req, res, async () => { + const updateSystemMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateSystemMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await updateSystemMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/:uuid/rebuild', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const rebuildMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.rebuildMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await rebuildMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'delete', - path: '/api/v3/microservices/:uuid', + method: 'patch', + path: '/api/v3/microservices/system/:uuid/rebuild', middleware: async (req, res) => { logger.apiReq(req) const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, { code: constants.HTTP_CODE_UNAUTHORIZED, errors: [Errors.AuthenticationError] @@ -249,24 +391,65 @@ module.exports = [ } ] - const deleteMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.deleteMicroserviceEndPoint, - successCode, errorCodes) - const responseObject = await deleteMicroserviceEndPoint(req) + await keycloak.protect(['SRE'])(req, res, async () => { + const rebuildSystemMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.rebuildSystemMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await rebuildSystemMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/yaml/:uuid', + supportSubstitution: true, + fileInput: 'microservice', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateMicroserviceYAMLEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateMicroserviceYAMLEndPoint, + successCode, errorCodes) + const responseObject = await updateMicroserviceYAMLEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'post', - path: '/api/v3/microservices/:uuid/routes/:receiverUuid', + method: 'patch', + path: '/api/v3/microservices/system/yaml/:uuid', + supportSubstitution: true, + fileInput: 'microservice', middleware: async (req, res) => { logger.apiReq(req) - const successCode = constants.HTTP_CODE_CREATED + const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ { code: constants.HTTP_CODE_BAD_REQUEST, @@ -282,20 +465,57 @@ module.exports = [ } ] - const createMicroserviceRouteEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.createMicroserviceRouteEndPoint, successCode, errorCodes) - const responseObject = await createMicroserviceRouteEndPoint(req) + await keycloak.protect(['SRE'])(req, res, async () => { + const updateSystemMicroserviceYAMLEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateSystemMicroserviceYAMLEndPoint, + successCode, errorCodes) + const responseObject = await updateSystemMicroserviceYAMLEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/microservices/:uuid/config', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await getMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'delete', - path: '/api/v3/microservices/:uuid/routes/:receiverUuid', + method: 'patch', + path: '/api/v3/microservices/:uuid/config', middleware: async (req, res) => { logger.apiReq(req) @@ -315,24 +535,26 @@ module.exports = [ } ] - const deleteMicroserviceRouteEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.deleteMicroserviceRouteEndPoint, successCode, errorCodes) - const responseObject = await deleteMicroserviceRouteEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await updateMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'post', - path: '/api/v3/microservices/:uuid/port-mapping', + method: 'get', + path: '/api/v3/microservices/system/:uuid/config', middleware: async (req, res) => { logger.apiReq(req) - const successCode = constants.HTTP_CODE_CREATED + const successCode = constants.HTTP_CODE_SUCCESS const errorCodes = [ { code: constants.HTTP_CODE_BAD_REQUEST, @@ -348,25 +570,66 @@ module.exports = [ } ] - const createMicroservicePortMappingEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.createMicroservicePortMappingEndPoint, successCode, errorCodes) - const responseObject = await createMicroservicePortMappingEndPoint(req) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getSystemMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.getSystemMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await getSystemMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/system/:uuid/config', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const updateSystemMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.updateSystemMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await updateSystemMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { method: 'delete', - path: '/api/v3/microservices/:uuid/port-mapping/:internalPort', + path: '/api/v3/microservices/:uuid/config', middleware: async (req, res) => { logger.apiReq(req) const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, { code: constants.HTTP_CODE_UNAUTHORIZED, errors: [Errors.AuthenticationError] @@ -377,25 +640,31 @@ module.exports = [ } ] - const deleteMicroservicePortMapping = ResponseDecorator.handleErrors( - MicroservicesController.deleteMicroservicePortMappingEndPoint, successCode, errorCodes) - const responseObject = await deleteMicroservicePortMapping(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.deleteMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await deleteMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'get', - path: '/api/v3/microservices/:uuid/port-mapping', + method: 'delete', + path: '/api/v3/microservices/system/:uuid/config', middleware: async (req, res) => { logger.apiReq(req) - const successCode = constants.HTTP_CODE_SUCCESS + const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, { code: constants.HTTP_CODE_UNAUTHORIZED, errors: [Errors.AuthenticationError] @@ -406,24 +675,26 @@ module.exports = [ } ] - const getMicroservicePortMapping = ResponseDecorator.handleErrors( - MicroservicesController.getMicroservicePortMappingListEndPoint, successCode, errorCodes) - const responseObject = await getMicroservicePortMapping(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteSystemMicroserviceConfigEndPoint = ResponseDecorator.handleErrors(MicroservicesController.deleteSystemMicroserviceConfigEndPoint, + successCode, errorCodes) + const responseObject = await deleteSystemMicroserviceConfigEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { - method: 'get', - path: '/api/v3/microservices/:uuid/volume-mapping', + method: 'delete', + path: '/api/v3/microservices/:uuid', middleware: async (req, res) => { logger.apiReq(req) - const successCode = constants.HTTP_CODE_SUCCESS + const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, @@ -435,23 +706,22 @@ module.exports = [ } ] - const listMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.listMicroserviceVolumeMappingsEndPoint, - successCode, - errorCodes - ) - const responseObject = await listMicroserviceVolumeMappingEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.deleteMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await deleteMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { method: 'post', - path: '/api/v3/microservices/:uuid/volume-mapping', + path: '/api/v3/microservices/:uuid/routes/:receiverUuid', middleware: async (req, res) => { logger.apiReq(req) @@ -471,23 +741,22 @@ module.exports = [ } ] - const createMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.createMicroserviceVolumeMappingEndPoint, - successCode, - errorCodes - ) - const responseObject = await createMicroserviceVolumeMappingEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroserviceRouteEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroserviceRouteEndPoint, successCode, errorCodes) + const responseObject = await createMicroserviceRouteEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { method: 'delete', - path: '/api/v3/microservices/:uuid/volume-mapping/:id', + path: '/api/v3/microservices/:uuid/routes/:receiverUuid', middleware: async (req, res) => { logger.apiReq(req) @@ -507,18 +776,631 @@ module.exports = [ } ] - const deleteMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( - MicroservicesController.deleteMicroserviceVolumeMappingEndPoint, - successCode, - errorCodes - ) - const responseObject = await deleteMicroserviceVolumeMappingEndPoint(req) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroserviceRouteEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.deleteMicroserviceRouteEndPoint, successCode, errorCodes) + const responseObject = await deleteMicroserviceRouteEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/:uuid/port-mapping', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroservicePortMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroservicePortMappingEndPoint, successCode, errorCodes) + const responseObject = await createMicroservicePortMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/system/:uuid/port-mapping', + middleware: async (req, res) => { + logger.apiReq(req) - res - .status(responseObject.code) - .send(responseObject.body) + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE'])(req, res, async () => { + const createSystemMicroservicePortMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createSystemMicroservicePortMappingEndPoint, successCode, errorCodes) + const responseObject = await createSystemMicroservicePortMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/:uuid/port-mapping/:internalPort', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroservicePortMapping = ResponseDecorator.handleErrors( + MicroservicesController.deleteMicroservicePortMappingEndPoint, successCode, errorCodes) + const responseObject = await deleteMicroservicePortMapping(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/system/:uuid/port-mapping/:internalPort', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteSystemMicroservicePortMapping = ResponseDecorator.handleErrors( + MicroservicesController.deleteSystemMicroservicePortMappingEndPoint, successCode, errorCodes) + const responseObject = await deleteSystemMicroservicePortMapping(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/microservices/:uuid/port-mapping', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getMicroservicePortMapping = ResponseDecorator.handleErrors( + MicroservicesController.getMicroservicePortMappingListEndPoint, successCode, errorCodes) + const responseObject = await getMicroservicePortMapping(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/microservices/:uuid/volume-mapping', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.listMicroserviceVolumeMappingsEndPoint, + successCode, + errorCodes + ) + const responseObject = await listMicroserviceVolumeMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/:uuid/volume-mapping', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroserviceVolumeMappingEndPoint, + successCode, + errorCodes + ) + const responseObject = await createMicroserviceVolumeMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/system/:uuid/volume-mapping', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const createSystemMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createSystemMicroserviceVolumeMappingEndPoint, + successCode, + errorCodes + ) + const responseObject = await createSystemMicroserviceVolumeMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/:uuid/volume-mapping/:id', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.deleteMicroserviceVolumeMappingEndPoint, + successCode, + errorCodes + ) + const responseObject = await deleteMicroserviceVolumeMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/system/:uuid/volume-mapping/:id', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteSystemMicroserviceVolumeMappingEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.deleteSystemMicroserviceVolumeMappingEndPoint, + successCode, + errorCodes + ) + const responseObject = await deleteSystemMicroserviceVolumeMappingEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createMicroserviceExecEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createMicroserviceExecEndPoint, + successCode, + errorCodes + ) + const responseObject = await createMicroserviceExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/microservices/system/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const createSystemMicroserviceExecEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.createSystemMicroserviceExecEndPoint, + successCode, + errorCodes + ) + const responseObject = await createSystemMicroserviceExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteMicroserviceExecEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.deleteMicroserviceExecEndPoint, + successCode, + errorCodes + ) + const responseObject = await deleteMicroserviceExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/microservices/system/:uuid/exec', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteSystemMicroserviceExecEndPoint = ResponseDecorator.handleErrors( + MicroservicesController.deleteSystemMicroserviceExecEndPoint, + successCode, + errorCodes + ) + const responseObject = await deleteSystemMicroserviceExecEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/:uuid/start', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const startMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.startMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await startMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/:uuid/stop', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const stopMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.stopMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await stopMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'ws', + path: '/api/v3/microservices/exec/:microserviceUuid', + middleware: async (ws, req) => { + logger.apiReq(req) + try { + const token = req.headers.authorization + if (!token) { + logger.error('WebSocket connection failed: Missing authentication token') + try { + ws.close(1008, 'Missing authentication token') + } catch (error) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: error.message, + originalError: 'Missing authentication token' + })) + } + return + } + + // Initialize WebSocket connection for microservice + const wsServer = WebSocketServer.getInstance() + await wsServer.handleConnection(ws, req) + } catch (error) { + logger.error('Error in microservice WebSocket connection:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url, + microserviceUuid: req.params.microserviceUuid + })) + try { + if (ws.readyState === ws.OPEN) { + ws.close(1008, error.message || 'Authentication failed') + } + } catch (closeError) { + logger.error('Error closing microservice WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } } } ] diff --git a/src/routes/registries.js b/src/routes/registries.js index 7f9d416a2..e2d2f8d40 100644 --- a/src/routes/registries.js +++ b/src/routes/registries.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const RegistryController = require('../controllers/registry-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -35,13 +36,17 @@ module.exports = [ errors: [Errors.AuthenticationError] } ] - const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.createRegistryEndPoint, successCode, errorCodes) - const responseObject = await registriesEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.createRegistryEndPoint, successCode, errorCodes) + const responseObject = await registriesEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -61,13 +66,17 @@ module.exports = [ errors: [Errors.AuthenticationError] } ] - const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.getRegistriesEndPoint, successCode, errorCodes) - const responseObject = await registriesEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.getRegistriesEndPoint, successCode, errorCodes) + const responseObject = await registriesEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -91,13 +100,17 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.deleteRegistryEndPoint, successCode, errorCodes) - const responseObject = await registriesEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + await keycloak.protect('SRE', 'Developer')(req, res, async () => { + const registriesEndPoint = ResponseDecorator.handleErrors(RegistryController.deleteRegistryEndPoint, successCode, errorCodes) + const responseObject = await registriesEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -122,14 +135,18 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const updateRegistryEndPoint = ResponseDecorator.handleErrors(RegistryController.updateRegistryEndPoint, - successCode, errorCodes) - const responseObject = await updateRegistryEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + // Protecting for both SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateRegistryEndPoint = ResponseDecorator.handleErrors(RegistryController.updateRegistryEndPoint, successCode, errorCodes) + const responseObject = await updateRegistryEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/router.js b/src/routes/router.js index 5653f2a3a..cc13fbbde 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const Router = require('../controllers/router-controller') const ResponseDecorator = require('../decorators/response-decorator') const logger = require('../logger') const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -34,14 +35,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const getRouterEndpoint = ResponseDecorator.handleErrors(Router.getRouterEndPoint, successCode, errorCodes) - const responseObject = await getRouterEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Protecting for SRE, Developer, and Viewer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getRouterEndpoint = ResponseDecorator.handleErrors( + Router.getRouterEndPoint, + successCode, + errorCodes + ) + const responseObject = await getRouterEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -62,14 +71,22 @@ module.exports = [ errors: [Errors.ValidationError] } ] - const upsertDefaultRouter = ResponseDecorator.handleErrors(Router.upsertDefaultRouter, successCode, errorCodes) - const responseObject = await upsertDefaultRouter(req) - res - .status(responseObject.code) - .send(responseObject.body) + // Protecting for SRE role + await keycloak.protect('SRE')(req, res, async () => { + const upsertDefaultRouter = ResponseDecorator.handleErrors( + Router.upsertDefaultRouter, + successCode, + errorCodes + ) + const responseObject = await upsertDefaultRouter(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/routing.js b/src/routes/routing.js index 280b17cdc..e44c9d2ab 100644 --- a/src/routes/routing.js +++ b/src/routes/routing.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const Routing = require('../controllers/routing-controller') const ResponseDecorator = require('../decorators/response-decorator') const logger = require('../logger') const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -34,14 +35,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const getRouterEndpoint = ResponseDecorator.handleErrors(Routing.getRoutingsEndPoint, successCode, errorCodes) - const responseObject = await getRouterEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE , Developer and Viewer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getRouterEndpoint = ResponseDecorator.handleErrors( + Routing.getRoutingsEndPoint, + successCode, + errorCodes + ) + const responseObject = await getRouterEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -61,14 +70,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const getRouterEndpoint = ResponseDecorator.handleErrors(Routing.getRoutingEndPoint, successCode, errorCodes) - const responseObject = await getRouterEndpoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE, Developer, and Viewer roles + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getRouterEndpoint = ResponseDecorator.handleErrors( + Routing.getRoutingEndPoint, + successCode, + errorCodes + ) + const responseObject = await getRouterEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -97,14 +114,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const createRoutingEndpoint = ResponseDecorator.handleErrors(Routing.createRoutingEndpoint, successCode, errorCodes) - const responseObject = await createRoutingEndpoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createRoutingEndpoint = ResponseDecorator.handleErrors( + Routing.createRoutingEndpoint, + successCode, + errorCodes + ) + const responseObject = await createRoutingEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -130,15 +155,21 @@ module.exports = [ } ] - const updateRoutingEndpoint = ResponseDecorator.handleErrors(Routing.updateRoutingEndpoint, - successCode, errorCodes) - const responseObject = await updateRoutingEndpoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateRoutingEndpoint = ResponseDecorator.handleErrors( + Routing.updateRoutingEndpoint, + successCode, + errorCodes + ) + const responseObject = await updateRoutingEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -159,15 +190,21 @@ module.exports = [ } ] - const deleteRoutingEndpoint = ResponseDecorator.handleErrors(Routing.deleteRoutingEndpoint, - successCode, errorCodes) - const responseObject = await deleteRoutingEndpoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE and Developer roles + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteRoutingEndpoint = ResponseDecorator.handleErrors( + Routing.deleteRoutingEndpoint, + successCode, + errorCodes + ) + const responseObject = await deleteRoutingEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/secret.js b/src/routes/secret.js new file mode 100644 index 000000000..b6c6f55ae --- /dev/null +++ b/src/routes/secret.js @@ -0,0 +1,246 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const constants = require('../helpers/constants') +const SecretController = require('../controllers/secret-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'post', + path: '/api/v3/secrets', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createSecretEndpoint = ResponseDecorator.handleErrors(SecretController.createSecretEndpoint, successCode, errorCodes) + const responseObject = await createSecretEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/secrets/yaml', + fileInput: 'secret', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_CONFLICT, + errors: [Errors.ConflictError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createSecretFromYamlEndpoint = ResponseDecorator.handleErrors(SecretController.createSecretFromYamlEndpoint, successCode, errorCodes) + const responseObject = await createSecretFromYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/secrets/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateSecretEndpoint = ResponseDecorator.handleErrors(SecretController.updateSecretEndpoint, successCode, errorCodes) + const responseObject = await updateSecretEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/secrets/yaml/:name', + fileInput: 'secret', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateSecretFromYamlEndpoint = ResponseDecorator.handleErrors(SecretController.updateSecretFromYamlEndpoint, successCode, errorCodes) + const responseObject = await updateSecretFromYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/secrets/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getSecretEndpoint = ResponseDecorator.handleErrors(SecretController.getSecretEndpoint, successCode, errorCodes) + const responseObject = await getSecretEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/secrets', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listSecretsEndpoint = ResponseDecorator.handleErrors(SecretController.listSecretsEndpoint, successCode, errorCodes) + const responseObject = await listSecretsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/secrets/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteSecretEndpoint = ResponseDecorator.handleErrors(SecretController.deleteSecretEndpoint, successCode, errorCodes) + const responseObject = await deleteSecretEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/routes/service.js b/src/routes/service.js new file mode 100644 index 000000000..e3f198ba1 --- /dev/null +++ b/src/routes/service.js @@ -0,0 +1,274 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const constants = require('../helpers/constants') +const ServiceController = require('../controllers/service-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'get', + path: '/api/v3/services', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const listServicesEndpoint = ResponseDecorator.handleErrors( + ServiceController.listServicesEndpoint, + successCode, + errorCodes + ) + const responseObject = await listServicesEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/services/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getServiceEndpoint = ResponseDecorator.handleErrors( + ServiceController.getServiceEndpoint, + successCode, + errorCodes + ) + const responseObject = await getServiceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/services', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.DuplicatePropertyError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createServiceEndpoint = ResponseDecorator.handleErrors( + ServiceController.createServiceEndpoint, + successCode, + errorCodes + ) + const responseObject = await createServiceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/services/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateServiceEndpoint = ResponseDecorator.handleErrors( + ServiceController.updateServiceEndpoint, + successCode, + errorCodes + ) + const responseObject = await updateServiceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/services/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const deleteServiceEndpoint = ResponseDecorator.handleErrors( + ServiceController.deleteServiceEndpoint, + successCode, + errorCodes + ) + const responseObject = await deleteServiceEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/services/yaml', + fileInput: 'service', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_CREATED + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.DuplicatePropertyError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const createServiceYAMLEndpoint = ResponseDecorator.handleErrors( + ServiceController.createServiceYAMLEndpoint, + successCode, + errorCodes + ) + const responseObject = await createServiceYAMLEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/services/yaml/:name', + fileInput: 'service', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const updateServiceYAMLEndpoint = ResponseDecorator.handleErrors( + ServiceController.updateServiceYAMLEndpoint, + successCode, + errorCodes + ) + const responseObject = await updateServiceYAMLEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/routes/tunnel.js b/src/routes/tunnel.js index 03a9c62ea..40c3b6223 100644 --- a/src/routes/tunnel.js +++ b/src/routes/tunnel.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,7 @@ const TunnelController = require('../controllers/tunnel-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') const logger = require('../logger') +const keycloak = require('../config/keycloak.js').initKeycloak() module.exports = [ { @@ -38,13 +39,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const tunnelEndPoint = ResponseDecorator.handleErrors(TunnelController.manageTunnelEndPoint, successCode, errorCodes) - const responseObject = await tunnelEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE and Developer roles + await keycloak.protect(['SRE'])(req, res, async () => { + const tunnelEndPoint = ResponseDecorator.handleErrors( + TunnelController.manageTunnelEndPoint, + successCode, + errorCodes + ) + const responseObject = await tunnelEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } }, { @@ -64,13 +74,22 @@ module.exports = [ errors: [Errors.NotFoundError] } ] - const tunnelEndPoint = ResponseDecorator.handleErrors(TunnelController.getTunnelEndPoint, successCode, errorCodes) - const responseObject = await tunnelEndPoint(req) - res - .status(responseObject.code) - .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + // Protecting for SRE and Developer roles + await keycloak.protect(['SRE'])(req, res, async () => { + const tunnelEndPoint = ResponseDecorator.handleErrors( + TunnelController.getTunnelEndPoint, + successCode, + errorCodes + ) + const responseObject = await tunnelEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) } } ] diff --git a/src/routes/user.js b/src/routes/user.js index f010c7659..f900af895 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,12 +11,9 @@ * */ const constants = require('../helpers/constants') - const UserController = require('../controllers/user-controller') const ResponseDecorator = require('../decorators/response-decorator') const Errors = require('../helpers/errors') - -const Config = require('../config') const logger = require('../logger') module.exports = [ @@ -51,103 +48,31 @@ module.exports = [ }, { method: 'post', - path: '/api/v3/user/logout', + path: '/api/v3/user/refresh', middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_NO_CONTENT - const errorCodes = [ - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - } - ] + logger.apiReq('POST /api/v3/user/refresh') // don't use req as arg, because password not encrypted - const userLogoutEndPoint = ResponseDecorator.handleErrors(UserController.userLogoutEndPoint, successCode, errorCodes) - const responseObject = await userLogoutEndPoint(req) - - res - .status(responseObject.code) - .send() - } - }, - { - method: 'post', - path: '/api/v3/user/signup', - middleware: async (req, res) => { - logger.apiReq('POST /api/v3/user/signup') // don't use req as arg, because password not encrypted - - const successCode = constants.HTTP_CODE_CREATED - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - } - ] - - const userSignupEndPoint = ResponseDecorator.handleErrors(UserController.userSignupEndPoint, successCode, errorCodes) - const responseObject = await userSignupEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'get', - path: '/api/v3/user/signup/resend-activation', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_NO_CONTENT + const successCode = constants.HTTP_CODE_SUCCESS const errorCodes = [ { code: constants.HTTP_CODE_BAD_REQUEST, errors: [Errors.ValidationError] - } - ] - - const resendActivationEndPoint = ResponseDecorator.handleErrors(UserController.resendActivationEndPoint, - successCode, errorCodes) - const responseObject = await resendActivationEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'post', - path: '/api/v3/user/activate', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SEE_OTHER - const errorCodes = [ + }, { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.InvalidCredentialsError] } ] - const activateUserEndPoint = ResponseDecorator.handleErrors(UserController.activateUserAccountEndPoint, - successCode, errorCodes) - const responseObject = await activateUserEndPoint(req) - - // redirect to login page - if (responseObject.code === successCode) { - res.setHeader('Location', Config.get('Email:HomeUrl')) - } + const refreshTokenEndPoint = ResponseDecorator.handleErrors(UserController.refreshTokenEndPoint, successCode, errorCodes) + const responseObject = await refreshTokenEndPoint(req) res .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes('POST /api/v3/user/refresh', { args: { statusCode: responseObject.code } }) + // don't use req and responseObject as args, because they have password and token } }, { @@ -164,48 +89,23 @@ module.exports = [ } ] - const getUserProfileEndPoint = ResponseDecorator.handleErrors(UserController.getUserProfileEndPoint, successCode, errorCodes) + const getUserProfileEndPoint = ResponseDecorator.handleErrors( + UserController.getUserProfileEndPoint, + successCode, + errorCodes + ) const responseObject = await getUserProfileEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'patch', - path: '/api/v3/user/profile', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - } - ] - - const updateUserProfileEndPoint = ResponseDecorator.handleErrors(UserController.updateUserProfileEndPoint, - successCode, errorCodes) - const responseObject = await updateUserProfileEndPoint(req) - + const user = req.kauth.grant.access_token.content.preferred_username res .status(responseObject.code) .send(responseObject.body) - logger.apiRes({ req: req, res: responseObject }) + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) } }, { - method: 'delete', - path: '/api/v3/user/profile', + method: 'post', + path: '/api/v3/user/logout', middleware: async (req, res) => { logger.apiReq(req) @@ -217,69 +117,12 @@ module.exports = [ } ] - const deleteUserProfileEndPoint = ResponseDecorator.handleErrors(UserController.deleteUserProfileEndPoint, - successCode, errorCodes) - const responseObject = await deleteUserProfileEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'patch', - path: '/api/v3/user/password', - middleware: async (req, res) => { - logger.apiReq('PATCH /api/v3/user/password') // don't use req as arg, because password not encrypted - - const successCode = constants.HTTP_CODE_NO_CONTENT - const errorCodes = [ - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - }, - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - } - ] - - const updateUserPasswordEndPoint = ResponseDecorator.handleErrors(UserController.updateUserPasswordEndPoint, - successCode, errorCodes) - const responseObject = await updateUserPasswordEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - } - }, - { - method: 'delete', - path: '/api/v3/user/password', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_NO_CONTENT - const errorCodes = [ - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } - ] - - const resetUserPasswordEndPoint = ResponseDecorator.handleErrors(UserController.resetUserPasswordEndPoint, - successCode, errorCodes) - const responseObject = await resetUserPasswordEndPoint(req) + const userLogoutEndPoint = ResponseDecorator.handleErrors(UserController.userLogoutEndPoint, successCode, errorCodes) + const responseObject = await userLogoutEndPoint(req) res .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) + .send() } } ] diff --git a/src/routes/volumeMount.js b/src/routes/volumeMount.js new file mode 100644 index 000000000..99dee9a5f --- /dev/null +++ b/src/routes/volumeMount.js @@ -0,0 +1,343 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ +const constants = require('../helpers/constants') +const VolumeMountController = require('../controllers/volume-mount-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const keycloak = require('../config/keycloak.js').initKeycloak() + +module.exports = [ + { + method: 'get', + path: '/api/v3/volumeMounts', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getVolumeMountsEndpoint = ResponseDecorator.handleErrors(VolumeMountController.listVolumeMountsEndpoint, successCode, errorCodes) + const responseObject = await getVolumeMountsEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/volumeMounts/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer', 'Viewer'])(req, res, async () => { + const getVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.getVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await getVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/volumeMounts/:name', + supportSubstitution: true, + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const updateVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.updateVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await updateVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/volumeMounts/:name', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_ACCEPTED + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const deleteVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.deleteVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await deleteVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/volumeMounts', + supportSubstitution: true, + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const createVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.createVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await createVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/volumeMounts/yaml', + fileInput: 'volumeMount', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const createVolumeMountYamlEndpoint = ResponseDecorator.handleErrors(VolumeMountController.createVolumeMountYamlEndpoint, successCode, errorCodes) + const responseObject = await createVolumeMountYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/volumeMounts/yaml/:name', + fileInput: 'volumeMount', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE'])(req, res, async () => { + const updateVolumeMountYamlEndpoint = ResponseDecorator.handleErrors(VolumeMountController.updateVolumeMountYamlEndpoint, successCode, errorCodes) + const responseObject = await updateVolumeMountYamlEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'get', + path: '/api/v3/volumeMounts/:name/link', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const getVolumeMountLinkEndpoint = ResponseDecorator.handleErrors(VolumeMountController.getVolumeMountLinkEndpoint, successCode, errorCodes) + const responseObject = await getVolumeMountLinkEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'post', + path: '/api/v3/volumeMounts/:name/link', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const linkVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.linkVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await linkVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'delete', + path: '/api/v3/volumeMounts/:name/link', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + // Add keycloak.protect() middleware to protect the route for SRE role + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const unlinkVolumeMountEndpoint = ResponseDecorator.handleErrors(VolumeMountController.unlinkVolumeMountEndpoint, successCode, errorCodes) + const responseObject = await unlinkVolumeMountEndpoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + } +] diff --git a/src/schemas/agent.js b/src/schemas/agent.js index a0cf1fc17..40bd2046c 100644 --- a/src/schemas/agent.js +++ b/src/schemas/agent.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -55,6 +55,9 @@ const updateAgentConfig = { 'latitude': { 'type': 'number', 'minimum': -90, 'maximum': 90 }, 'longitude': { 'type': 'number', 'minimum': -180, 'maximum': 180 }, 'gpsMode': { 'type': 'string' }, + 'gpsDevice': { 'type': 'string' }, + 'gpsScanFrequency': { 'type': 'integer', 'minimum': 0 }, + 'edgeGuardFrequency': { 'type': 'integer', 'minimum': 0 }, 'dockerPruningFrequency': { 'type': 'integer', 'minimum': 0 }, 'availableDiskThreshold': { 'type': 'integer', 'minimum': 0 }, 'logLevel': { 'type': 'string' }, @@ -63,11 +66,22 @@ const updateAgentConfig = { 'additionalProperties': true } +const updateAgentGps = { + 'id': '/updateAgentGps', + 'type': 'object', + 'properties': { + 'latitude': { 'type': 'number', 'minimum': -90, 'maximum': 90 }, + 'longitude': { 'type': 'number', 'minimum': -180, 'maximum': 180 } + }, + 'additionalProperties': true +} + const updateAgentStatus = { 'id': '/updateAgentStatus', 'type': 'object', 'properties': { 'daemonStatus': { 'type': 'string' }, + 'warningMessage': { 'type': 'string' }, 'daemonOperatingDuration': { 'type': 'integer', 'minimum': 0 }, 'daemonLastStart': { 'type': 'integer', 'minimum': 0 }, 'memoryUsage': { 'type': 'number', 'minimum': 0 }, @@ -92,10 +106,15 @@ const updateAgentStatus = { 'microserviceMessageCounts': { 'type': 'string' }, 'messageSpeed': { 'type': 'number', 'minimum': 0 }, 'lastCommandTime': { 'type': 'integer', 'minimum': 0 }, + 'gpsMode': { 'type': 'string' }, + 'gpsDevice': { 'type': 'string' }, + 'gpsScanFrequency': { 'type': 'integer', 'minimum': 0 }, + 'edgeGuardFrequency': { 'type': 'integer', 'minimum': 0 }, 'tunnelStatus': { 'type': 'string' }, 'version': { 'type': 'string' }, 'isReadyToUpgrade': { 'type': 'boolean' }, - 'isReadyToRollback': { 'type': 'boolean' } + 'isReadyToRollback': { 'type': 'boolean' }, + 'gpsStatus': { 'type': 'string' } }, 'additionalProperties': true } @@ -131,10 +150,14 @@ const microserviceStatus = { 'id': { 'type': 'string' }, 'containerId': { 'type': 'string' }, 'status': { 'type': 'string' }, + 'healthStatus': { 'type': 'string' }, 'startTime': { 'type': 'integer' }, 'operatingDuration': { 'type': 'integer' }, 'cpuUsage': { 'type': 'number' }, - 'memoryUsage': { 'type': 'number' } + 'memoryUsage': { 'type': 'number' }, + 'ipAddress': { 'type': 'string' }, + 'ipAddressExternal': { 'type': 'string' }, + 'execSessionIds': { 'type': 'array', 'items': { 'type': 'string' } } }, 'required': ['id'], 'additionalProperties': true @@ -161,7 +184,7 @@ const updateUsbInfo = { } module.exports = { - mainSchemas: [agentProvision, agentDeprovision, updateAgentConfig, updateAgentStatus, updateAgentStrace, + mainSchemas: [agentProvision, agentDeprovision, updateAgentConfig, updateAgentGps, updateAgentStatus, updateAgentStrace, updateHardwareInfo, updateUsbInfo], innerSchemas: [straceData, microserviceStatus] } diff --git a/src/schemas/catalog.js b/src/schemas/catalog.js index fb7e69646..4b61d9a15 100644 --- a/src/schemas/catalog.js +++ b/src/schemas/catalog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/certificate.js b/src/schemas/certificate.js new file mode 100644 index 000000000..07e538922 --- /dev/null +++ b/src/schemas/certificate.js @@ -0,0 +1,143 @@ +const caCreate = { + id: '/caCreate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + subject: { type: 'string', minLength: 1 }, + expiration: { type: 'integer', minimum: 0 }, + type: { + type: 'string', + enum: ['k8s-secret', 'direct', 'self-signed'] + }, + secretName: { type: 'string' } + }, + required: ['type', 'name'], + additionalProperties: false, + allOf: [ + { + if: { properties: { type: { const: 'self-signed' } } }, + then: { required: ['name', 'subject', 'expiration'] } + }, + { + if: { + properties: { + type: { + enum: ['k8s-secret', 'direct'] + } + } + }, + then: { required: ['secretName'] } + } + ] +} + +const certificateCreate = { + id: '/certificateCreate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + subject: { type: 'string', minLength: 1 }, + hosts: { type: 'string', minLength: 1 }, + expiration: { type: 'integer', minimum: 0 }, + ca: { + type: 'object', + properties: { + type: { type: 'string', enum: ['k8s-secret', 'direct', 'self-signed'] }, + secretName: { type: 'string' } + // cert: { type: 'string' }, + // key: { type: 'string' } + }, + required: ['type'] + } + }, + required: ['name', 'subject', 'hosts'], + additionalProperties: false +} + +const caResponse = { + id: '/caResponse', + type: 'object', + properties: { + name: { type: 'string' }, + subject: { type: 'string' }, + type: { type: 'string' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['name', 'subject', 'type', 'created_at', 'updated_at'], + additionalProperties: false +} + +const certificateResponse = { + id: '/certificateResponse', + type: 'object', + properties: { + name: { type: 'string' }, + subject: { type: 'string' }, + hosts: { type: 'string' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['name', 'subject', 'hosts', 'created_at', 'updated_at'], + additionalProperties: false +} + +const caListResponse = { + id: '/caListResponse', + type: 'object', + properties: { + cas: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + subject: { type: 'string' }, + type: { type: 'string' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['name', 'subject', 'type', 'created_at', 'updated_at'], + additionalProperties: false + } + } + }, + required: ['cas'], + additionalProperties: false +} + +const certificateListResponse = { + id: '/certificateListResponse', + type: 'object', + properties: { + certificates: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + subject: { type: 'string' }, + hosts: { type: 'string' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['name', 'subject', 'hosts', 'created_at', 'updated_at'], + additionalProperties: false + } + } + }, + required: ['certificates'], + additionalProperties: false +} + +module.exports = { + mainSchemas: [ + caCreate, + certificateCreate, + caResponse, + certificateResponse, + caListResponse, + certificateListResponse + ], + innerSchemas: [] +} diff --git a/src/schemas/config-map.js b/src/schemas/config-map.js new file mode 100644 index 000000000..2376be9ee --- /dev/null +++ b/src/schemas/config-map.js @@ -0,0 +1,67 @@ +const configMapCreate = { + id: '/configMapCreate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + immutable: { type: 'boolean' }, + data: { type: 'object' } + }, + required: ['name', 'data'], + additionalProperties: false +} + +const configMapUpdate = { + id: '/configMapUpdate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + immutable: { type: 'boolean' }, + data: { type: 'object' } + }, + required: ['data'], + additionalProperties: false +} + +const configMapResponse = { + id: '/configMapResponse', + type: 'object', + properties: { + id: { type: 'integer' }, + name: { type: 'string' }, + immutable: { type: 'boolean' }, + data: { type: 'object' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['id', 'name', 'data', 'created_at', 'updated_at'], + additionalProperties: false +} + +const configMapListResponse = { + id: '/configMapListResponse', + type: 'object', + properties: { + configMaps: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'integer' }, + name: { type: 'string' }, + immutable: { type: 'boolean' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['id', 'name', 'created_at', 'updated_at'], + additionalProperties: false + } + } + }, + required: ['configMaps'], + additionalProperties: false +} + +module.exports = { + mainSchemas: [configMapCreate, configMapUpdate, configMapResponse, configMapListResponse], + innerSchemas: [] +} diff --git a/src/schemas/config.js b/src/schemas/config.js index 6c3db3c3a..5c4e390ba 100644 --- a/src/schemas/config.js +++ b/src/schemas/config.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,34 +12,27 @@ */ const configUpdate = { - 'id': '/configUpdate', - 'type': 'object', - 'properties': { - 'port': { 'type': 'integer', 'minimum': 0, 'maximum': 65535 }, - 'sslCert': { 'type': 'string' }, - 'sslKey': { 'type': 'string' }, - 'intermediateCert': { 'type': 'string' }, - 'emailActivationOn': { 'type': 'boolean' }, - 'emailActivationOff': { 'type': 'boolean' }, - 'homeUrl': { 'type': 'string' }, - 'emailAddress': { 'type': 'string' }, - 'emailPassword': { 'type': 'string', 'minLength': 1 }, - 'emailService': { 'type': 'string' }, - 'logDir': { 'type': 'string' }, - 'logSize': { 'type': 'integer' }, - 'kubelet': { 'type': 'string' } + id: '/configUpdate', + type: 'object', + properties: { + port: { type: 'integer', minimum: 0, maximum: 65535 }, + sslCert: { type: 'string' }, + sslKey: { type: 'string' }, + intermediateCert: { type: 'string', optional: true }, + logDir: { type: 'string' }, + logSize: { type: 'integer' } } } const configElement = { - 'id': '/configElement', - 'type': 'object', - 'properties': { - 'key': { 'type': 'string', 'minLength': 1 }, - 'value': { 'type': 'string' } + id: '/configElement', + type: 'object', + properties: { + key: { type: 'string', minLength: 1 }, + value: { type: 'string' } }, - 'required': ['key', 'value'], - 'additionalProperties': true + required: ['key', 'value'], + additionalProperties: true } module.exports = { diff --git a/src/data/managers/kubelet-access-token-manager.js b/src/schemas/controlPlane.js similarity index 54% rename from src/data/managers/kubelet-access-token-manager.js rename to src/schemas/controlPlane.js index c563fa455..79b659a93 100644 --- a/src/data/managers/kubelet-access-token-manager.js +++ b/src/schemas/controlPlane.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,15 +11,16 @@ * */ -const BaseManager = require('./base-manager') -const models = require('../models') -const KubeletAccessToken = models.KubeletAccessToken - -class KubeletAccessTokenManager extends BaseManager { - getEntity () { - return KubeletAccessToken - } +const details = { + id: '/profile', + type: 'object', + properties: { + }, + required: [], + additionalProperties: true } -const instance = new KubeletAccessTokenManager() -module.exports = instance +module.exports = { + mainSchemas: [details], + innerSchemas: [] +} diff --git a/src/schemas/diagnostics.js b/src/schemas/diagnostics.js index dc1e5d9a7..8947c331e 100644 --- a/src/schemas/diagnostics.js +++ b/src/schemas/diagnostics.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/edgeResource.js b/src/schemas/edgeResource.js index fa48c3f1d..1bc887799 100644 --- a/src/schemas/edgeResource.js +++ b/src/schemas/edgeResource.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/event.js b/src/schemas/event.js new file mode 100644 index 000000000..5803045b0 --- /dev/null +++ b/src/schemas/event.js @@ -0,0 +1,64 @@ +const eventListQuery = { + id: '/eventListQuery', + type: 'object', + properties: { + startTime: { + anyOf: [ + { type: 'number' }, + { type: 'string' } + ] + }, + endTime: { + anyOf: [ + { type: 'number' }, + { type: 'string' } + ] + }, + endpointType: { type: 'string', minLength: 1 }, + resourceType: { type: 'string', minLength: 1 }, + status: { type: 'string', minLength: 1 }, + method: { + anyOf: [ + { type: 'string', minLength: 1 }, + { + type: 'array', + items: { type: 'string', minLength: 1 } + } + ] + }, + actorId: { type: 'string', minLength: 1 }, + eventType: { type: 'string', minLength: 1 }, + limit: { + anyOf: [ + { type: 'number', minimum: 1 }, + { type: 'string', pattern: '^\\d+$' } + ] + }, + offset: { + anyOf: [ + { type: 'number', minimum: 0 }, + { type: 'string', pattern: '^\\d+$' } + ] + } + }, + additionalProperties: false +} + +const eventDeleteRequest = { + id: '/eventDeleteRequest', + type: 'object', + properties: { + days: { + type: 'number', + minimum: 0, + maximum: 365 + } + }, + required: ['days'], + additionalProperties: false +} + +module.exports = { + mainSchemas: [eventListQuery, eventDeleteRequest], + innerSchemas: [] +} diff --git a/src/schemas/index.js b/src/schemas/index.js index 7a843f03e..3ec077aae 100644 --- a/src/schemas/index.js +++ b/src/schemas/index.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/iofog.js b/src/schemas/iofog.js index 6af9298ef..c2246808a 100644 --- a/src/schemas/iofog.js +++ b/src/schemas/iofog.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -20,7 +20,10 @@ const iofogCreate = { 'latitude': { 'type': 'number', 'minimum': -90, 'maximum': 90 }, 'longitude': { 'type': 'number', 'minimum': -180, 'maximum': 180 }, 'description': { 'type': 'string' }, + 'networkInterface': { 'type': 'string' }, 'dockerUrl': { 'type': 'string' }, + 'containerEngine': { 'type': 'string', 'enum': ['docker', 'podman'] }, + 'deploymentType': { 'type': 'string', 'enum': ['native', 'container'] }, 'diskLimit': { 'type': 'integer', 'minimum': 0 }, 'diskDirectory': { 'type': 'string' }, 'memoryLimit': { 'type': 'integer', 'minimum': 0 }, @@ -82,7 +85,10 @@ const iofogUpdate = { 'latitude': { 'type': 'number', 'minimum': -90, 'maximum': 90 }, 'longitude': { 'type': 'number', 'minimum': -180, 'maximum': 180 }, 'description': { 'type': 'string' }, + 'networkInterface': { 'type': 'string' }, 'dockerUrl': { 'type': 'string' }, + 'containerEngine': { 'type': 'string', 'enum': ['docker', 'podman'] }, + 'deploymentType': { 'type': 'string', 'enum': ['native', 'container'] }, 'diskLimit': { 'type': 'integer', 'minimum': 0 }, 'diskDirectory': { 'type': 'string' }, 'memoryLimit': { 'type': 'integer', 'minimum': 0 }, @@ -250,9 +256,30 @@ const iofogTag = { 'type': 'string' } +const enableNodeExec = { + 'id': '/enableNodeExec', + 'type': 'object', + 'properties': { + 'uuid': { 'type': 'string' }, + 'image': { 'type': 'string' } + }, + 'required': ['uuid'], + 'additionalProperties': true +} + +const disableNodeExec = { + 'id': '/disableNodeExec', + 'type': 'object', + 'properties': { + 'uuid': { 'type': 'string' } + }, + 'required': ['uuid'], + 'additionalProperties': true +} + module.exports = { mainSchemas: [iofogCreate, iofogUpdate, iofogDelete, iofogGet, iofogGenerateProvision, iofogSetVersionCommand, - iofogReboot, iofogFilters, halGet, iofogPrune, defaultRouterCreate, iofogTag], + iofogReboot, iofogFilters, halGet, iofogPrune, defaultRouterCreate, iofogTag, enableNodeExec, disableNodeExec], innerSchemas: [filter, iofogTag] } diff --git a/src/schemas/microservice.js b/src/schemas/microservice.js index fd1637bc6..670c491d6 100644 --- a/src/schemas/microservice.js +++ b/src/schemas/microservice.js @@ -9,6 +9,7 @@ const microserviceCreate = { 'pattern': nameRegex }, 'config': { 'type': 'string' }, + 'annotations': { 'type': 'string' }, 'catalogItemId': { 'type': 'integer', 'minimum': 4 @@ -29,7 +30,13 @@ const microserviceCreate = { }, 'iofogUuid': { 'type': 'string' }, 'agentName': { 'type': 'string' }, - 'rootHostAccess': { 'type': 'boolean' }, + 'hostNetworkMode': { 'type': 'boolean' }, + 'isPrivileged': { 'type': 'boolean' }, + 'schedule': { + 'type': 'integer', + 'minimum': 0, + 'maximum': 100 + }, 'logSize': { 'type': 'integer' }, 'imageSnapshot': { 'type': 'string' }, 'volumeMappings': { @@ -49,7 +56,33 @@ const microserviceCreate = { 'items': { '$ref': '/env' } }, 'cmd': { 'type': 'array', - 'items': { 'type': 'string' } } + 'items': { 'type': 'string' } }, + 'cdiDevices': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'capAdd': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'capDrop': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'runAsUser': { 'type': 'string' }, + 'platform': { 'type': 'string' }, + 'runtime': { 'type': 'string' }, + 'cpuSetCpus': { 'type': 'string' }, + 'memoryLimit': { 'type': 'integer' }, + 'pubTags': { + 'type': 'array', + 'items': { 'type': 'string' } + }, + 'subTags': { + 'type': 'array', + 'items': { 'type': 'string' } + }, + 'healthCheck': { + 'type': 'object', + 'properties': { '$ref': '/microserviceHealthCheck' } + } }, 'required': ['name'], 'additionalProperties': true @@ -64,11 +97,18 @@ const microserviceUpdate = { 'pattern': nameRegex }, 'config': { 'type': 'string' }, + 'annotations': { 'type': 'string' }, 'rebuild': { 'type': 'boolean' }, 'iofogUuid': { 'type': 'string' }, 'agentName': { 'type': 'string' }, - 'rootHostAccess': { 'type': 'boolean' }, + 'hostNetworkMode': { 'type': 'boolean' }, + 'isPrivileged': { 'type': 'boolean' }, 'logSize': { 'type': 'integer', 'minimum': 0 }, + 'schedule': { + 'type': 'integer', + 'minimum': 0, + 'maximum': 100 + }, 'volumeMappings': { 'type': 'array', 'items': { '$ref': '/volumeMappings' } @@ -90,7 +130,33 @@ const microserviceUpdate = { 'items': { '$ref': '/env' } }, 'cmd': { 'type': 'array', - 'items': { 'type': 'string' } } + 'items': { 'type': 'string' } }, + 'cdiDevices': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'capAdd': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'capDrop': { + 'type': 'array', + 'items': { 'type': 'string' } }, + 'runAsUser': { 'type': 'string' }, + 'platform': { 'type': 'string' }, + 'runtime': { 'type': 'string' }, + 'cpuSetCpus': { 'type': 'string' }, + 'memoryLimit': { 'type': 'integer' }, + 'pubTags': { + 'type': 'array', + 'items': { 'type': 'string' } + }, + 'subTags': { + 'type': 'array', + 'items': { 'type': 'string' } + }, + 'healthCheck': { + 'type': 'object', + 'properties': { '$ref': '/microserviceHealthCheck' } + } }, 'additionalProperties': true } @@ -111,9 +177,22 @@ const env = { 'type': 'object', 'properties': { 'key': { 'type': 'string' }, - 'value': { 'type': 'string' } + 'value': { 'type': 'string' }, + 'valueFromSecret': { 'type': 'string' }, + 'valueFromConfigMap': { 'type': 'string' } }, - 'required': ['key', 'value'], + 'required': ['key'], + 'oneOf': [ + { + 'required': ['value'] + }, + { + 'required': ['valueFromSecret'] + }, + { + 'required': ['valueFromConfigMap'] + } + ], 'additionalProperties': true } @@ -134,44 +213,19 @@ const ports = { 'properties': { 'internal': { 'type': 'integer' }, 'external': { 'type': 'integer' }, - 'public': { '$ref': '/publicPort' }, - 'proxy': { type: 'boolean' }, 'protocol': { 'enum': ['tcp', 'udp'] } }, 'required': ['internal', 'external'], 'additionalProperties': true } -const publicPort = { - 'id': '/publicPort', - type: 'object', - properties: { - enabled: { type: 'boolean' }, - schemes: { type: 'array', items: { type: 'string' } }, - protocol: { 'enum': ['tcp', 'http'] }, - router: { '$ref': '/publicPortRouter' } - }, - required: ['schemes', 'protocol'] -} - -const publicPortRouter = { - 'id': '/publicPortRouter', - type: 'object', - properties: { - host: { type: 'string' }, - port: { type: 'number' } - }, - required: [] -} - const portsCreate = { 'id': '/portsCreate', 'type': 'object', 'properties': { 'internal': { 'type': 'integer' }, 'external': { 'type': 'integer' }, - 'protocol': { 'enum': ['tcp', 'udp'] }, - 'public': { '$ref': '/publicPort' } + 'protocol': { 'enum': ['tcp', 'udp'] } }, 'required': ['internal', 'external'], 'additionalProperties': true @@ -190,7 +244,25 @@ const volumeMappings = { 'additionalProperties': true } +const microserviceHealthCheck = { + + 'id': '/microserviceHealthCheck', + 'type': 'object', + 'properties': { + 'test': { + 'type': 'array', + 'items': { 'type': 'string' } + }, + 'interval': { 'type': 'integer' }, + 'timeout': { 'type': 'integer' }, + 'startPeriod': { 'type': 'integer' }, + 'startInterval': { 'type': 'integer' }, + 'retries': { 'type': 'integer' } + }, + 'required': ['test'] +} + module.exports = { - mainSchemas: [microserviceCreate, microserviceUpdate, env, ports, publicPort, publicPortRouter, extraHosts, portsCreate, microserviceDelete, volumeMappings], - innerSchemas: [volumeMappings, ports, publicPort, publicPortRouter, env, extraHosts, microserviceCreate] + mainSchemas: [microserviceCreate, microserviceUpdate, env, ports, extraHosts, portsCreate, microserviceDelete, volumeMappings, microserviceHealthCheck], + innerSchemas: [volumeMappings, ports, env, extraHosts, microserviceCreate, microserviceHealthCheck] } diff --git a/src/schemas/registry.js b/src/schemas/registry.js index bdd4b4ed4..4e31e9868 100644 --- a/src/schemas/registry.js +++ b/src/schemas/registry.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -23,9 +23,7 @@ const registryCreate = { 'type': 'string', 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' - }, - 'requiresCert': { 'type': 'boolean' }, - 'certificate': { 'type': 'string' } + } }, 'required': ['url', 'isPublic', 'username', 'password', 'email'], 'additionalProperties': true @@ -53,9 +51,7 @@ const registryUpdate = { 'type': 'string', 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' - }, - 'requiresCert': { 'type': 'boolean' }, - 'certificate': { 'type': 'string' } + } }, 'additionalProperties': true } diff --git a/src/schemas/routing.js b/src/schemas/routing.js index 83434a977..36d15db0c 100644 --- a/src/schemas/routing.js +++ b/src/schemas/routing.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/secret.js b/src/schemas/secret.js new file mode 100644 index 000000000..3a370c673 --- /dev/null +++ b/src/schemas/secret.js @@ -0,0 +1,67 @@ +const secretCreate = { + id: '/secretCreate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + type: { type: 'string', enum: ['Opaque', 'tls'] }, + data: { type: 'object' } + }, + required: ['name', 'type', 'data'], + additionalProperties: false +} + +const secretUpdate = { + id: '/secretUpdate', + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 255 }, + type: { type: 'string', enum: ['Opaque', 'tls'] }, + data: { type: 'object' } + }, + required: ['data'], + additionalProperties: false +} + +const secretResponse = { + id: '/secretResponse', + type: 'object', + properties: { + id: { type: 'integer' }, + name: { type: 'string' }, + type: { type: 'string', enum: ['Opaque', 'tls'] }, + data: { type: 'object' }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['id', 'name', 'type', 'data', 'created_at', 'updated_at'], + additionalProperties: false +} + +const secretListResponse = { + id: '/secretListResponse', + type: 'object', + properties: { + secrets: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'integer' }, + name: { type: 'string' }, + type: { type: 'string', enum: ['Opaque', 'tls'] }, + created_at: { type: 'string', format: 'date-time' }, + updated_at: { type: 'string', format: 'date-time' } + }, + required: ['id', 'name', 'type', 'created_at', 'updated_at'], + additionalProperties: false + } + } + }, + required: ['secrets'], + additionalProperties: false +} + +module.exports = { + mainSchemas: [secretCreate, secretUpdate, secretResponse, secretListResponse], + innerSchemas: [] +} diff --git a/src/schemas/service.js b/src/schemas/service.js new file mode 100644 index 000000000..770661499 --- /dev/null +++ b/src/schemas/service.js @@ -0,0 +1,108 @@ +const { serviceNameRegex } = require('./utils/utils') + +const serviceCreate = { + id: '/serviceCreate', + type: 'object', + required: ['name', 'type', 'resource', 'targetPort'], + properties: { + name: { + type: 'string', + pattern: serviceNameRegex + }, + type: { + type: 'string', + enum: ['microservice', 'k8s', 'agent', 'external'] + }, + resource: { + type: 'string', + required: true + }, + targetPort: { + type: 'integer' + }, + defaultBridge: { + type: 'string' + }, + servicePort: { + type: 'integer' + }, + k8sType: { + type: 'string', + enum: ['LoadBalancer', 'ClusterIP', 'NodePort'] + }, + tags: { + type: 'array', + items: { '$ref': '/serviceTag' } + } + } + // allOf: [ + // { + // if: { + // properties: { type: { const: 'k8s' } } + // }, + // then: { + // required: ['servicePort', 'k8sType'] + // } + // } + // ] +} + +const serviceUpdate = { + id: '/serviceUpdate', + type: 'object', + required: ['name'], + properties: { + name: { + type: 'string', + pattern: serviceNameRegex + }, + type: { + type: 'string', + enum: ['microservice', 'k8s', 'agent', 'external'] + }, + resource: { + type: 'string' + }, + targetPort: { + type: 'integer' + }, + defaultBridge: { + type: 'string' + }, + servicePort: { + type: 'integer' + }, + k8sType: { + type: 'string', + enum: ['LoadBalancer', 'ClusterIP', 'NodePort'] + }, + tags: { + type: 'array', + items: { '$ref': '/serviceTag' } + } + } + // allOf: [ + // { + // if: { + // properties: { type: { const: 'k8s' } } + // }, + // then: { + // required: ['servicePort', 'k8sType'] + // } + // } + // ] +} + +const serviceTag = { + id: '/serviceTag', + type: 'string' +} + +module.exports = { + mainSchemas: [ + serviceCreate, + serviceUpdate, + serviceTag + ], + innerSchemas: [serviceTag] +} diff --git a/src/schemas/tunnel.js b/src/schemas/tunnel.js index d0593947c..1b3b0110e 100644 --- a/src/schemas/tunnel.js +++ b/src/schemas/tunnel.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/schemas/user.js b/src/schemas/user.js index f7abab0db..d069afebd 100644 --- a/src/schemas/user.js +++ b/src/schemas/user.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,122 +11,33 @@ * */ -const signUp = { - 'id': '/signUp', - 'type': 'object', - 'properties': { - 'firstName': { 'type': 'string', 'minLength': 3 }, - 'lastName': { 'type': 'string', 'minLength': 3 }, - 'email': { - 'type': 'string', - 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + - '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' - }, - 'password': { 'type': 'string', 'minLength': 8 } - }, - 'required': ['email', 'password', 'firstName', 'lastName'], - 'additionalProperties': true -} - const login = { - 'id': '/login', - 'type': 'object', - 'properties': { - 'email': { - 'type': 'string', - 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + + id: '/login', + type: 'object', + properties: { + email: { + type: 'string', + pattern: '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' }, - 'password': { 'type': 'string' } + password: { type: 'string' }, + totp: { type: 'string' } }, - 'required': ['email', 'password'], - 'additionalProperties': true + required: ['email', 'password'], + additionalProperties: true } -const resendActivation = { - 'id': '/resendActivation', - 'type': 'object', - 'properties': { - 'email': { - 'type': 'string', - 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + - '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' - } - }, - 'required': ['email'], - 'additionalProperties': true -} - -const activateUser = { - 'id': '/activateUser', - 'type': 'object', - 'properties': { - 'activationCode': { 'type': 'string' } - }, - 'required': ['activationCode'], - 'additionalProperties': true -} - -const activateUserCLI = { - 'id': '/activateUserCLI', - 'type': 'object', - 'properties': { - 'email': { 'type': 'string' } - }, - 'required': ['email'], - 'additionalProperties': true -} - -const updateUserProfile = { - 'id': '/updateUserProfile', - 'type': 'object', - 'properties': { - 'firstName': { 'type': 'string', 'minLength': 3 }, - 'lastName': { 'type': 'string', 'minLength': 3 } - }, - 'required': [], - 'additionalProperties': true -} - -const updateUserProfileCLI = { - 'id': '/updateUserProfileCLI', - 'type': 'object', - 'properties': { - 'firstName': { 'type': 'string', 'minLength': 3 }, - 'lastName': { 'type': 'string', 'minLength': 3 }, - 'password': { 'type': 'string', 'minLength': 8 } - }, - 'required': [], - 'additionalProperties': true -} - -const updatePassword = { - 'id': '/updatePassword', - 'type': 'object', - 'properties': { - 'oldPassword': { 'type': 'string' }, - 'newPassword': { 'type': 'string', 'minLength': 8 } - }, - 'required': ['oldPassword', 'newPassword'], - 'additionalProperties': true -} - -const resetUserPassword = { - 'id': '/resetUserPassword', - 'type': 'object', - 'properties': { - 'email': { - 'type': 'string', - 'pattern': '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}' + - '\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$' - } +const refresh = { + id: '/refresh', + type: 'object', + properties: { + refreshToken: { type: 'string' } }, - 'required': ['email'], - 'additionalProperties': true + required: ['refreshToken'], + additionalProperties: true } module.exports = { - mainSchemas: [signUp, login, resendActivation, activateUser, activateUserCLI, updateUserProfile, - updateUserProfileCLI, updatePassword, resetUserPassword], + mainSchemas: [login, refresh], innerSchemas: [] } diff --git a/src/schemas/utils/utils.js b/src/schemas/utils/utils.js index 8f77b872b..9fcd98e95 100644 --- a/src/schemas/utils/utils.js +++ b/src/schemas/utils/utils.js @@ -1,6 +1,7 @@ /* eslint-disable no-useless-escape */ module.exports = { nameRegex: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$', + serviceNameRegex: '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$', // Supports hex, rgb, and rgba colorRegex: '^(#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8}))|(rgb\(\s*(?:(\d{1,3})\s*,?){3}\))|(rgba\(\s*(?:(\d{1,3})\s*,?){4}\))|$', // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string diff --git a/src/schemas/volume-mount.js b/src/schemas/volume-mount.js new file mode 100644 index 000000000..7aabc0eda --- /dev/null +++ b/src/schemas/volume-mount.js @@ -0,0 +1,91 @@ +const { serviceNameRegex } = require('./utils/utils') + +const volumeMountCreate = { + 'id': '/volumeMountCreate', + 'type': 'object', + 'properties': { + 'name': { + 'type': 'string', + 'pattern': serviceNameRegex + }, + 'secretName': { + 'type': 'string' + }, + 'configMapName': { + 'type': 'string' + } + }, + 'required': ['name'], + 'oneOf': [ + { + 'required': ['secretName'] + }, + { + 'required': ['configMapName'] + } + ], + 'additionalProperties': false +} + +const volumeMountUpdate = { + 'id': '/volumeMountUpdate', + 'type': 'object', + 'properties': { + 'name': { + 'type': 'string', + 'pattern': serviceNameRegex + }, + 'secretName': { + 'type': 'string' + }, + 'configMapName': { + 'type': 'string' + } + }, + 'oneOf': [ + { + 'required': ['secretName'] + }, + { + 'required': ['configMapName'] + } + ], + 'additionalProperties': false +} + +const volumeMountLink = { + 'id': '/volumeMountLink', + 'type': 'object', + 'properties': { + 'fogUuids': { + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'minItems': 1 + } + }, + 'required': ['fogUuids'], + 'additionalProperties': false +} + +const volumeMountUnlink = { + 'id': '/volumeMountUnlink', + 'type': 'object', + 'properties': { + 'fogUuids': { + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'minItems': 1 + } + }, + 'required': ['fogUuids'], + 'additionalProperties': false +} + +module.exports = { + mainSchemas: [volumeMountCreate, volumeMountUpdate, volumeMountLink, volumeMountUnlink], + innerSchemas: [volumeMountCreate, volumeMountUpdate] +} diff --git a/src/server.js b/src/server.js index 2f018ef15..f5b1d3f88 100755 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,213 +11,296 @@ * */ -const config = require('./config') -const logger = require('./logger') -const db = require('./data/models') - -const bodyParser = require('body-parser') -const cookieParser = require('cookie-parser') -const express = require('express') -const ecnViewer = process.env.ECN_VIEWER_PATH ? require(`${process.env.ECN_VIEWER_PATH}/package/index.js`) : require('@iofog/ecn-viewer') -const fs = require('fs') -const helmet = require('helmet') -const cors = require('cors') -const https = require('https') -const path = require('path') -const { renderFile } = require('ejs') -const xss = require('xss-clean') -const { substitutionMiddleware } = require('./helpers/template-helper') -const swaggerUi = require('swagger-ui-express') -const swaggerFile = require('../docs/swagger.json') -const multer = require('multer') -const multerMemStorage = multer.memoryStorage() -const uploadFile = (fileName) => multer({ - storage: multerMemStorage -}).single(fileName) - -const viewerApp = express() - -const app = express() - -app.use(cors()) - -app.use(helmet()) -app.use(xss()) - -// express logs -// app.use(morgan('combined')); - -app.use(bodyParser.urlencoded({ - extended: true -})) -app.use(bodyParser.json()) - -app.engine('ejs', renderFile) -app.set('view engine', 'ejs') -app.use(cookieParser()) - -app.set('views', path.join(__dirname, 'views')) - -app.on('uncaughtException', (req, res, route, err) => { - // TODO -}) +// Initialize everything in the correct order +const { initialize } = require('./init') +initialize().then(() => { + const config = require('./config') + const logger = require('./logger') + const db = require('./data/models') + const WebSocketServer = require('./websocket/server') + + const bodyParser = require('body-parser') + const cookieParser = require('cookie-parser') + const express = require('express') + const ecnViewer = process.env.ECN_VIEWER_PATH ? require(`${process.env.ECN_VIEWER_PATH}/package/index.js`) : require('@datasance/ecn-viewer') + const fs = require('fs') + const helmet = require('helmet') + const cors = require('cors') + const https = require('https') + const path = require('path') + const { renderFile } = require('ejs') + const xss = require('xss-clean') + const { substitutionMiddleware } = require('./helpers/template-helper') + const multer = require('multer') + const multerMemStorage = multer.memoryStorage() + const uploadFile = (fileName) => multer({ + storage: multerMemStorage + }).single(fileName) + + // Initialize session and Keycloak after config is loaded + const session = require('express-session') + const { initKeycloak, getMemoryStore } = require('./config/keycloak.js') + const memoryStore = getMemoryStore() + const keycloak = initKeycloak() + + const viewerApp = express() + const app = express() + + app.use(cors()) + + app.use(helmet()) + app.use(xss()) + + // express logs + // app.use(morgan('combined')); + app.use(session({ + secret: 'pot-controller', + resave: false, + saveUninitialized: true, + store: memoryStore + })) + app.use(keycloak.middleware()) + app.use(bodyParser.urlencoded({ + extended: true + })) + app.use(bodyParser.json()) + + app.engine('ejs', renderFile) + app.set('view engine', 'ejs') + app.use(cookieParser()) + + app.set('views', path.join(__dirname, 'views')) + + app.on('uncaughtException', (req, res, route, err) => { + // TODO + }) -app.use((req, res, next) => { - if (req.headers && req.headers['request-id']) { - req.id = req.headers['request-id'] - delete req.headers['request-id'] - } + app.use((req, res, next) => { + if (req.headers && req.headers['request-id']) { + req.id = req.headers['request-id'] + delete req.headers['request-id'] + } - res.append('X-Timestamp', Date.now()) - next() -}) + res.append('X-Timestamp', Date.now()) + next() + }) -global.appRoot = path.resolve(__dirname) + // Event audit middleware - tracks non-GET operations + // Must be after authentication middleware but before route handlers + const eventAuditMiddleware = require('./middlewares/event-audit-middleware') + app.use(eventAuditMiddleware) -const registerRoute = (route) => { - const middlewares = [route.middleware] - if (route.supportSubstitution) { - middlewares.unshift(substitutionMiddleware) - } - if (route.fileInput) { - middlewares.unshift(uploadFile(route.fileInput)) - } - app[route.method.toLowerCase()](route.path, ...middlewares) -} + global.appRoot = path.resolve(__dirname) -const setupMiddleware = function (routeName) { - const routes = [].concat(require(path.join(__dirname, 'routes', routeName)) || []) - routes.forEach(registerRoute) -} + const registerRoute = (route) => { + if (route.method.toLowerCase() === 'ws') { + // Handle WebSocket routes by registering them with our custom WebSocket server + const wsServer = WebSocketServer.getInstance() + wsServer.registerRoute(route.path, route.middleware) + } else { + // Handle HTTP routes + const middlewares = [route.middleware] + if (route.supportSubstitution) { + middlewares.unshift(substitutionMiddleware) + } + if (route.fileInput) { + middlewares.unshift(uploadFile(route.fileInput)) + } + app[route.method.toLowerCase()](route.path, ...middlewares) + } + } -fs.readdirSync(path.join(__dirname, 'routes')) - .forEach(setupMiddleware) + const setupMiddleware = function (routeName) { + const routes = [].concat(require(path.join(__dirname, 'routes', routeName)) || []) + routes.forEach(registerRoute) + } -const jobs = [] + fs.readdirSync(path.join(__dirname, 'routes')) + .forEach(setupMiddleware) -const setupJobs = function (file) { - jobs.push((require(path.join(__dirname, 'jobs', file)) || [])) -} + const jobs = [] -fs.readdirSync(path.join(__dirname, 'jobs')) - .filter((file) => { - return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js') - }) - .forEach(setupJobs) - -function registerServers (api, viewer) { - process.once('SIGTERM', async function (code) { - console.log('SIGTERM received. Shutting down.') - await new Promise((resolve) => { api.close(resolve) }) - console.log('API Server closed.') - await new Promise((resolve) => { viewer.close(resolve) }) - console.log('Viewer Server closed.') - process.exit(0) - }) -} + const setupJobs = function (file) { + jobs.push((require(path.join(__dirname, 'jobs', file)) || [])) + } -function startHttpServer (apps, ports, jobs) { - logger.info('SSL not configured, starting HTTP server.') + fs.readdirSync(path.join(__dirname, 'jobs')) + .filter((file) => { + return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js') + }) + .forEach(setupJobs) + + function registerServers (api, viewer) { + process.once('SIGTERM', async function (code) { + console.log('SIGTERM received. Shutting down.') + await new Promise((resolve) => { api.close(resolve) }) + console.log('API Server closed.') + await new Promise((resolve) => { viewer.close(resolve) }) + console.log('Viewer Server closed.') + process.exit(0) + }) + } - const viewerServer = apps.viewer.listen(ports.viewer, function onStart (err) { - if (err) { - logger.error(err) - } - logger.info(`==> 🌎 Viewer listening on port ${ports.viewer}. Open up http://localhost:${ports.viewer}/ in your browser.`) - }) - const apiServer = apps.api.listen(ports.api, function onStart (err) { - if (err) { - logger.error(err) - } - logger.info(`==> 🌎 API Listening on port ${ports.api}. Open up http://localhost:${ports.api}/ in your browser.`) - jobs.forEach((job) => job.run()) - }) - registerServers(apiServer, viewerServer) -} - -function startHttpsServer (apps, ports, sslKey, sslCert, intermedKey, jobs) { - try { - const sslOptions = { - key: fs.readFileSync(sslKey), - cert: fs.readFileSync(sslCert), - ca: fs.readFileSync(intermedKey), - requestCert: true, - rejectUnauthorized: false // currently for some reason iofog agent doesn't work without this option - } + function startHttpServer (apps, ports, jobs) { + logger.info('SSL not configured, starting HTTP server.') - const viewerServer = https.createServer(sslOptions, apps.viewer).listen(ports.viewer, function onStart (err) { + const viewerServer = apps.viewer.listen(ports.viewer, function onStart (err) { if (err) { logger.error(err) } - logger.info(`==> 🌎 HTTPS Viewer server listening on port ${ports.viewer}. Open up https://localhost:${ports.viewer}/ in your browser.`) - jobs.forEach((job) => job.run()) + logger.info(`==> 🌎 Viewer listening on port ${ports.viewer}. Open up http://localhost:${ports.viewer}/ in your browser.`) }) - - const apiServer = https.createServer(sslOptions, apps.api).listen(ports.api, function onStart (err) { + const apiServer = apps.api.listen(ports.api, function onStart (err) { if (err) { logger.error(err) } - logger.info(`==> 🌎 HTTPS API server listening on port ${ports.api}. Open up https://localhost:${ports.api}/ in your browser.`) + logger.info(`==> 🌎 API Listening on port ${ports.api}. Open up http://localhost:${ports.api}/ in your browser.`) jobs.forEach((job) => job.run()) }) + + // Initialize WebSocket server + const wsConfig = config.get('server.webSocket') + const wsServer = new WebSocketServer(wsConfig) + wsServer.initialize(apiServer) + logger.info(`==> 🌎 Webscoker API server listening on port ${ports.api}. Open up ws://localhost:${ports.api}/.`) registerServers(apiServer, viewerServer) - } catch (e) { - logger.error('ssl_key or ssl_cert or intermediate_cert is either missing or invalid. Provide valid SSL configurations.') } -} -const devMode = config.get('Server:DevMode') -const apiPort = +(config.get('Server:Port')) -const viewerPort = +(process.env.VIEWER_PORT || config.get('Viewer:Port')) -const viewerURL = process.env.VIEWER_URL || config.get('Viewer:Url') -const sslKey = config.get('Server:SslKey') -const sslCert = config.get('Server:SslCert') -const intermedKey = config.get('Server:IntermediateCert') + const { createSSLOptions } = require('./utils/ssl-utils') -viewerApp.use('/', ecnViewer.middleware(express)) + function startHttpsServer (apps, ports, sslKey, sslCert, intermedKey, jobs, isBase64 = false) { + try { + const sslOptions = createSSLOptions({ + key: sslKey, + cert: sslCert, + intermedKey: intermedKey, + isBase64: isBase64 + }) + + const viewerServer = https.createServer(sslOptions, apps.viewer).listen(ports.viewer, function onStart (err) { + if (err) { + logger.error(err) + } + logger.info(`==> 🌎 HTTPS Viewer server listening on port ${ports.viewer}. Open up https://localhost:${ports.viewer}/ in your browser.`) + jobs.forEach((job) => job.run()) + }) + + const apiServer = https.createServer(sslOptions, apps.api).listen(ports.api, function onStart (err) { + if (err) { + logger.error(err) + } + logger.info(`==> 🌎 HTTPS API server listening on port ${ports.api}. Open up https://localhost:${ports.api}/ in your browser.`) + jobs.forEach((job) => job.run()) + }) + + // Initialize WebSocket server with SSL + const wsConfig = config.get('server.webSocket') + const wsServer = new WebSocketServer(wsConfig) + wsServer.initialize(apiServer) + logger.info(`==> 🌎 WSS API server listening on port ${ports.api}. Open up wss://localhost:${ports.api}/.`) + + registerServers(apiServer, viewerServer) + } catch (e) { + logger.error('Error loading SSL certificates. Please check your configuration.') + } + } -const isDaemon = process.argv[process.argv.length - 1] === 'daemonize2' + const devMode = process.env.DEV_MODE || config.get('server.devMode') + const apiPort = process.env.API_PORT || config.get('server.port') + const viewerPort = process.env.VIEWER_PORT || config.get('viewer.port') + const viewerURL = process.env.VIEWER_URL || config.get('viewer.url') + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + + // File-based SSL configuration + const sslKey = process.env.SSL_PATH_KEY || config.get('server.ssl.path.key') + const sslCert = process.env.SSL_PATH_CERT || config.get('server.ssl.path.cert') + const intermedKey = process.env.SSL_PATH_INTERMEDIATE_CERT || config.get('server.ssl.path.intermediateCert') + + // Base64 SSL configuration + const sslKeyBase64 = process.env.SSL_BASE64_KEY || config.get('server.ssl.base64.key') + const sslCertBase64 = process.env.SSL_BASE64_CERT || config.get('server.ssl.base64.cert') + const intermedKeyBase64 = process.env.SSL_BASE64_INTERMEDIATE_CERT || config.get('server.ssl.base64.intermediateCert') + + const hasFileBasedSSL = !devMode && sslKey && sslCert + const hasBase64SSL = !devMode && sslKeyBase64 && sslCertBase64 + + const kcRealm = process.env.KC_REALM || config.get('auth.realm') + const kcURL = process.env.KC_URL || config.get('auth.url') + const kcClient = process.env.KC_VIEWER_CLIENT || config.get('auth.viewerClient') + + viewerApp.use('/', ecnViewer.middleware(express)) + + const isDaemon = process.argv[process.argv.length - 1] === 'daemonize2' + + const initState = async () => { + if (!isDaemon) { + // InitDB + try { + await db.initDB(true) + } catch (err) { + logger.error('Unable to initialize the database. Error: ' + err) + process.exit(1) + } -const initState = async () => { - if (!isDaemon) { - // InitDB - try { - await db.initDB(true) - } catch (err) { - logger.error('Unable to initialize the database. Error: ' + err) - process.exit(1) + // Store PID to let deamon know we are running. + jobs.push({ + run: () => { + const pidFile = path.join((process.env.PID_BASE || __dirname), 'iofog-controller.pid') + logger.info(`==> PID file: ${pidFile}`) + fs.writeFileSync(pidFile, process.pid.toString()) + } + }) } + // Set up controller-config.js for ECN Viewer + const ecnViewerControllerConfigFilePath = path.join(__dirname, '..', 'node_modules', '@datasance', 'ecn-viewer', 'build', 'controller-config.js') + const ecnViewerControllerConfig = { + port: apiPort, + user: {}, + controllerDevMode: devMode, + keycloakURL: kcURL, + keycloakRealm: kcRealm, + keycloakClientid: kcClient + } + if (viewerURL) { + ecnViewerControllerConfig.url = viewerURL + } + if (controlPlane) { + ecnViewerControllerConfig.controlPlane = controlPlane + } + const ecnViewerConfigScript = ` + window.controllerConfig = ${JSON.stringify(ecnViewerControllerConfig)} + ` + fs.writeFileSync(ecnViewerControllerConfigFilePath, ecnViewerConfigScript) + } - // Store PID to let deamon know we are running. - jobs.push({ - run: () => { - const pidFile = path.join((process.env.PID_BASE || __dirname), 'iofog-controller.pid') - logger.info(`==> PID file: ${pidFile}`) - fs.writeFileSync(pidFile, process.pid.toString()) + initState() + .then(() => { + if (hasFileBasedSSL) { + startHttpsServer( + { api: app, viewer: viewerApp }, + { api: apiPort, viewer: viewerPort }, + sslKey, + sslCert, + intermedKey, + jobs, + false + ) + } else if (hasBase64SSL) { + startHttpsServer( + { api: app, viewer: viewerApp }, + { api: apiPort, viewer: viewerPort }, + sslKeyBase64, + sslCertBase64, + intermedKeyBase64, + jobs, + true + ) + } else { + startHttpServer( + { api: app, viewer: viewerApp }, + { api: apiPort, viewer: viewerPort }, + jobs + ) } }) - } - // Set up controller-config.js for ECN Viewer - const ecnViewerControllerConfigFilePath = path.join(__dirname, '..', 'node_modules', '@iofog', 'ecn-viewer', 'build', 'controller-config.js') - const ecnViewerControllerConfig = { - port: apiPort, - user: {} - } - if (viewerURL) { - ecnViewerControllerConfig.url = viewerURL - } - const ecnViewerConfigScript = ` - window.controllerConfig = ${JSON.stringify(ecnViewerControllerConfig)} - ` - fs.writeFileSync(ecnViewerControllerConfigFilePath, ecnViewerConfigScript) -} - -initState() - .then(() => { - if (!devMode && sslKey && sslCert && intermedKey) { - startHttpsServer({ api: app, viewer: viewerApp }, { api: apiPort, viewer: viewerPort }, sslKey, sslCert, intermedKey, jobs) - } else { - startHttpServer({ api: app, viewer: viewerApp }, { api: apiPort, viewer: viewerPort }, jobs) - } - }) -app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerFile)) +}) diff --git a/src/services/access-token-service.js b/src/services/access-token-service.js deleted file mode 100644 index 6363acd20..000000000 --- a/src/services/access-token-service.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const AccessTokenManager = require('../data/managers/access-token-manager') - -const createAccessToken = async function (accessToken, transaction) { - return AccessTokenManager.create(accessToken, transaction) -} - -const removeAccessTokenByUserId = async function (userId, transaction) { - return AccessTokenManager.delete({ - userId: userId - }, transaction) -} - -module.exports = { - createAccessToken: createAccessToken, - removeAccessTokenByUserId: removeAccessTokenByUserId -} diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 14e008d2c..0fbd07050 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,23 +11,26 @@ * */ +const config = require('../config') const path = require('path') const fs = require('fs') const formidable = require('formidable') -const Sequelize = require('sequelize') +// const Sequelize = require('sequelize') const moment = require('moment') -const Op = Sequelize.Op +// const Op = Sequelize.Op +const logger = require('../logger') const TransactionDecorator = require('../decorators/transaction-decorator') const FogProvisionKeyManager = require('../data/managers/iofog-provision-key-manager') const FogManager = require('../data/managers/iofog-manager') -const FogAccessTokenService = require('../services/iofog-access-token-service') +const FogKeyService = require('../services/iofog-key-service') const ChangeTrackingService = require('./change-tracking-service') const FogVersionCommandManager = require('../data/managers/iofog-version-command-manager') const StraceManager = require('../data/managers/strace-manager') const RegistryManager = require('../data/managers/registry-manager') const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') -const MicroserviceStates = require('../enums/microservice-state') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') +const { microserviceState, microserviceExecState } = require('../enums/microservice-state') const FogStates = require('../enums/fog-state') const Validator = require('../schemas') const Errors = require('../helpers/errors') @@ -41,9 +44,11 @@ const MicroserviceService = require('../services/microservices-service') const RouterManager = require('../data/managers/router-manager') const EdgeResourceService = require('./edge-resource-service') const constants = require('../helpers/constants') +const SecretManager = require('../data/managers/secret-manager') +const ConfigMapManager = require('../data/managers/config-map-manager') const IncomingForm = formidable.IncomingForm const CHANGE_TRACKING_DEFAULT = {} -const CHANGE_TRACKING_KEYS = ['config', 'version', 'reboot', 'deleteNode', 'microserviceList', 'microserviceConfig', 'routing', 'registries', 'tunnel', 'diagnostics', 'isImageSnapshot', 'prune', 'routerChanged', 'linkedEdgeResources'] +const CHANGE_TRACKING_KEYS = ['config', 'version', 'reboot', 'deleteNode', 'microserviceList', 'microserviceConfig', 'routing', 'registries', 'tunnel', 'diagnostics', 'isImageSnapshot', 'prune', 'routerChanged', 'linkedEdgeResources', 'volumeMounts', 'execSessions'] for (const key of CHANGE_TRACKING_KEYS) { CHANGE_TRACKING_DEFAULT[key] = false } @@ -54,6 +59,7 @@ const agentProvision = async function (provisionData, transaction) { const provision = await FogProvisionKeyManager.findOne({ provisionKey: provisionData.key }, transaction) + if (!provision) { throw new Errors.NotFoundError(ErrorMessages.INVALID_PROVISIONING_KEY) } @@ -67,11 +73,17 @@ const agentProvision = async function (provisionData, transaction) { uuid: provision.iofogUuid }, transaction) + if (!fog) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_IOFOG_UUID) + } + await _checkMicroservicesFogType(fog, provisionData.type, transaction) - const newAccessToken = await FogAccessTokenService.generateAccessToken(transaction) + // Generate Ed25519 key pair + const keyPair = await FogKeyService.generateKeyPair(transaction) - await FogAccessTokenService.updateAccessToken(fog.uuid, newAccessToken, transaction) + // Store the public key + await FogKeyService.storePublicKey(fog.uuid, keyPair.publicKey, transaction) await FogManager.update({ uuid: fog.uuid @@ -83,9 +95,13 @@ const agentProvision = async function (provisionData, transaction) { provisionKey: provisionData.key }, transaction) + await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.volumeMounts, transaction) + await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.registries, transaction) + await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.microserviceFull, transaction) + return { uuid: fog.uuid, - token: newAccessToken.token + privateKey: keyPair.privateKey } } @@ -94,7 +110,13 @@ const agentDeprovision = async function (deprovisionData, fog, transaction) { await MicroserviceStatusManager.update( { microserviceUuid: deprovisionData.microserviceUuids }, - { status: MicroserviceStates.DELETING }, + { status: microserviceState.DELETING }, + transaction + ) + + await MicroserviceExecStatusManager.update( + { microserviceUuid: deprovisionData.microserviceUuids }, + { status: microserviceExecState.INACTIVE }, transaction ) @@ -103,10 +125,10 @@ const agentDeprovision = async function (deprovisionData, fog, transaction) { const _invalidateFogNode = async function (fog, transaction) { const where = { uuid: fog.uuid } - const data = { daemonStatus: FogStates.UNKNOWN, ipAddress: '0.0.0.0', ipAddressExternal: '0.0.0.0' } + const data = { daemonStatus: FogStates.DEPROVISIONED, ipAddress: '0.0.0.0', ipAddressExternal: '0.0.0.0' } await FogManager.update(where, data, transaction) const updatedFog = Object.assign({}, fog) - updatedFog.daemonStatus = FogStates.UNKNOWN + updatedFog.daemonStatus = FogStates.DEPROVISIONED updatedFog.ipAddress = '0.0.0.0' updatedFog.ipAddressExternal = '0.0.0.0' return updatedFog @@ -114,30 +136,43 @@ const _invalidateFogNode = async function (fog, transaction) { const getAgentConfig = async function (fog, transaction) { const router = fog.routerId ? await RouterManager.findOne({ id: fog.routerId }, transaction) : await fog.getRouter() - // fog is the result of FogManager.FindOne() in the checkFogToken middleware - return { - networkInterface: fog.networkInterface, - dockerUrl: fog.dockerUrl, - diskLimit: fog.diskLimit, - diskDirectory: fog.diskDirectory, - memoryLimit: fog.memoryLimit, - cpuLimit: fog.cpuLimit, - logLimit: fog.logLimit, - logDirectory: fog.logDirectory, - logFileCount: fog.logFileCount, - statusFrequency: fog.statusFrequency, - changeFrequency: fog.changeFrequency, - deviceScanFrequency: fog.deviceScanFrequency, - watchdogEnabled: fog.watchdogEnabled, - latitude: fog.latitude, - longitude: fog.longitude, - logLevel: fog.logLevel, - availableDiskThreshold: fog.availableDiskThreshold, - dockerPruningFrequency: fog.dockerPruningFrequency, - routerHost: router.host === fog.host ? 'localhost' : router.host, + // Get local agent certificate from secrets + const localAgentSecret = await SecretManager.getSecret(`${fog.uuid}-local-agent`, transaction) + + const fogData = await FogManager.findOne({ + uuid: fog.uuid + }, transaction) + const resp = { + networkInterface: fogData.networkInterface, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + gpsMode: fogData.gpsMode, + gpsDevice: fogData.gpsDevice, + gpsScanFrequency: fogData.gpsScanFrequency, + edgeGuardFrequency: fogData.edgeGuardFrequency, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + watchdogEnabled: fogData.watchdogEnabled, + latitude: fogData.latitude, + longitude: fogData.longitude, + logLevel: fogData.logLevel, + availableDiskThreshold: fogData.availableDiskThreshold, + dockerPruningFrequency: fogData.dockerPruningFrequency, + routerHost: router.host === fogData.host ? 'localhost' : router.host, routerPort: router.messagingPort, - timeZone: fog.timeZone + timeZone: fogData.timeZone, + caCert: localAgentSecret ? localAgentSecret.data['ca.crt'] : null, + tlsCert: localAgentSecret ? localAgentSecret.data['tls.crt'] : null, + tlsKey: localAgentSecret ? localAgentSecret.data['tls.key'] : null } + return resp } const updateAgentConfig = async function (updateData, fog, transaction) { @@ -160,6 +195,9 @@ const updateAgentConfig = async function (updateData, fog, transaction) { latitude: updateData.latitude, longitude: updateData.longitude, gpsMode: updateData.gpsMode, + gpsDevice: updateData.gpsDevice, + gpsScanFrequency: updateData.gpsScanFrequency, + edgeGuardFrequency: updateData.edgeGuardFrequency, dockerPruningFrequency: updateData.dockerPruningFrequency, availableDiskThreshold: updateData.availableDiskThreshold, logLevel: updateData.logLevel, @@ -172,6 +210,20 @@ const updateAgentConfig = async function (updateData, fog, transaction) { }, update, transaction) } +const updateAgentGpsEndPoint = async function (updateData, fog, transaction) { + await Validator.validate(updateData, Validator.schemas.updateAgentGps) + + let update = { + latitude: updateData.latitude, + longitude: updateData.longitude + } + update = AppHelper.deleteUndefinedFields(update) + + await FogManager.update({ + uuid: fog.uuid + }, update, transaction) +} + const getAgentConfigChanges = async function (ioFog, transaction) { const changeTrackings = await ChangeTrackingService.getByIoFogUuid(ioFog.uuid, transaction) const res = { ...CHANGE_TRACKING_DEFAULT } @@ -183,7 +235,6 @@ const getAgentConfigChanges = async function (ioFog, transaction) { } res.lastUpdated = changeTracking.lastUpdated } - return res } @@ -223,11 +274,32 @@ const updateAgentStatus = async function (agentStatus, fog, transaction) { tunnelStatus: agentStatus.tunnelStatus, version: agentStatus.version, isReadyToUpgrade: agentStatus.isReadyToUpgrade, - isReadyToRollback: agentStatus.isReadyToRollback + isReadyToRollback: agentStatus.isReadyToRollback, + activeVolumeMounts: agentStatus.activeVolumeMounts, + volumeMountLastUpdate: agentStatus.volumeMountLastUpdate, + gpsStatus: agentStatus.gpsStatus } fogStatus = AppHelper.deleteUndefinedFields(fogStatus) + const existingFog = await FogManager.findOne({ + uuid: fog.uuid + }, transaction) + + if (!existingFog.warningMessage.includes('Background orchestration')) { + fogStatus.daemonStatus = agentStatus.daemonStatus + } else { + fogStatus.daemonStatus = FogStates.WARNING + } + + if (agentStatus.warningMessage.includes('HW signature changed') || agentStatus.warningMessage.includes('HW signature mismatch')) { + fogStatus.securityStatus = 'WARNING' + fogStatus.securityViolationInfo = 'HW signature mismatch' + } else { + fogStatus.securityStatus = 'OK' + fogStatus.securityViolationInfo = 'No violation' + } + await FogManager.update({ uuid: fog.uuid }, fogStatus, transaction) @@ -241,12 +313,15 @@ const _updateMicroserviceStatuses = async function (microserviceStatus, fog, tra let microserviceStatus = { containerId: status.containerId, status: status.status, + healthStatus: status.healthStatus, startTime: status.startTime, operatingDuration: status.operatingDuration, cpuUsage: status.cpuUsage, memoryUsage: status.memoryUsage, percentage: status.percentage, - errorMessage: status.errorMessage + errorMessage: status.errorMessage, + ipAddress: status.ipAddress, + execSessionIds: status.execSessionIds } microserviceStatus = AppHelper.deleteUndefinedFields(microserviceStatus) const microservice = await MicroserviceManager.findOne({ @@ -280,6 +355,7 @@ const getAgentMicroservices = async function (fog, transaction) { const routes = await MicroserviceService.getReceiverMicroservices(microservice, transaction) const isConsumer = await MicroserviceService.isMicroserviceConsumer(microservice, transaction) + const isRouter = await MicroserviceService.isMicroserviceRouter(microservice, transaction) const env = microservice.env && microservice.env.map((it) => { return { @@ -288,17 +364,63 @@ const getAgentMicroservices = async function (fog, transaction) { } }) const cmd = microservice.cmd && microservice.cmd.sort((a, b) => a.id - b.id).map((it) => it.cmd) - + const cdiDevices = microservice.cdiDevices && microservice.cdiDevices.sort((a, b) => a.id - b.id).map((it) => it.cdiDevices) + const capAdd = microservice.capAdd && microservice.capAdd.sort((a, b) => a.id - b.id).map((it) => it.capAdd) + const capDrop = microservice.capDrop && microservice.capDrop.sort((a, b) => a.id - b.id).map((it) => it.capDrop) const registryId = microservice.catalogItem && microservice.catalogItem.registry ? microservice.catalogItem.registry.id : microservice.registry.id const extraHosts = microservice.extraHosts ? microservice.extraHosts.map(_mapExtraHost) : [] + // Process health check data - handle both old and new formats + let healthCheck = null + + if (microservice.healthCheck) { + // Handle the test field - it could be already an array or a JSON string + let testData = microservice.healthCheck.test + if (testData && testData !== null && testData !== undefined && testData.length > 0) { + if (typeof testData === 'string') { + // It's a JSON string, try to parse it + try { + testData = JSON.parse(testData) + } catch (e) { + // If not valid JSON, treat as a single string command + testData = [testData] + } + } else if (!Array.isArray(testData)) { + // If it's not an array, convert to array + testData = [testData] + } + // If it's already an array, leave as is + } + + healthCheck = { + test: testData, + interval: microservice.healthCheck.interval, + timeout: microservice.healthCheck.timeout, + startPeriod: microservice.healthCheck.startPeriod, + startInterval: microservice.healthCheck.startInterval, + retries: microservice.healthCheck.retries + } + } else { + healthCheck = {} + } + const responseMicroservice = { uuid: microservice.uuid, imageId: imageId, config: microservice.config, + annotations: microservice.annotations, rebuild: microservice.rebuild, - rootHostAccess: microservice.rootHostAccess, + hostNetworkMode: microservice.hostNetworkMode, + isPrivileged: microservice.isPrivileged, + cpuSetCpus: microservice.cpuSetCpus, + memoryLimit: microservice.memoryLimit, + healthCheck: healthCheck, + pidMode: microservice.pidMode, + ipcMode: microservice.ipcMode, + runAsUser: microservice.runAsUser, + platform: microservice.platform, + runtime: microservice.runtime, logSize: parseInt(microservice.logSize) || constants.MICROSERVICE_DEFAULT_LOG_SIZE, registryId, portMappings: microservice.ports, @@ -309,8 +431,14 @@ const getAgentMicroservices = async function (fog, transaction) { env, extraHosts, cmd, + cdiDevices, + capAdd, + capDrop, routes, - isConsumer + isConsumer, + isRouter, + execEnabled: microservice.execEnabled, + schedule: microservice.schedule } response.push(responseMicroservice) @@ -366,17 +494,7 @@ const getAgentMicroservice = async function (microserviceUuid, fog, transaction) } const getAgentRegistries = async function (fog, transaction) { - const registries = await RegistryManager.findAll({ - [Op.or]: - [ - { - userId: fog.userId - }, - { - isPublic: true - } - ] - }, transaction) + const registries = await RegistryManager.findAll({}, transaction) return { registries: registries } @@ -568,11 +686,114 @@ async function _checkMicroservicesFogType (fog, fogTypeId, transaction) { } } +const getControllerCA = async function (fog, transaction) { + const devMode = process.env.DEV_MODE || config.get('server.devMode') + const sslCert = process.env.SSL_CERT || config.get('server.ssl.path.cert') + const intermedKey = process.env.INTERMEDIATE_CERT || config.get('server.ssl.path.intermediateCert') + const sslCertBase64 = config.get('server.ssl.base64.cert') + const intermedKeyBase64 = config.get('server.ssl.base64.intermediateCert') + const hasFileBasedSSL = !devMode && sslCert + const hasBase64SSL = !devMode && sslCertBase64 + + if (devMode) { + throw new Errors.ValidationError('Controller is in development mode') + } + + if (hasFileBasedSSL) { + try { + if (intermedKey) { + // Check if intermediate certificate file exists before trying to read it + if (fs.existsSync(intermedKey)) { + const certData = fs.readFileSync(intermedKey, 'utf8') + return Buffer.from(certData).toString('base64') + } else { + // Intermediate certificate file doesn't exist, don't provide any CA cert + // Let the system's default trust store handle validation + logger.info(`Intermediate certificate file not found at path: ${intermedKey}, not providing CA certificate`) + return '' + } + } else { + // No intermediate certificate path provided, don't provide any CA cert + // Let the system's default trust store handle validation + return '' + } + } catch (error) { + throw new Errors.ValidationError('Failed to read SSL certificate file') + } + } + + if (hasBase64SSL) { + if (intermedKeyBase64) { + return intermedKeyBase64 + } else { + // No intermediate certificate base64 provided, don't provide any CA cert + // Let the system's default trust store handle validation + return '' + } + } + + throw new Errors.ValidationError('No valid SSL certificate configuration found') +} + +const getAgentLinkedVolumeMounts = async function (fog, transaction) { + const volumeMounts = [] + const resourceAttributes = [ + 'uuid', + 'name', + 'version', + 'configMapName', + 'secretName' + ] + const resources = await fog.getVolumeMounts({ attributes: resourceAttributes }) + for (const resource of resources) { + const resourceObject = resource.toJSON() + let data = {} + + if (resourceObject.configMapName) { + // Handle ConfigMap + const configMap = await ConfigMapManager.getConfigMap(resourceObject.configMapName, transaction) + if (configMap) { + // For configmaps, we need to base64 encode all values + data = Object.entries(configMap.data).reduce((acc, [key, value]) => { + acc[key] = Buffer.from(value).toString('base64') + return acc + }, {}) + } + } else if (resourceObject.secretName) { + // Handle Secret + const secret = await SecretManager.getSecret(resourceObject.secretName, transaction) + if (secret) { + if (secret.type === 'tls') { + // For TLS secrets, values are already base64 encoded + data = secret.data + } else { + // For Opaque secrets, we need to base64 encode all values + data = Object.entries(secret.data).reduce((acc, [key, value]) => { + acc[key] = Buffer.from(value).toString('base64') + return acc + }, {}) + } + } + } + + // Create final response object without configMapName and secretName + const responseObject = { + uuid: resourceObject.uuid, + name: resourceObject.name, + version: resourceObject.version, + data: data + } + volumeMounts.push(responseObject) + } + return volumeMounts +} + module.exports = { agentProvision: TransactionDecorator.generateTransaction(agentProvision), agentDeprovision: TransactionDecorator.generateTransaction(agentDeprovision), getAgentConfig: TransactionDecorator.generateTransaction(getAgentConfig), updateAgentConfig: TransactionDecorator.generateTransaction(updateAgentConfig), + updateAgentGpsEndPoint: TransactionDecorator.generateTransaction(updateAgentGpsEndPoint), getAgentConfigChanges: TransactionDecorator.generateTransaction(getAgentConfigChanges), resetAgentConfigChanges: TransactionDecorator.generateTransaction(resetAgentConfigChanges), updateAgentStatus: TransactionDecorator.generateTransaction(updateAgentStatus), @@ -588,5 +809,7 @@ module.exports = { deleteNode: TransactionDecorator.generateTransaction(deleteNode), getImageSnapshot: TransactionDecorator.generateTransaction(getImageSnapshot), putImageSnapshot: TransactionDecorator.generateTransaction(putImageSnapshot), - getAgentLinkedEdgeResources: TransactionDecorator.generateTransaction(getAgentLinkedEdgeResources) + getAgentLinkedEdgeResources: TransactionDecorator.generateTransaction(getAgentLinkedEdgeResources), + getAgentLinkedVolumeMounts: TransactionDecorator.generateTransaction(getAgentLinkedVolumeMounts), + getControllerCA: TransactionDecorator.generateTransaction(getControllerCA) } diff --git a/src/services/application-service.js b/src/services/application-service.js index b057789ba..4e0aee1ae 100644 --- a/src/services/application-service.js +++ b/src/services/application-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -28,11 +28,11 @@ const remove = require('lodash/remove') const onlyUnique = (value, index, self) => self.indexOf(value) === index -const createApplicationEndPoint = async function (applicationData, user, isCLI, transaction) { +const createApplicationEndPoint = async function (applicationData, isCLI, transaction) { // if template is provided, use template data if (applicationData.template && applicationData.template.name) { applicationData = { - ...await ApplicationTemplateService.getApplicationDataFromTemplate(applicationData.template, user, isCLI, transaction), + ...await ApplicationTemplateService.getApplicationDataFromTemplate(applicationData.template, isCLI, transaction), isSystem: applicationData.isSystem, name: applicationData.name, description: applicationData.description, @@ -56,14 +56,13 @@ const createApplicationEndPoint = async function (applicationData, user, isCLI, } await Validator.validate(applicationData, Validator.schemas.applicationCreate) - await _checkForDuplicateName(applicationData.name, null, user.id, transaction) + await _checkForDuplicateName(applicationData.name, null, transaction) const applicationToCreate = { name: applicationData.name, description: applicationData.description, isActivated: !!applicationData.isActivated, - isSystem: !!applicationData.isSystem, - userId: user.id + isSystem: !!applicationData.isSystem } const applicationDataCreate = AppHelper.deleteUndefinedFields(applicationToCreate) @@ -73,13 +72,13 @@ const createApplicationEndPoint = async function (applicationData, user, isCLI, try { if (applicationData.microservices) { for (const msvcData of applicationData.microservices) { - await MicroserviceService.createMicroserviceEndPoint(msvcData, user, isCLI, transaction) + await MicroserviceService.createMicroserviceEndPoint(msvcData, isCLI, transaction) } } if (applicationData.routes) { for (const routeData of applicationData.routes) { - await RoutingService.createRouting(routeData, user, isCLI, transaction) + await RoutingService.createRouting(routeData, isCLI, transaction) } } @@ -89,15 +88,31 @@ const createApplicationEndPoint = async function (applicationData, user, isCLI, } } catch (e) { // If anything failed during creating the application, delete all that was created - await deleteApplicationEndPoint({ name: application.name }, user, isCLI, transaction) + await deleteApplicationEndPoint({ name: application.name }, isCLI, transaction) throw e } } -const deleteApplicationEndPoint = async function (conditions, user, isCLI, transaction) { +const deleteApplicationEndPoint = async function (conditions, isCLI, transaction) { const whereObj = { - ...conditions, - userId: user.id + ...conditions + } + const where = AppHelper.deleteUndefinedFields(whereObj) + + const application = await ApplicationManager.findOne({ ...conditions }, transaction) + + if (application.isSystem) { + throw new Errors.ValidationError('Cannot delete system application.') + } + + await _updateChangeTrackingsAndDeleteMicroservicesByApplicationId(conditions, true, transaction) + + await ApplicationManager.delete(where, transaction) +} + +const deleteSystemApplicationEndPoint = async function (conditions, isCLI, transaction) { + const whereObj = { + ...conditions } const where = AppHelper.deleteUndefinedFields(whereObj) @@ -107,16 +122,16 @@ const deleteApplicationEndPoint = async function (conditions, user, isCLI, trans } // Only patches the metadata (running, name, description, etc.) -const patchApplicationEndPoint = async function (applicationData, conditions, user, isCLI, transaction) { +const patchApplicationEndPoint = async function (applicationData, conditions, isCLI, transaction) { await Validator.validate(applicationData, Validator.schemas.applicationPatch) - const oldApplication = await ApplicationManager.findOne({ ...conditions, userId: user.id }, transaction) + const oldApplication = await ApplicationManager.findOne({ ...conditions }, transaction) if (!oldApplication) { throw new Errors.NotFoundError(ErrorMessages.INVALID_FLOW_ID) } if (applicationData.name) { - await _checkForDuplicateName(applicationData.name, oldApplication.id, user.id || oldApplication.userId, transaction) + await _checkForDuplicateName(applicationData.name, oldApplication.id, transaction) } const application = { @@ -130,7 +145,7 @@ const patchApplicationEndPoint = async function (applicationData, conditions, us const where = isCLI ? { id: oldApplication.id } - : { id: oldApplication.id, userId: user.id } + : { id: oldApplication.id } await ApplicationManager.update(where, updateApplicationData, transaction) if (oldApplication.isActivated !== applicationData.isActivated) { @@ -139,11 +154,11 @@ const patchApplicationEndPoint = async function (applicationData, conditions, us } // Updates the state (microservices, routes, etc.) -const updateApplicationEndPoint = async function (applicationData, name, user, isCLI, transaction) { +const updateApplicationEndPoint = async function (applicationData, name, isCLI, transaction) { // if template is provided, use template data if (applicationData.template && applicationData.template.name) { applicationData = { - ...await ApplicationTemplateService.getApplicationDataFromTemplate(applicationData.template, user, isCLI, transaction), + ...await ApplicationTemplateService.getApplicationDataFromTemplate(applicationData.template, isCLI, transaction), isSystem: applicationData.isSystem, name: applicationData.name || name, description: applicationData.description, @@ -167,13 +182,13 @@ const updateApplicationEndPoint = async function (applicationData, name, user, i await Validator.validate(applicationData, Validator.schemas.applicationUpdate) - const oldApplication = await ApplicationManager.findOne({ name, userId: user.id }, transaction) + const oldApplication = await ApplicationManager.findOne({ name }, transaction) if (!oldApplication) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, name)) } if (applicationData.name) { - await _checkForDuplicateName(applicationData.name, oldApplication.id, user.id || oldApplication.userId, transaction) + await _checkForDuplicateName(applicationData.name, oldApplication.id, transaction) } const application = { @@ -186,14 +201,14 @@ const updateApplicationEndPoint = async function (applicationData, name, user, i const updateApplicationData = AppHelper.deleteUndefinedFields(application) const where = isCLI ? { id: oldApplication.id } - : { id: oldApplication.id, userId: user.id } + : { id: oldApplication.id } await ApplicationManager.update(where, updateApplicationData, transaction) if (applicationData.microservices) { - await _updateMicroservices(application.name, applicationData.microservices, user, isCLI, transaction) + await _updateMicroservices(application.name, applicationData.microservices, isCLI, transaction) } if (applicationData.routes) { - await _updateRoutes(application.name, applicationData.routes, user, isCLI, transaction) + await _updateRoutes(application.name, applicationData.routes, isCLI, transaction) } if (oldApplication.isActivated !== applicationData.isActivated) { @@ -201,7 +216,7 @@ const updateApplicationEndPoint = async function (applicationData, name, user, i } } -const _updateRoutes = async function (application, routes, user, isCLI, transaction) { +const _updateRoutes = async function (application, routes, isCLI, transaction) { // Update routes const updatedRoutes = [...routes] const oldRoutes = await ApplicationManager.findApplicationRoutes({ name: application }, transaction) @@ -211,19 +226,19 @@ const _updateRoutes = async function (application, routes, user, isCLI, transact for (const oldRoute of oldRoutes) { const removed = remove(updatedRoutes, (n) => oldRoute.name === n.name) if (!removed.length) { - await RoutingService.deleteRouting(oldRoute.name, user, isCLI, transaction) + await RoutingService.deleteRouting(oldRoute.name, isCLI, transaction) } else { const updatedRoute = removed[0] - await RoutingService.updateRouting(application, updatedRoute.name, updatedRoute, user, isCLI, transaction) + await RoutingService.updateRouting(application, updatedRoute.name, updatedRoute, isCLI, transaction) } } // Create missing routes for (const route of updatedRoutes) { - await RoutingService.createRouting(route, user, isCLI, transaction) + await RoutingService.createRouting(route, isCLI, transaction) } } -const _updateMicroservices = async function (application, microservices, user, isCLI, transaction) { +const _updateMicroservices = async function (application, microservices, isCLI, transaction) { const updatedMicroservices = [...microservices] // Update microservices const oldMicroservices = await ApplicationManager.findApplicationMicroservices({ name: application }, transaction) @@ -240,14 +255,14 @@ const _updateMicroservices = async function (application, microservices, user, i iofogUuids.push(oldMsvc.iofogUuid) } else { const updatedMsvc = removed[0] - const updatedMicroservices = await MicroserviceService.updateMicroserviceEndPoint(oldMsvc.uuid, updatedMsvc, user, isCLI, transaction, false) + const updatedMicroservices = await MicroserviceService.updateMicroserviceEndPoint(oldMsvc.uuid, updatedMsvc, isCLI, transaction, false) oldMsvcsIofogUuids.push(updatedMicroservices.microserviceIofogUuid) updatedMsvcsUuid.push(updatedMicroservices.updatedMicroserviceIofogUuid) } } // Create missing microservices for (const microservice of updatedMicroservices) { - await MicroserviceService.createMicroserviceEndPoint(microservice, user, isCLI, transaction) + await MicroserviceService.createMicroserviceEndPoint(microservice, isCLI, transaction) } iofogUuids .filter(onlyUnique) @@ -272,9 +287,8 @@ const _updateMicroservices = async function (application, microservices, user, i }) } -const getUserApplicationsEndPoint = async function (user, isCLI, transaction) { +const getUserApplicationsEndPoint = async function (isCLI, transaction) { const application = { - userId: user.id, isSystem: false } @@ -286,6 +300,19 @@ const getUserApplicationsEndPoint = async function (user, isCLI, transaction) { } } +const getSystemApplicationsEndPoint = async function (isCLI, transaction) { + const application = { + isSystem: true + } + + const attributes = { exclude: ['created_at', 'updated_at'] } + const applications = await ApplicationManager.findAllPopulated(application, attributes, transaction) + + return { + applications: await Promise.all(applications.map(async (app) => _buildApplicationObject(app, transaction))) + } +} + const getAllApplicationsEndPoint = async function (isCLI, transaction) { const attributes = { exclude: ['created_at', 'updated_at'] } const applications = await ApplicationManager.findAllPopulated({}, attributes, transaction) @@ -303,10 +330,24 @@ async function _buildApplicationObject (application, transaction) { return application } -async function getApplication (conditions, user, isCLI, transaction) { +async function getApplication (conditions, isCLI, transaction) { + const where = isCLI + ? { ...conditions, isSystem: false } + : { ...conditions, isSystem: false } + const attributes = { exclude: ['created_at', 'updated_at'] } + + const applicationRaw = await ApplicationManager.findOnePopulated(where, attributes, transaction) + if (!applicationRaw) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, conditions.name || conditions.id)) + } + const application = await _buildApplicationObject(applicationRaw, transaction) + return application +} + +async function getSystemApplication (conditions, isCLI, transaction) { const where = isCLI - ? { ...conditions } - : { ...conditions, userId: user.id } + ? { ...conditions, isSystem: true } + : { ...conditions, isSystem: true } const attributes = { exclude: ['created_at', 'updated_at'] } const applicationRaw = await ApplicationManager.findOnePopulated(where, attributes, transaction) @@ -317,16 +358,16 @@ async function getApplication (conditions, user, isCLI, transaction) { return application } -const getApplicationEndPoint = async function (conditions, user, isCLI, transaction) { - const application = await getApplication(conditions, user, isCLI, transaction) +const getApplicationEndPoint = async function (conditions, isCLI, transaction) { + const application = await getApplication(conditions, isCLI, transaction) return application } -const _checkForDuplicateName = async function (name, applicationId, userId, transaction) { +const _checkForDuplicateName = async function (name, applicationId, transaction) { if (name) { const where = applicationId - ? { name: name, userId: userId, id: { [Op.ne]: applicationId } } - : { name: name, userId: userId } + ? { name: name, id: { [Op.ne]: applicationId } } + : { name: name } const result = await ApplicationManager.findOne(where, transaction) if (result) { @@ -335,6 +376,10 @@ const _checkForDuplicateName = async function (name, applicationId, userId, tran } } +const getSystemApplicationEndPoint = async function (conditions, isCLI, transaction) { + const application = await getSystemApplication(conditions, isCLI, transaction) + return application +} async function _updateChangeTrackingsAndDeleteMicroservicesByApplicationId (conditions, deleteMicroservices, transaction) { const microservices = await ApplicationManager.findApplicationMicroservices(conditions, transaction) if (!microservices) { @@ -358,10 +403,14 @@ async function _updateChangeTrackingsAndDeleteMicroservicesByApplicationId (cond module.exports = { createApplicationEndPoint: TransactionDecorator.generateTransaction(createApplicationEndPoint), deleteApplicationEndPoint: TransactionDecorator.generateTransaction(deleteApplicationEndPoint), + deleteSystemApplicationEndPoint: TransactionDecorator.generateTransaction(deleteSystemApplicationEndPoint), updateApplicationEndPoint: TransactionDecorator.generateTransaction(updateApplicationEndPoint), patchApplicationEndPoint: TransactionDecorator.generateTransaction(patchApplicationEndPoint), getUserApplicationsEndPoint: TransactionDecorator.generateTransaction(getUserApplicationsEndPoint), + getSystemApplicationsEndPoint: TransactionDecorator.generateTransaction(getSystemApplicationsEndPoint), getAllApplicationsEndPoint: TransactionDecorator.generateTransaction(getAllApplicationsEndPoint), getApplicationEndPoint: TransactionDecorator.generateTransaction(getApplicationEndPoint), - getApplication: getApplication + getSystemApplicationEndPoint: TransactionDecorator.generateTransaction(getSystemApplicationEndPoint), + getApplication: getApplication, + getSystemApplication: getSystemApplication } diff --git a/src/services/application-template-service.js b/src/services/application-template-service.js index 771539bba..df68d89c2 100644 --- a/src/services/application-template-service.js +++ b/src/services/application-template-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -24,20 +24,19 @@ const ApplicationTemplateVariableManager = require('../data/managers/application const TransactionDecorator = require('../decorators/transaction-decorator') const Validator = require('../schemas') -const createApplicationTemplateEndPoint = async function (applicationTemplateData, user, isCLI, transaction) { +const createApplicationTemplateEndPoint = async function (applicationTemplateData, isCLI, transaction) { // Add a name field to pass schema validation using the applicationCreate schema applicationTemplateData.application = { ...applicationTemplateData.application, name: 'validation' } await Validator.validate(applicationTemplateData, Validator.schemas.applicationTemplateCreate) // Remove name before storing delete applicationTemplateData.application.name - await _checkForDuplicateName(applicationTemplateData.name, null, user.id, transaction) + await _checkForDuplicateName(applicationTemplateData.name, null, transaction) const applicationTemplateToCreate = { name: applicationTemplateData.name, description: applicationTemplateData.description, - applicationJSON: JSON.stringify(applicationTemplateData.application), - userId: user.id + applicationJSON: JSON.stringify(applicationTemplateData.application) } const applicationTemplateDataCreate = AppHelper.deleteUndefinedFields(applicationTemplateToCreate) @@ -57,31 +56,30 @@ const createApplicationTemplateEndPoint = async function (applicationTemplateDat } } catch (e) { // If anything failed during creating the application, delete all that was created - await deleteApplicationTemplateEndPoint({ name: applicationTemplate.name }, user, isCLI, transaction) + await deleteApplicationTemplateEndPoint({ name: applicationTemplate.name }, isCLI, transaction) throw e } } -const deleteApplicationTemplateEndPoint = async function (conditions, user, isCLI, transaction) { +const deleteApplicationTemplateEndPoint = async function (conditions, isCLI, transaction) { const whereObj = { - ...conditions, - userId: user.id + ...conditions } const where = AppHelper.deleteUndefinedFields(whereObj) await ApplicationTemplateManager.delete(where, transaction) } -const patchApplicationTemplateEndPoint = async function (applicationTemplateData, conditions, user, isCLI, transaction) { +const patchApplicationTemplateEndPoint = async function (applicationTemplateData, conditions, isCLI, transaction) { await Validator.validate(applicationTemplateData, Validator.schemas.applicationTemplatePatch) - const oldApplicationTemplate = await ApplicationTemplateManager.findOne({ ...conditions, userId: user.id }, transaction) + const oldApplicationTemplate = await ApplicationTemplateManager.findOne({ ...conditions }, transaction) if (!oldApplicationTemplate) { throw new Errors.NotFoundError(ErrorMessages.INVALID_FLOW_ID) } if (applicationTemplateData.name) { - await _checkForDuplicateName(applicationTemplateData.name, oldApplicationTemplate.id, user.id || oldApplicationTemplate.userId, transaction) + await _checkForDuplicateName(applicationTemplateData.name, oldApplicationTemplate.id, transaction) } const applicationTemplate = { @@ -93,24 +91,24 @@ const patchApplicationTemplateEndPoint = async function (applicationTemplateData const where = isCLI ? { id: oldApplicationTemplate.id } - : { id: oldApplicationTemplate.id, userId: user.id } + : { id: oldApplicationTemplate.id } await ApplicationTemplateManager.update(where, updateApplicationTemplateData, transaction) } -const updateApplicationTemplateEndPoint = async function (applicationTemplateData, name, user, isCLI, transaction) { +const updateApplicationTemplateEndPoint = async function (applicationTemplateData, name, isCLI, transaction) { // Add a name field to pass schema validation using the applicationCreate schema applicationTemplateData.application = { ...applicationTemplateData.application, name: 'validation' } await Validator.validate(applicationTemplateData, Validator.schemas.applicationTemplateUpdate) // Remove name before storing delete applicationTemplateData.application.name - const oldApplicationTemplate = await ApplicationTemplateManager.findOne({ name, userId: user.id }, transaction) + const oldApplicationTemplate = await ApplicationTemplateManager.findOne({ name }, transaction) if (!oldApplicationTemplate) { - return createApplicationTemplateEndPoint({ ...applicationTemplateData, name }, user, isCLI, transaction) + return createApplicationTemplateEndPoint({ ...applicationTemplateData, name }, isCLI, transaction) } if (applicationTemplateData.name) { - await _checkForDuplicateName(applicationTemplateData.name, oldApplicationTemplate.id, user.id || oldApplicationTemplate.userId, transaction) + await _checkForDuplicateName(applicationTemplateData.name, oldApplicationTemplate.id, transaction) } const applicationTemplateDBModel = { @@ -122,11 +120,11 @@ const updateApplicationTemplateEndPoint = async function (applicationTemplateDat const updateApplicationTemplateData = AppHelper.deleteUndefinedFields(applicationTemplateDBModel) const where = isCLI ? { id: oldApplicationTemplate.id } - : { id: oldApplicationTemplate.id, userId: user.id } + : { id: oldApplicationTemplate.id } await ApplicationTemplateManager.update(where, updateApplicationTemplateData, transaction) if (applicationTemplateData.variables) { - await _updateVariables(oldApplicationTemplate.id, applicationTemplateData.variables, user, isCLI, transaction) + await _updateVariables(oldApplicationTemplate.id, applicationTemplateData.variables, isCLI, transaction) } return { @@ -148,16 +146,15 @@ const _createVariable = async function (applicationTemplateId, variableData, tra return ApplicationTemplateVariableManager.create({ ...newVariable, applicationTemplateId }, transaction) } -const _updateVariables = async function (applicationTemplateId, variables, user, isCLI, transaction) { +const _updateVariables = async function (applicationTemplateId, variables, isCLI, transaction) { await ApplicationTemplateVariableManager.delete({ applicationTemplateId }, transaction) for (const variableData of variables) { await _createVariable(applicationTemplateId, variableData, transaction) } } -const getUserApplicationTemplatesEndPoint = async function (user, isCLI, transaction) { +const getUserApplicationTemplatesEndPoint = async function (isCLI, transaction) { const application = { - userId: user.id } const attributes = { exclude: ['created_at', 'updated_at'] } @@ -190,10 +187,10 @@ const getAllApplicationTemplatesEndPoint = async function (isCLI, transaction) { } } -async function getApplicationTemplate (conditions, user, isCLI, transaction) { +async function getApplicationTemplate (conditions, isCLI, transaction) { const where = isCLI ? { ...conditions } - : { ...conditions, userId: user.id } + : { ...conditions } const attributes = { exclude: ['created_at', 'updated_at'] } const application = await ApplicationTemplateManager.findOnePopulated(where, attributes, transaction) @@ -203,14 +200,14 @@ async function getApplicationTemplate (conditions, user, isCLI, transaction) { return _buildGetApplicationObj(application) } -const getApplicationTemplateEndPoint = async function (name, user, isCLI, transaction) { - return getApplicationTemplate(name, user, isCLI, transaction) +const getApplicationTemplateEndPoint = async function (name, isCLI, transaction) { + return getApplicationTemplate(name, isCLI, transaction) } -const getApplicationDataFromTemplate = async function (deploymentData, user, isCLI, transaction) { +const getApplicationDataFromTemplate = async function (deploymentData, isCLI, transaction) { await Validator.validate(deploymentData, Validator.schemas.applicationTemplateDeploy) - const applicationTemplateDBObject = await ApplicationTemplateManager.findOnePopulated({ name: deploymentData.name, userId: user.id }, transaction) + const applicationTemplateDBObject = await ApplicationTemplateManager.findOnePopulated({ name: deploymentData.name }, transaction) if (!applicationTemplateDBObject) { throw new Errors.NotFoundError(ErrorMessages.INVALID_APPLICATION_TEMPLATE_NAME, deploymentData.name) } @@ -239,7 +236,7 @@ const getApplicationDataFromTemplate = async function (deploymentData, user, isC } // default values are overwritten by user defined values, and self is always overwritten to the current object - await rvaluesVarSubstition(newApplication, { ...defaultVariablesValues, ...userProvidedVariables, self: newApplication }, user) + await rvaluesVarSubstition(newApplication, { ...defaultVariablesValues, ...userProvidedVariables, self: newApplication }) for (const msvc of newApplication.microservices) { // Send it back as a string for application creation and validation @@ -251,11 +248,11 @@ const getApplicationDataFromTemplate = async function (deploymentData, user, isC return newApplication } -const _checkForDuplicateName = async function (name, applicationId, userId, transaction) { +const _checkForDuplicateName = async function (name, applicationId, transaction) { if (name) { const where = applicationId - ? { name: name, userId: userId, id: { [Op.ne]: applicationId } } - : { name: name, userId: userId } + ? { name: name, id: { [Op.ne]: applicationId } } + : { name: name } const result = await ApplicationTemplateManager.findOne(where, transaction) if (result) { diff --git a/src/services/catalog-service.js b/src/services/catalog-service.js index bc9e6a569..dcd36ac95 100644 --- a/src/services/catalog-service.js +++ b/src/services/catalog-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -24,13 +24,14 @@ const Op = require('sequelize').Op const Validator = require('../schemas/index') const RegistryManager = require('../data/managers/registry-manager') const MicroserviceManager = require('../data/managers/microservice-manager') -const MicroseriveStates = require('../enums/microservice-state') +// const MicroseriveStates = require('../enums/microservice-state') +const ChangeTrackingService = require('./change-tracking-service') -const createCatalogItemEndPoint = async function (data, user, transaction) { +const createCatalogItemEndPoint = async function (data, transaction) { await Validator.validate(data, Validator.schemas.catalogItemCreate) - await _checkForDuplicateName(data.name, { userId: user.id }, transaction) + await _checkForDuplicateName(data.name, null, transaction) await _checkForRestrictedPublisher(data.publisher) - const catalogItem = await _createCatalogItem(data, user, transaction) + const catalogItem = await _createCatalogItem(data, transaction) await _createCatalogImages(data, catalogItem, transaction) await _createCatalogItemInputType(data, catalogItem, transaction) await _createCatalogItemOutputType(data, catalogItem, transaction) @@ -40,7 +41,7 @@ const createCatalogItemEndPoint = async function (data, user, transaction) { } } -const updateCatalogItemEndPoint = async function (id, data, user, isCLI, transaction) { +const updateCatalogItemEndPoint = async function (id, data, isCLI, transaction) { await Validator.validate(data, Validator.schemas.catalogItemUpdate) const where = isCLI @@ -48,8 +49,7 @@ const updateCatalogItemEndPoint = async function (id, data, user, isCLI, transac id: id } : { - id: id, - userId: user.id + id: id } data.id = id @@ -58,17 +58,17 @@ const updateCatalogItemEndPoint = async function (id, data, user, isCLI, transac await _updateCatalogItemIOTypes(data, where, transaction) } -const listCatalogItemsEndPoint = async function (user, isCLI, transaction) { +const listCatalogItemsEndPoint = async function (isCLI, transaction) { const where = isCLI ? {} - : { - [Op.or]: [{ userId: user.id }, { userId: null }], - [Op.or]: [{ category: { [Op.ne]: 'SYSTEM' } }, { category: null }] - } + // : { + // [Op.or]: [{ category: { [Op.ne]: 'SYSTEM' } }, { category: null }] + // } + : {} const attributes = isCLI ? {} - : { exclude: ['userId'] } + : {} const catalogItems = await CatalogItemManager.findAllWithDependencies(where, attributes, transaction) return { @@ -76,18 +76,18 @@ const listCatalogItemsEndPoint = async function (user, isCLI, transaction) { } } -async function getCatalogItem (id, user, isCLI, transaction) { +async function getCatalogItem (id, isCLI, transaction) { const where = isCLI ? { id: id } - : { - id: id, - [Op.or]: [{ userId: user.id }, { userId: null }], - [Op.or]: [{ category: { [Op.ne]: 'SYSTEM' } }, { category: null }] - } + // : { + // id: id, + // [Op.or]: [{ category: { [Op.ne]: 'SYSTEM' } }, { category: null }] + // } + : { id: id } const attributes = isCLI ? {} - : { exclude: ['userId'] } + : {} const item = await CatalogItemManager.findOneWithDependencies(where, attributes, transaction) if (!item) { @@ -96,17 +96,31 @@ async function getCatalogItem (id, user, isCLI, transaction) { return item } -const getCatalogItemEndPoint = async function (id, user, isCLI, transaction) { - return getCatalogItem(id, user, isCLI, transaction) +async function getSystemCatalogItem (id, isCLI, transaction) { + const where = { + id: id, + category: 'SYSTEM' + } + + const attributes = {} + + const item = await CatalogItemManager.findOneWithDependencies(where, attributes, transaction) + if (!item) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_CATALOG_ITEM_ID, id)) + } + return item } -const deleteCatalogItemEndPoint = async function (id, user, isCLI, transaction) { +const getCatalogItemEndPoint = async function (id, isCLI, transaction) { + return getCatalogItem(id, isCLI, transaction) +} + +const deleteCatalogItemEndPoint = async function (id, isCLI, transaction) { const where = isCLI ? { id: id } : { - userId: user.id, id: id } @@ -116,6 +130,11 @@ const deleteCatalogItemEndPoint = async function (id, user, isCLI, transaction) throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_CATALOG_ITEM_DELETE, id)) } + const microservices = await MicroserviceManager.findAllWithStatuses({ catalogItemId: id }, transaction) + if (microservices.length > 0) { + throw new Errors.ValidationError(ErrorMessages.CATALOG_ITEM_IMAGES_IS_FROZEN) + } + const affectedRows = await CatalogItemManager.delete(where, transaction) if (affectedRows === 0) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_CATALOG_ITEM_ID, id)) @@ -123,13 +142,12 @@ const deleteCatalogItemEndPoint = async function (id, user, isCLI, transaction) return affectedRows } -async function getNetworkCatalogItem (transaction) { +async function getNatsCatalogItem (transaction) { return CatalogItemManager.findOne({ - name: 'Networking Tool', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null + name: 'NATs', + category: 'UTILITIES', + publisher: 'Datasance', + registry_id: 1 }, transaction) } @@ -137,29 +155,17 @@ async function getRouterCatalogItem (transaction) { return CatalogItemManager.findOne({ name: DBConstants.ROUTER_CATALOG_NAME, category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null - }, transaction) -} - -async function getProxyCatalogItem (transaction) { - return CatalogItemManager.findOne({ - name: DBConstants.PROXY_CATALOG_NAME, - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null + publisher: 'Datasance', + registry_id: 1 }, transaction) } -async function getPortRouterCatalogItem (transaction) { +async function getDebugCatalogItem (transaction) { return CatalogItemManager.findOne({ - name: DBConstants.PORT_ROUTER_CATALOG_NAME, + name: DBConstants.DEBUG_CATALOG_NAME, category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null + publisher: 'Datasance', + registry_id: 1 }, transaction) } @@ -167,9 +173,8 @@ async function getBluetoothCatalogItem (transaction) { return CatalogItemManager.findOne({ name: 'RESTBlue', category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null + publisher: 'Datasance', + registry_id: 1 }, transaction) } @@ -177,17 +182,16 @@ async function getHalCatalogItem (transaction) { return CatalogItemManager.findOne({ name: 'HAL', category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null + publisher: 'Datasance', + registry_id: 1 }, transaction) } const _checkForDuplicateName = async function (name, item, transaction) { if (name) { - const where = item.id - ? { [Op.or]: [{ userId: item.userId }, { userId: null }], name: name, id: { [Op.ne]: item.id } } - : { [Op.or]: [{ userId: item.userId }, { userId: null }], name: name } + const where = (item && item.id) + ? { name: name, id: { [Op.ne]: item.id } } + : { name: name } const result = await CatalogItemManager.findOne(where, transaction) if (result) { @@ -210,7 +214,7 @@ const _checkIfItemExists = async function (where, transaction) { return item } -const _createCatalogItem = async function (data, user, transaction) { +const _createCatalogItem = async function (data, transaction) { let catalogItem = { name: data.name, description: data.description, @@ -221,8 +225,7 @@ const _createCatalogItem = async function (data, user, transaction) { ramRequired: data.ramRequired, picture: data.picture, isPublic: data.isPublic, - registryId: data.registryId, - userId: user.id + registryId: data.registryId } catalogItem = AppHelper.deleteUndefinedFields(catalogItem) @@ -317,6 +320,12 @@ const _updateCatalogItem = async function (data, where, transaction) { if (!registry) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, data.registryId)) } + const microservices = await MicroserviceManager.findAllWithStatuses({ catalogItemId: data.id }, transaction) + if (microservices.length > 0) { + for (const ms of microservices) { + await MicroserviceManager.updateAndFind({ uuid: ms.uuid }, { registryId: data.registryId }, transaction) + } + } } const item = await _checkIfItemExists(where, transaction) @@ -331,12 +340,13 @@ const _updateCatalogItem = async function (data, where, transaction) { const _updateCatalogItemImages = async function (data, transaction) { if (data.images) { - const microservices = await MicroserviceManager.findAllWithStatuses({ catalogItemId: data.id }, transaction) - for (const ms of microservices) { - if (ms.microserviceStatus.status === MicroseriveStates.RUNNING) { - throw new Errors.ValidationError(ErrorMessages.CATALOG_ITEM_IMAGES_IS_FROZEN) - } - } + // TODO: Rather than not allowing images for running microservices, update changetracking for agent microsevice list so that once catalog item images are updated, the microservices are updated and restarted. + // const microservices = await MicroserviceManager.findAllWithStatuses({ catalogItemId: data.id }, transaction) + // for (const ms of microservices) { + // if (ms.microserviceStatus.status === MicroseriveStates.RUNNING) { + // throw new Errors.ValidationError(ErrorMessages.CATALOG_ITEM_IMAGES_IS_FROZEN) + // } + // } for (const image of data.images) { await CatalogItemImageManager.updateOrCreate({ @@ -348,6 +358,13 @@ const _updateCatalogItemImages = async function (data, transaction) { containerImage: image.containerImage }, transaction) } + const microservices = await MicroserviceManager.findAllWithStatuses({ catalogItemId: data.id }, transaction) + if (microservices.length > 0) { + for (const ms of microservices) { + await MicroserviceManager.updateAndFind({ uuid: ms.uuid }, { rebuild: true }, transaction) + await ChangeTrackingService.update(ms.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + } + } } } @@ -379,10 +396,10 @@ module.exports = { deleteCatalogItemEndPoint: TransactionDecorator.generateTransaction(deleteCatalogItemEndPoint), updateCatalogItemEndPoint: TransactionDecorator.generateTransaction(updateCatalogItemEndPoint), getCatalogItem: getCatalogItem, - getNetworkCatalogItem: getNetworkCatalogItem, + getSystemCatalogItem: getSystemCatalogItem, + getNatsCatalogItem: getNatsCatalogItem, getBluetoothCatalogItem: getBluetoothCatalogItem, getHalCatalogItem: getHalCatalogItem, getRouterCatalogItem: getRouterCatalogItem, - getProxyCatalogItem: getProxyCatalogItem, - getPortRouterCatalogItem: getPortRouterCatalogItem + getDebugCatalogItem: getDebugCatalogItem } diff --git a/src/services/certificate-service.js b/src/services/certificate-service.js new file mode 100644 index 000000000..e0e6b35eb --- /dev/null +++ b/src/services/certificate-service.js @@ -0,0 +1,628 @@ +const TransactionDecorator = require('../decorators/transaction-decorator') +const SecretService = require('./secret-service') +const CertificateManager = require('../data/managers/certificate-manager') +const SecretManager = require('../data/managers/secret-manager') +const Errors = require('../helpers/errors') +const ErrorMessages = require('../helpers/error-messages') +const AppHelper = require('../helpers/app-helper') +const Validator = require('../schemas/index') +const { generateSelfSignedCA, storeCA, generateCertificate } = require('../utils/cert') +const config = require('../config') +const forge = require('node-forge') + +// Helper function to check Kubernetes environment +function checkKubernetesEnvironment () { + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + const isKubernetes = controlPlane && controlPlane.toLowerCase() === 'kubernetes' + if (!isKubernetes) { + throw new Errors.ValidationError(ErrorMessages.NOT_KUBERNETES_ENV) + } +} + +// Helper function to validate CA type +function validateCertType (type) { + if (type === 'k8s-secret') { + checkKubernetesEnvironment() + } else if (type !== 'self-signed' && type !== 'direct') { + throw new Errors.ValidationError(`Invalid CA type: ${type}. Must be one of: self-signed, direct, k8s-secret`) + } +} + +// Parse PEM certificate to extract metadata +function parseCertificate (certPem) { + try { + const cert = forge.pki.certificateFromPem(certPem) + return { + subject: cert.subject.getField('CN') ? cert.subject.getField('CN').value : '', + issuer: cert.issuer.getField('CN') ? cert.issuer.getField('CN').value : '', + validFrom: cert.validity.notBefore, + validTo: cert.validity.notAfter, + serialNumber: cert.serialNumber + } + } catch (error) { + throw new Errors.ValidationError(`Invalid certificate: ${error.message}`) + } +} + +// Helper function to convert months to milliseconds +function monthsToMilliseconds (months) { + // Average month length in milliseconds (30.44 days per month) + const avgMonthInMs = 30.44 * 24 * 60 * 60 * 1000 + return months * avgMonthInMs +} + +// Helper function to handle expiration input +function processExpiration (expiration) { + // If expiration is less than 1000, assume it's in months + // This threshold is chosen because no realistic certificate would expire in less than 1 second + if (expiration && expiration < 1000) { + return monthsToMilliseconds(expiration) + } + // Otherwise, use as-is (assuming milliseconds) + return expiration +} + +async function createCAEndpoint (caData, transaction) { + // Validate input data + const validation = await Validator.validate(caData, Validator.schemas.caCreate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + + // Only process expiration if present (for self-signed) + if (caData.expiration) { + caData.expiration = processExpiration(caData.expiration) + } + // Validate CA type based on environment + validateCertType(caData.type) + + try { + const secretName = caData.type === 'self-signed' ? caData.name : caData.secretName + const existingSecret = await SecretService.getSecretEndpoint(secretName) + if (caData.type === 'self-signed') { + if (existingSecret) { + throw new Errors.ConflictError(`CA with name ${secretName} already exists`) + } + } else { + if (!existingSecret) { + throw new Errors.NotFoundError(`Secret with name ${secretName} does not exist. You must create the secret first.`) + } + // For direct/k8s-secret, check if CA record already exists + const existingCA = await CertificateManager.findCertificateByName(secretName, transaction) + if (existingCA && existingCA.isCA) { + throw new Errors.ConflictError(`CA with name ${secretName} already exists`) + } + } + } catch (error) { + // Only proceed if the error is NotFoundError + if (!(error instanceof Errors.NotFoundError)) { + throw error + } + // For self-signed, NotFoundError is fine (secret doesn't exist yet) + // For direct/k8s-secret, NotFoundError is handled above + } + + let ca + let certDetails + + if (caData.type === 'self-signed') { + ca = await generateSelfSignedCA(caData.subject, caData.expiration) + await storeCA(ca, caData.name) + certDetails = parseCertificate(ca.cert) + } else if (caData.type === 'k8s-secret') { + // Import CA from Kubernetes secret + ca = await require('../utils/cert').getCAFromK8sSecret(caData.secretName) + certDetails = parseCertificate(ca.certificate) + // Store the CA locally with the same name as the secret + const checkedSecret = await SecretManager.findOne({ name: caData.secretName || caData.name }, transaction) + if (!checkedSecret) { + await storeCA({ cert: ca.certificate, key: ca.key }, caData.secretName) + } + } else if (caData.type === 'direct') { + // Load from internal secret + const caObj = await require('../utils/cert').loadCA(caData.secretName) + ca = await require('../utils/cert').getCAFromDirect(caObj) + certDetails = parseCertificate(ca.certificate) + } else { + throw new Errors.ValidationError('Unsupported CA type') + } + + // Get the secret that was just created or referenced + const secret = await SecretManager.findOne({ name: caData.secretName || caData.name }, transaction) + + if (caData.type !== 'k8s-secret') { + // Create certificate record in database + await CertificateManager.createCertificateRecord({ + name: caData.secretName || caData.name, // Use secretName if available, otherwise use provided name + subject: certDetails.subject, + isCA: true, + validFrom: certDetails.validFrom, + validTo: certDetails.validTo, + serialNumber: certDetails.serialNumber, + secretId: secret ? secret.id : null + }, transaction) + } + + return { + name: caData.secretName || caData.name, // Use secretName if available, otherwise use provided name + subject: certDetails.subject, + type: caData.type, + valid_from: certDetails.validFrom, + valid_to: certDetails.validTo + } +} + +async function getCAEndpoint (name, transaction) { + const certRecord = await CertificateManager.findCertificateByName(name, transaction) + + if (!certRecord || !certRecord.isCA) { + throw new Errors.NotFoundError(`CA with name ${name} not found`) + } + + // Get the actual cert data from the secret + const secret = await SecretService.getSecretEndpoint(name) + + if (!secret || secret.type !== 'tls') { + throw new Errors.NotFoundError(`CA with name ${name} not found`) + } + + // Normalize line endings in the certificate and private key + const certificate = normalizeLineEndings(Buffer.from(secret.data['tls.crt'], 'base64').toString()) + const privateKey = normalizeLineEndings(Buffer.from(secret.data['tls.key'], 'base64').toString()) + + return { + name: certRecord.name, + subject: certRecord.subject, + isCA: certRecord.isCA, + validFrom: certRecord.validFrom, + validTo: certRecord.validTo, + serialNumber: certRecord.serialNumber, + data: { + certificate, + privateKey: privateKey + } + } +} + +async function listCAEndpoint (transaction) { + const caRecords = await CertificateManager.findAllCAs(transaction) + + return { + cas: caRecords.map(ca => ({ + name: ca.name, + subject: ca.subject, + valid_from: ca.validFrom, + valid_to: ca.validTo, + days_remaining: ca.getDaysUntilExpiration(), + is_expired: ca.isExpired() + })) + } +} + +async function deleteCAEndpoint (name, transaction) { + const caRecord = await CertificateManager.findCertificateByName(name, transaction) + + if (!caRecord || !caRecord.isCA) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CA_NOT_FOUND, name)) + } + + // Check if this CA has signed certificates + const signedCerts = await CertificateManager.findCertificatesByCA(caRecord.id, transaction) + + if (signedCerts.length > 0) { + throw new Errors.ValidationError(`Cannot delete CA that has signed certificates. Please delete the following certificates first: ${signedCerts.map(cert => cert.name).join(', ')}`) + } + + // Delete certificate record and the secret + await CertificateManager.deleteCertificate(name, transaction) + await SecretService.deleteSecretEndpoint(name, transaction) + + return {} +} + +async function createCertificateEndpoint (certData, transaction) { + // Validate input data + const validation = await Validator.validate(certData, Validator.schemas.certificateCreate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + // Validate CA type based on environment + validateCertType(certData.ca.type) + // Process expiration in months if needed + if (certData.expiration) { + certData.expiration = processExpiration(certData.expiration) + } + + // Check if certificate already exists + try { + const existingSecret = await SecretService.getSecretEndpoint(certData.name) + if (existingSecret) { + throw new Errors.ConflictError(`Certificate with name ${certData.name} already exists`) + } + } catch (error) { + if (!(error instanceof Errors.NotFoundError)) { + throw error + } + } + + // Find signing CA if one is specified + let caRecord = null + if (certData.ca && certData.ca.secretName) { + // Skip CA lookup for self-signed type - it's meant to be self-signed, not signed by another CA + if (certData.ca.type && certData.ca.type.toLowerCase() === 'self-signed') { + // Modify the CA structure to properly indicate self-signed + certData.ca = { type: 'self-signed' } + // Continue with certificate generation + } else { + caRecord = await CertificateManager.findCertificateByName(certData.ca.secretName, transaction) + if (!caRecord || !caRecord.isCA) { + // Log if we're dealing with a k8s-secret type + if (certData.ca.type === 'k8s-secret') { + try { + // Try to directly generate cert with k8s CA - this should invoke getCAFromInput + await generateCertificate({ + name: certData.name, + subject: certData.subject, + hosts: certData.hosts, + expiration: certData.expiration, + ca: certData.ca + }) + + // Get certificate details from newly created secret + const certSecret = await SecretService.getSecretEndpoint(certData.name) + const certPem = Buffer.from(certSecret.data['tls.crt'], 'base64').toString() + const certDetails = parseCertificate(certPem) + + // Find or create the CA record to get its ID + let caId = null + const caRecord = await CertificateManager.findCertificateByName(certData.ca.secretName, transaction) + if (caRecord) { + caId = caRecord.id + } + + // Create certificate record in database + await CertificateManager.createCertificateRecord({ + name: certData.name, + subject: certDetails.subject, + isCA: false, + signedById: caId, + hosts: certData.hosts, + validFrom: certDetails.validFrom, + validTo: certDetails.validTo, + serialNumber: certDetails.serialNumber + }, transaction) + + // Return response with CA name + return { + name: certData.name, + subject: certData.subject, + hosts: certData.hosts, + valid_from: certDetails.validFrom, + valid_to: certDetails.validTo, + ca_name: certData.ca.secretName + } + } catch (error) { + throw error + } + } + throw new Errors.NotFoundError(`CA with name ${certData.ca.secretName} not found`) + } + // Check if CA is expired + if (caRecord.isExpired()) { + throw new Errors.ValidationError(`CA ${certData.ca.secretName} is expired and cannot be used to sign new certificates`) + } + } + } + + // Generate certificate + await generateCertificate({ + name: certData.name, + subject: certData.subject, + hosts: certData.hosts, + expiration: certData.expiration, + ca: certData.ca + }) + + // Get certificate from secret to parse details + const certSecret = await SecretService.getSecretEndpoint(certData.name) + const certPem = Buffer.from(certSecret.data['tls.crt'], 'base64').toString() + const certDetails = parseCertificate(certPem) + + // Create certificate record in database + await CertificateManager.createCertificateRecord({ + name: certData.name, + subject: certDetails.subject, + isCA: false, + signedById: caRecord ? caRecord.id : null, + hosts: certData.hosts, + validFrom: certDetails.validFrom, + validTo: certDetails.validTo, + serialNumber: certDetails.serialNumber + }, transaction) + + return { + name: certData.name, + subject: certData.subject, + hosts: certData.hosts, + valid_from: certDetails.validFrom, + valid_to: certDetails.validTo, + ca_name: caRecord ? caRecord.name : null + } +} + +async function getCertificateEndpoint (name, transaction) { + const certRecord = await CertificateManager.findCertificateByName(name, transaction) + + if (!certRecord) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CERTIFICATE_NOT_FOUND, name)) + } + + // Get the actual cert data from the secret + const secret = await SecretService.getSecretEndpoint(name) + + if (!secret || secret.type !== 'tls') { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CERTIFICATE_NOT_FOUND, name)) + } + + // Get the certificate chain if available + const certChain = await CertificateManager.getCertificateChain(certRecord.id, transaction) + const chainInfo = certChain.length > 1 + ? certChain.slice(1).map(c => ({ name: c.name, subject: c.subject })) + : [] + + // Normalize line endings in the certificate and private key + const certificate = normalizeLineEndings(Buffer.from(secret.data['tls.crt'], 'base64').toString()) + const privateKey = normalizeLineEndings(Buffer.from(secret.data['tls.key'], 'base64').toString()) + + return { + name: certRecord.name, + subject: certRecord.subject, + hosts: certRecord.hosts, + isCA: certRecord.isCA, + validFrom: certRecord.validFrom, + validTo: certRecord.validTo, + serialNumber: certRecord.serialNumber, + caName: certRecord.signingCA ? certRecord.signingCA.name : null, + certificateChain: chainInfo, + daysRemaining: certRecord.getDaysUntilExpiration(), + isExpired: certRecord.isExpired(), + data: { + certificate, + privateKey: privateKey + } + } +} + +async function listCertificatesEndpoint (transaction) { + const certRecords = await CertificateManager.findAllCertificates(transaction) + + return { + certificates: certRecords.map(cert => ({ + name: cert.name, + subject: cert.subject, + hosts: cert.hosts, + isCA: cert.isCA, + validFrom: cert.validFrom, + validTo: cert.validTo, + daysRemaining: cert.getDaysUntilExpiration(), + isExpired: cert.isExpired(), + caName: cert.signingCA ? cert.signingCA.name : null + })) + } +} + +async function deleteCertificateEndpoint (name, transaction) { + const certRecord = await CertificateManager.findCertificateByName(name, transaction) + + if (!certRecord) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CERTIFICATE_NOT_FOUND, name)) + } + + // Check if this is a CA with signed certificates + if (certRecord.isCA) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CERTIFICATE_NOT_FOUND, name)) + } + + // Delete certificate record and the secret + await CertificateManager.deleteCertificate(name, transaction) + await SecretService.deleteSecretEndpoint(name, transaction) + + return {} +} + +// Phase 3: Renewal Implementation +async function renewCertificateEndpoint (name, transaction) { + try { + // First check if certificate exists in database + let certRecord = await CertificateManager.findCertificateByName(name, transaction) + let isNewRecord = false + + // If no certificate record but secret exists, we'll create a new record + if (!certRecord) { + try { + const secret = await SecretManager.findOne({ name, type: 'tls' }, transaction) + if (secret) { + isNewRecord = true + // console.log(`Certificate record not found for ${name}, but secret exists. Will create new record.`) + } else { + throw new Errors.NotFoundError(`Certificate with name ${name} not found`) + } + } catch (error) { + if (error instanceof Errors.NotFoundError) { + throw error + } + throw new Errors.NotFoundError(`Certificate with name ${name} not found: ${error.message}`) + } + } + + // Delete existing secret (if any) - we'll create a new one + try { + await SecretService.deleteSecretEndpoint(name) + } catch (error) { + // Ignore NotFoundError + if (!(error instanceof Errors.NotFoundError)) { + throw error + } + } + + // Prepare renewal data + const renewalData = { + name: name, + subject: certRecord ? certRecord.subject : name, + hosts: certRecord ? certRecord.hosts : null, + isRenewal: true + } + + // Handle signing CA if this certificate was signed by a CA + if (certRecord && certRecord.signedById) { + const signingCA = await CertificateManager.findOne({ id: certRecord.signedById }, transaction) + + if (!signingCA || !signingCA.isCA) { + throw new Errors.NotFoundError(`Signing CA for certificate ${name} not found or is not a valid CA`) + } + + if (signingCA.isExpired()) { + throw new Errors.ValidationError(`CA ${signingCA.name} is expired and cannot be used to renew certificates. Please renew the CA first.`) + } + + renewalData.ca = { + type: 'direct', + secretName: signingCA.name + } + } else { + // Self-signed renewal + renewalData.ca = { + type: 'self-signed' + } + } + + // Generate new certificate + await generateCertificate(renewalData) + + // Get the newly created secret + const secretModel = await SecretManager.findOne({ name }, transaction) + + if (!secretModel) { + throw new Errors.NotFoundError(`Failed to find renewed certificate secret: ${name}`) + } + + // Current date and expiration date + const nowDate = new Date() + const expiryDate = new Date() + expiryDate.setMonth(expiryDate.getMonth() + (certRecord && certRecord.isCA ? 36 : 12)) + + // Use Sequelize transaction for both operations + if (isNewRecord) { + // Create new certificate record + await CertificateManager.create({ + name: name, + subject: renewalData.subject, + hosts: renewalData.hosts, + isCA: renewalData.ca.type === 'self-signed', + validFrom: nowDate, + validTo: expiryDate, + serialNumber: `renewed-${Date.now()}`, + secretId: secretModel.id + }, transaction) + } else { + // Update the existing certificate record + await CertificateManager.update( + { id: certRecord.id }, + { + validFrom: nowDate, + validTo: expiryDate, + secretId: secretModel.id + }, + transaction + ) + } + + // Get the updated certificate record + const updatedCert = await CertificateManager.findCertificateByName(name, transaction) + + if (!updatedCert) { + // If certificate record still doesn't exist, try to create it again with all fields + await CertificateManager.create({ + name: name, + subject: renewalData.subject, + hosts: renewalData.hosts, + isCA: renewalData.ca.type === 'self-signed', + validFrom: nowDate, + validTo: expiryDate, + serialNumber: `renewed-${Date.now()}`, + secretId: secretModel.id + }, transaction) + + // Try to get it again + const newCert = await CertificateManager.findCertificateByName(name, transaction) + if (!newCert) { + throw new Error(`Failed to retrieve or create certificate record for ${name}`) + } + + return { + name: newCert.name, + subject: newCert.subject, + hosts: newCert.hosts, + valid_from: newCert.validFrom, + valid_to: newCert.validTo, + renewed: true + } + } + + return { + name: updatedCert.name, + subject: updatedCert.subject, + hosts: updatedCert.hosts, + valid_from: updatedCert.validFrom, + valid_to: updatedCert.validTo, + renewed: true + } + } catch (error) { + console.error(`Certificate renewal error: ${error.message}`) + throw error + } +} + +// Get certificates expiring soon +async function listExpiringCertificatesEndpoint (days = 30, transaction) { + const expiringCerts = await CertificateManager.findCertificatesForRenewal(days, transaction) + + // Ensure we return an empty array, not null, if no certificates are expiring + return { + certificates: expiringCerts ? expiringCerts.map(cert => ({ + name: cert.name, + subject: cert.subject, + hosts: cert.hosts, + is_ca: cert.isCA, + valid_from: cert.validFrom, + valid_to: cert.validTo, + days_remaining: cert.getDaysUntilExpiration(), + ca_name: cert.signingCA ? cert.signingCA.name : null + })) : [] + } +} + +/** + * Normalizes line endings to Unix style (\n) + * Handles both \r\n and \n cases to ensure consistent output + * @param {string} str - String to normalize + * @returns {string} - String with normalized line endings + */ +function normalizeLineEndings (str) { + // First replace all \r\n with \n + // Then replace any remaining \r with \n + return str.replace(/\r\n/g, '\n').replace(/\r/g, '\n') +} + +module.exports = { + createCAEndpoint: TransactionDecorator.generateTransaction(createCAEndpoint), + getCAEndpoint: TransactionDecorator.generateTransaction(getCAEndpoint), + listCAEndpoint: TransactionDecorator.generateTransaction(listCAEndpoint), + deleteCAEndpoint: TransactionDecorator.generateTransaction(deleteCAEndpoint), + createCertificateEndpoint: TransactionDecorator.generateTransaction(createCertificateEndpoint), + getCertificateEndpoint: TransactionDecorator.generateTransaction(getCertificateEndpoint), + listCertificatesEndpoint: TransactionDecorator.generateTransaction(listCertificatesEndpoint), + deleteCertificateEndpoint: TransactionDecorator.generateTransaction(deleteCertificateEndpoint), + renewCertificateEndpoint: TransactionDecorator.generateTransaction(renewCertificateEndpoint), + listExpiringCertificatesEndpoint: TransactionDecorator.generateTransaction(listExpiringCertificatesEndpoint) +} diff --git a/src/services/change-tracking-service.js b/src/services/change-tracking-service.js index ef6d0c7dc..d7e64aeba 100644 --- a/src/services/change-tracking-service.js +++ b/src/services/change-tracking-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -29,7 +29,9 @@ const events = Object.freeze({ diagnostics: false, isImageSnapshot: false, prune: false, - routerChanged: false + routerChanged: false, + volumeMounts: false, + execSessions: false }, diagnostics: { diagnostics: true @@ -81,6 +83,12 @@ const events = Object.freeze({ }, prune: { prune: true + }, + volumeMounts: { + volumeMounts: true + }, + microserviceExecSessions: { + execSessions: true } }) diff --git a/src/services/config-map-service.js b/src/services/config-map-service.js new file mode 100644 index 000000000..a1aa48c47 --- /dev/null +++ b/src/services/config-map-service.js @@ -0,0 +1,192 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const TransactionDecorator = require('../decorators/transaction-decorator') +const ConfigMapManager = require('../data/managers/config-map-manager') +const MicroserviceManager = require('../data/managers/microservice-manager') +const MicroserviceEnvManager = require('../data/managers/microservice-env-manager') +const ChangeTrackingService = require('./change-tracking-service') +const AppHelper = require('../helpers/app-helper') +const Errors = require('../helpers/errors') +const ErrorMessages = require('../helpers/error-messages') +const Validator = require('../schemas/index') +const VolumeMountService = require('./volume-mount-service') +const VolumeMountingManager = require('../data/managers/volume-mounting-manager') + +async function createConfigMapEndpoint (configMapData, transaction) { + const validation = await Validator.validate(configMapData, Validator.schemas.configMapCreate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + + const existingConfigMap = await ConfigMapManager.findOne({ name: configMapData.name }, transaction) + if (existingConfigMap) { + throw new Errors.ConflictError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_ALREADY_EXISTS, configMapData.name)) + } + + const configMap = await ConfigMapManager.createConfigMap(configMapData.name, configMapData.immutable, configMapData.data, transaction) + return { + id: configMap.id, + name: configMap.name, + immutable: configMap.immutable, + created_at: configMap.created_at, + updated_at: configMap.updated_at + } +} + +async function updateConfigMapEndpoint (configMapName, configMapData, transaction) { + const validation = await Validator.validate(configMapData, Validator.schemas.configMapUpdate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + + const existingConfigMap = await ConfigMapManager.findOne({ name: configMapName }, transaction) + if (!existingConfigMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, configMapName)) + } + + if (existingConfigMap.immutable === true) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_IMMUTABLE, configMapName)) + } + + const configMap = await ConfigMapManager.updateConfigMap(configMapName, configMapData.immutable, configMapData.data, transaction) + await _updateChangeTrackingForFogs(configMapName, transaction) + await _updateMicroservicesUsingConfigMap(configMapName, transaction) + return { + id: configMap.id, + name: configMap.name, + created_at: configMap.created_at, + updated_at: configMap.updated_at + } +} + +async function getConfigMapEndpoint (configMapName, transaction) { + const configMap = await ConfigMapManager.getConfigMap(configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, configMapName)) + } + + return { + id: configMap.id, + name: configMap.name, + data: configMap.data, + immutable: configMap.immutable, + created_at: configMap.created_at, + updated_at: configMap.updated_at + } +} + +async function listConfigMapsEndpoint (transaction) { + const configMaps = await ConfigMapManager.listConfigMaps(transaction) + return { + configMaps: configMaps.map(configMap => ({ + id: configMap.id, + name: configMap.name, + immutable: configMap.immutable, + created_at: configMap.created_at, + updated_at: configMap.updated_at + })) + } +} + +async function deleteConfigMapEndpoint (configMapName, transaction) { + const existingConfigMap = await ConfigMapManager.findOne({ name: configMapName }, transaction) + if (!existingConfigMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, configMapName)) + } + + await ConfigMapManager.deleteConfigMap(configMapName, transaction) + await _deleteVolumeMountsUsingConfigMap(configMapName, transaction) + return {} +} + +async function _deleteVolumeMountsUsingConfigMap (configMapName, transaction) { + const volumeMounts = await VolumeMountingManager.findAll({ configMapName: configMapName }, transaction) + if (volumeMounts.length > 0) { + for (const volumeMount of volumeMounts) { + await VolumeMountService.deleteVolumeMountEndpoint(volumeMount.name, transaction) + } + } +} + +async function _updateChangeTrackingForFogs (configMapName, transaction) { + const configMapVolumeMounts = await VolumeMountingManager.findAll({ configMapName: configMapName }, transaction) + if (configMapVolumeMounts.length > 0) { + for (const configMapVolumeMount of configMapVolumeMounts) { + const volumeMountObj = { + name: configMapVolumeMount.name, + configMapName: configMapName + } + await VolumeMountService.updateVolumeMountEndpoint(configMapVolumeMount.name, volumeMountObj, transaction) + } + } +} + +async function _updateMicroservicesUsingConfigMap (configMapName, transaction) { + // Find all microservice environment variables that use this config map + const envVars = await MicroserviceEnvManager.findAll({ + valueFromConfigMap: { [require('sequelize').Op.like]: `${configMapName}/%` } + }, transaction) + + if (envVars.length === 0) { + return + } + + // Get the updated config map data + const configMap = await ConfigMapManager.getConfigMap(configMapName, transaction) + if (!configMap) { + return + } + + // Group environment variables by microservice UUID + const microserviceEnvMap = new Map() + for (const envVar of envVars) { + if (!microserviceEnvMap.has(envVar.microserviceUuid)) { + microserviceEnvMap.set(envVar.microserviceUuid, []) + } + microserviceEnvMap.get(envVar.microserviceUuid).push(envVar) + } + + // Update each microservice's environment variables and change tracking + for (const [microserviceUuid, envVars] of microserviceEnvMap) { + // Get the microservice to access its iofogUuid + const microservice = await MicroserviceManager.findOne({ uuid: microserviceUuid }, transaction) + if (!microservice) { + continue + } + + // Update each environment variable with the new config map data + for (const envVar of envVars) { + const [configMapNameFromRef, dataKey] = envVar.valueFromConfigMap.split('/') + if (configMapNameFromRef === configMapName && configMap.data[dataKey]) { + // Update the environment variable value with the new config map data + await MicroserviceEnvManager.update( + { id: envVar.id }, + { value: configMap.data[dataKey] }, + transaction + ) + } + } + + // Update change tracking for the microservice's fog node + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + } +} + +module.exports = { + createConfigMapEndpoint: TransactionDecorator.generateTransaction(createConfigMapEndpoint), + updateConfigMapEndpoint: TransactionDecorator.generateTransaction(updateConfigMapEndpoint), + getConfigMapEndpoint: TransactionDecorator.generateTransaction(getConfigMapEndpoint), + listConfigMapsEndpoint: TransactionDecorator.generateTransaction(listConfigMapsEndpoint), + deleteConfigMapEndpoint: TransactionDecorator.generateTransaction(deleteConfigMapEndpoint) +} diff --git a/src/services/config-service.js b/src/services/config-service.js index 323f61e4b..63cab6f67 100644 --- a/src/services/config-service.js +++ b/src/services/config-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/services/controller-service.js b/src/services/controller-service.js index 2e73322f7..79fa63aeb 100644 --- a/src/services/controller-service.js +++ b/src/services/controller-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,6 @@ */ const ioFogTypesManager = require('../data/managers/iofog-type-manager') -const Config = require('../config') const TransactionDecorator = require('../decorators/transaction-decorator') const packageJson = require('../../package') const AppHelper = require('../helpers/app-helper') @@ -35,13 +34,6 @@ const getFogTypes = async function (isCLI, transaction) { } } -const emailActivation = async function (isCLI) { - const emailActivation = await Config.get('Email:ActivationEnabled', false) - return { - isEmailActivationEnabled: emailActivation - } -} - const statusController = async function (isCLI) { let status @@ -57,7 +49,7 @@ const statusController = async function (isCLI) { 'uptimeSec': process.uptime(), versions: { controller: packageJson.version, - ecnViewer: packageJson.dependencies['@iofog/ecn-viewer'] + ecnViewer: packageJson.dependencies['@datasance/ecn-viewer'] } } } @@ -68,7 +60,6 @@ const getVersion = async function (isCLI) { module.exports = { getFogTypes: TransactionDecorator.generateTransaction(getFogTypes), - emailActivation: emailActivation, statusController: statusController, getVersion: getVersion } diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 98b771a75..41ace819c 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -26,9 +26,9 @@ const logger = require('../logger') const FtpClient = require('ftp') const mime = require('mime') -const changeMicroserviceStraceState = async function (uuid, data, user, isCLI, transaction) { +const changeMicroserviceStraceState = async function (uuid, data, isCLI, transaction) { await Validator.validate(data, Validator.schemas.straceStateUpdate) - const microservice = await MicroserviceService.getMicroserviceEndPoint(uuid, user, isCLI, transaction) + const microservice = await MicroserviceService.getMicroserviceEndPoint(uuid, isCLI, transaction) if (microservice.iofogUuid === null) { throw new Errors.ValidationError(ErrorMessages.STRACE_WITHOUT_FOG) } @@ -42,12 +42,12 @@ const changeMicroserviceStraceState = async function (uuid, data, user, isCLI, t await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.diagnostics, transaction) } -const getMicroserviceStraceData = async function (uuid, data, user, isCLI, transaction) { +const getMicroserviceStraceData = async function (uuid, data, isCLI, transaction) { await Validator.validate(data, Validator.schemas.straceGetData) const microserviceWhere = isCLI ? { uuid: uuid } - : { uuid: uuid, userId: user.id } + : { uuid: uuid } const microservice = await MicroserviceManager.findOne(microserviceWhere, transaction) if (!microservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, uuid)) @@ -58,7 +58,7 @@ const getMicroserviceStraceData = async function (uuid, data, user, isCLI, trans throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = Config.get('Diagnostics:DiagnosticDir') || 'diagnostics' + const dir = Config.get('diagnostics.directory') || 'diagnostics' const filePath = dir + '/' + uuid let result = straceData.buffer @@ -75,12 +75,12 @@ const getMicroserviceStraceData = async function (uuid, data, user, isCLI, trans } } -const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, transaction) { +const postMicroserviceStraceDatatoFtp = async function (uuid, data, isCLI, transaction) { await Validator.validate(data, Validator.schemas.stracePostToFtp) const microserviceWhere = isCLI ? { uuid: uuid } - : { uuid: uuid, userId: user.id } + : { uuid: uuid } const microservice = await MicroserviceManager.findOne(microserviceWhere, transaction) if (!microservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, uuid)) @@ -91,7 +91,7 @@ const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = Config.get('Diagnostics:DiagnosticDir') + const dir = Config.get('diagnostics.directory') const filePath = dir + '/' + uuid _createDirectoryIfNotExists(dir) @@ -100,14 +100,13 @@ const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, _deleteFile(filePath) } -const postMicroserviceImageSnapshotCreate = async function (microserviceUuid, user, isCLI, transaction) { +const postMicroserviceImageSnapshotCreate = async function (microserviceUuid, isCLI, transaction) { const where = isCLI ? { uuid: microserviceUuid } : { - uuid: microserviceUuid, - userId: user.id + uuid: microserviceUuid } const microservice = await MicroserviceManager.findOneWithDependencies(where, {}, transaction) @@ -127,14 +126,13 @@ const postMicroserviceImageSnapshotCreate = async function (microserviceUuid, us await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.imageSnapshot, transaction) } -const getMicroserviceImageSnapshot = async function (microserviceUuid, user, isCLI, transaction) { +const getMicroserviceImageSnapshot = async function (microserviceUuid, isCLI, transaction) { const where = isCLI ? { uuid: microserviceUuid } : { - uuid: microserviceUuid, - userId: user.id + uuid: microserviceUuid } const microservice = await MicroserviceManager.findOneWithDependencies(where, {}, transaction) if (!microservice) { diff --git a/src/services/edge-resource-service.js b/src/services/edge-resource-service.js index 360491711..10e943cae 100644 --- a/src/services/edge-resource-service.js +++ b/src/services/edge-resource-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -23,14 +23,14 @@ const TransactionDecorator = require('../decorators/transaction-decorator') const Validator = require('../schemas') const ChangeTrackingService = require('./change-tracking-service') -async function listEdgeResources (user, transaction) { - const edgeResources = await EdgeResourceManager.findAllWithOrchestrationTags({ userId: user.id }, transaction) +async function listEdgeResources () { + const edgeResources = await EdgeResourceManager.findAllWithOrchestrationTags() return edgeResources.map(buildGetObject) } -async function getEdgeResource ({ name, version }, user, transaction) { +async function getEdgeResource ({ name, version }, transaction) { if (version) { - const resource = await EdgeResourceManager.findOneWithOrchestrationTags({ name, version, userId: user.id }, transaction) + const resource = await EdgeResourceManager.findOneWithOrchestrationTags({ name, version }, transaction) if (!resource) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_RESOURCE_NAME_VERSION, name, version)) } @@ -39,7 +39,7 @@ async function getEdgeResource ({ name, version }, user, transaction) { const result = { ...resource.toJSON(), interface: (intrface || { toJSON: () => {} }).toJSON() } return buildGetObject(result) } else { - const resources = await EdgeResourceManager.findAllWithOrchestrationTags({ name, userId: user.id }, transaction) + const resources = await EdgeResourceManager.findAllWithOrchestrationTags({ name }, transaction) if (!resources.length) { return [] } @@ -155,15 +155,14 @@ async function _updateOrchestrationTags (tagArray, edgeResourceModel, transactio } } -async function createEdgeResource (edgeResourceData, user, transaction) { +async function createEdgeResource (edgeResourceData, transaction) { await Validator.validate(edgeResourceData, Validator.schemas.edgeResourceCreate) const { name, description, version, orchestrationTags, interfaceProtocol, display, custom } = edgeResourceData - const existingResource = await EdgeResourceManager.findOne({ name, version, userId: user.id }, transaction) + const existingResource = await EdgeResourceManager.findOne({ name, version }, transaction) if (existingResource) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_RESOURCE_NAME_VERSION, name, version)) } const resourceData = { - userId: user.id, name, description, orchestrationTags, @@ -193,9 +192,9 @@ async function createEdgeResource (edgeResourceData, user, transaction) { return buildGetObject(resource) } -async function updateEdgeResourceEndpoint (edgeResourceData, { name: oldName, version }, user, transaction) { +async function updateEdgeResourceEndpoint (edgeResourceData, { name: oldName, version }, transaction) { await Validator.validate(edgeResourceData, Validator.schemas.edgeResourceUpdate) - const oldData = await EdgeResourceManager.findOne({ name: oldName, version, userId: user.id }, transaction) + const oldData = await EdgeResourceManager.findOne({ name: oldName, version }, transaction) if (!oldData) { if (!edgeResourceData.name) { edgeResourceData.name = oldName @@ -203,14 +202,13 @@ async function updateEdgeResourceEndpoint (edgeResourceData, { name: oldName, ve if (!edgeResourceData.version) { edgeResourceData.version = version } - return createEdgeResource(edgeResourceData, user, transaction) + return createEdgeResource(edgeResourceData, transaction) } if (edgeResourceData.version && oldData.version !== edgeResourceData.version) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.RESOURCE_UPDATE_VERSION_MISMATCH)) } const { name, description, orchestrationTags, interfaceProtocol, display, custom } = edgeResourceData const newData = { - userId: user.id, name, description, orchestrationTags, @@ -227,7 +225,7 @@ async function updateEdgeResourceEndpoint (edgeResourceData, { name: oldName, ve AppHelper.deleteUndefinedFields(newData) if (newData.name && newData.name !== oldData.name) { const newVersion = newData.version ? newData.version : version - const existingResource = await EdgeResourceManager.findOne({ name, version: newVersion, userId: user.id }, transaction) + const existingResource = await EdgeResourceManager.findOne({ name, version: newVersion }, transaction) if (existingResource) { throw new Errors.DuplicatePropertyError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_RESOURCE_NAME_VERSION, name, newVersion)) } @@ -246,8 +244,8 @@ async function updateEdgeResourceEndpoint (edgeResourceData, { name: oldName, ve } } -async function deleteEdgeResource ({ name, version }, user, transaction) { - const resource = await EdgeResourceManager.findOne({ name, version, userId: user.id }, transaction) +async function deleteEdgeResource ({ name, version }, transaction) { + const resource = await EdgeResourceManager.findOne({ name, version }, transaction) if (!resource) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_RESOURCE_NAME_VERSION, name, version)) } @@ -257,15 +255,15 @@ async function deleteEdgeResource ({ name, version }, user, transaction) { await agent.removeTags(tags) await ChangeTrackingService.update(agent.uuid, ChangeTrackingService.events.edgeResources, transaction) } - await EdgeResourceManager.delete({ name, version, userId: user.id }, transaction) + await EdgeResourceManager.delete({ name, version }, transaction) } -async function linkEdgeResource ({ name, version }, uuid, user, transaction) { - const resource = await EdgeResourceManager.findOne({ name, version, userId: user.id }, transaction) +async function linkEdgeResource ({ name, version }, uuid, transaction) { + const resource = await EdgeResourceManager.findOne({ name, version }, transaction) if (!resource) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_RESOURCE_NAME_VERSION, name, version)) } - const agent = await FogManager.findOne({ uuid, userId: user.id }, transaction) + const agent = await FogManager.findOne({ uuid }, transaction) if (!agent) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_AGENT_NAME, uuid)) } @@ -276,12 +274,12 @@ async function linkEdgeResource ({ name, version }, uuid, user, transaction) { await ChangeTrackingService.update(agent.uuid, ChangeTrackingService.events.edgeResources, transaction) } -async function unlinkEdgeResource ({ name, version }, uuid, user, transaction) { - const resource = await EdgeResourceManager.findOne({ name, version, userId: user.id }, transaction) +async function unlinkEdgeResource ({ name, version }, uuid, transaction) { + const resource = await EdgeResourceManager.findOne({ name, version }, transaction) if (!resource) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_RESOURCE_NAME_VERSION, name, version)) } - const agent = await FogManager.findOne({ uuid, userId: user.id }, transaction) + const agent = await FogManager.findOne({ uuid }, transaction) if (!agent) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_AGENT_NAME, uuid)) } diff --git a/src/services/email-activation-code-service.js b/src/services/email-activation-code-service.js deleted file mode 100644 index 0c62a8003..000000000 --- a/src/services/email-activation-code-service.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const EmailActivationCodeManager = require('../data/managers/email-activation-code-manager') -const AppHelper = require('../helpers/app-helper') -const ErrorMessages = require('../helpers/error-messages') - -const generateActivationCode = async function (transaction) { - while (true) { - const newActivationCode = AppHelper.generateRandomString(16) - const exists = await EmailActivationCodeManager.getByActivationCode(newActivationCode, transaction) - if (!exists) { - const activationCodeExpiryTime = new Date().getTime() + ((60 * 60 * 24 * 3) * 1000) - return { - activationCode: newActivationCode, - expirationTime: activationCodeExpiryTime - } - } - } -} - -const saveActivationCode = async function (userId, activationCodeData, transaction) { - const activationCode = activationCodeData.activationCode - const expirationTime = activationCodeData.expirationTime - - try { - const code = await EmailActivationCodeManager.createActivationCode(userId, activationCode, expirationTime, transaction) - return code - } catch (errMsg) { - throw new Error(ErrorMessages.UNABLE_TO_CREATE_ACTIVATION_CODE) - } -} - -const verifyActivationCode = async function (activationCode, transaction) { - try { - const value = await EmailActivationCodeManager.verifyActivationCode(activationCode, transaction) - return value - } catch (errMsg) { - throw new Error(ErrorMessages.UNABLE_TO_GET_ACTIVATION_CODE) - } -} - -const deleteActivationCode = async function (activationCode, transaction) { - return EmailActivationCodeManager.delete({ - activationCode: activationCode - }, transaction) -} - -module.exports = { - generateActivationCode: generateActivationCode, - saveActivationCode: saveActivationCode, - verifyActivationCode: verifyActivationCode, - deleteActivationCode: deleteActivationCode -} diff --git a/src/services/event-service.js b/src/services/event-service.js new file mode 100644 index 000000000..08dbc1015 --- /dev/null +++ b/src/services/event-service.js @@ -0,0 +1,697 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const EventManager = require('../data/managers/event-manager') +const config = require('../config') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const Validator = require('../schemas') +const TransactionDecorator = require('../decorators/transaction-decorator') + +/** + * Extract resource type from URL path + * @param {string} path - URL path + * @returns {string|null} Resource type or null + */ +function extractResourceType (path) { + if (!path) return null + + // Resource type mapping based on URL patterns + const resourcePatterns = [ + { pattern: /^\/api\/v3\/iofog/, type: 'agent' }, + { pattern: /^\/api\/v3\/microservices/, type: 'microservice' }, + { pattern: /^\/api\/v3\/applications/, type: 'application' }, + { pattern: /^\/api\/v3\/agent/, type: 'agent' }, + { pattern: /^\/api\/v3\/config/, type: 'config' }, + { pattern: /^\/api\/v3\/secrets/, type: 'secret' }, + { pattern: /^\/api\/v3\/services/, type: 'service' }, + { pattern: /^\/api\/v3\/certificates/, type: 'certificate' }, + { pattern: /^\/api\/v3\/tunnels/, type: 'tunnel' }, + { pattern: /^\/api\/v3\/routes/, type: 'routing' }, + { pattern: /^\/api\/v3\/router/, type: 'router' }, + { pattern: /^\/api\/v3\/registries/, type: 'registry' }, + { pattern: /^\/api\/v3\/volumeMounts/, type: 'volumeMount' }, + { pattern: /^\/api\/v3\/configMaps/, type: 'configMap' }, + { pattern: /^\/api\/v3\/edgeResources/, type: 'edgeResource' }, + { pattern: /^\/api\/v3\/diagnostics/, type: 'diagnostics' }, + { pattern: /^\/api\/v3\/flows/, type: 'application' }, + { pattern: /^\/api\/v3\/applicationTemplates/, type: 'applicationTemplate' }, + { pattern: /^\/api\/v3\/catalog/, type: 'catalog' }, + { pattern: /^\/api\/v3\/controller/, type: 'controller' }, + { pattern: /^\/api\/v3\/users/, type: 'user' }, + { pattern: /^\/api\/v3\/capabilities/, type: 'capabilities' }, + { pattern: /^\/api\/v3\/events/, type: 'event' } + ] + + for (const { pattern, type } of resourcePatterns) { + if (pattern.test(path)) { + return type + } + } + + return null +} + +/** + * Extract resource ID from URL path, params, or body + * @param {object} req - Express request object + * @returns {string|null} Resource ID or null + */ +function extractResourceId (req) { + if (!req) return null + + // Try path parameters first (most common) + if (req.params) { + // Common parameter names + const paramNames = ['uuid', 'id', 'name', 'key', 'appName', 'microserviceUuid'] + for (const paramName of paramNames) { + if (req.params[paramName]) { + return req.params[paramName] + } + } + // Check for versioned resources (name:version) + if (req.params.name && req.params.version) { + return `${req.params.name}:${req.params.version}` + } + } + + // Try query parameters + if (req.query) { + if (req.query.uuid) return req.query.uuid + if (req.query.id) return req.query.id + if (req.query.name) return req.query.name + } + + // Try request body (for POST/PUT/PATCH) + if (req.body) { + if (req.body.uuid) return req.body.uuid + if (req.body.id) return req.body.id + if (req.body.name) return req.body.name + } + + return null +} + +/** + * Extract username from Keycloak JWT token + * @param {string} token - Bearer token string (with or without "Bearer " prefix) + * @returns {string|null} Username or null + */ +function extractUsernameFromToken (token) { + if (!token) return null + + try { + // Remove "Bearer " prefix if present + const cleanToken = token.replace(/^Bearer\s+/i, '') + const tokenParts = cleanToken.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + return payload.preferred_username || null + } + } catch (error) { + logger.debug('Failed to extract username from token:', error) + } + + return null +} + +/** + * Extract IPv4 address from request, converting IPv6-mapped IPv4 addresses + * @param {object} req - Express request object + * @returns {string|null} IPv4 address or null + */ +function extractIPv4Address (req) { + if (!req) return null + + let ipAddress = null + + // Try req.ip first (Express sets this) + if (req.ip) { + ipAddress = req.ip + } else if (req.connection && req.connection.remoteAddress) { + ipAddress = req.connection.remoteAddress + } else if (req.socket && req.socket.remoteAddress) { + ipAddress = req.socket.remoteAddress + } + + if (!ipAddress) { + return null + } + + // Convert IPv6-mapped IPv4 address (::ffff:127.0.0.1) to IPv4 (127.0.0.1) + if (ipAddress.startsWith('::ffff:')) { + return ipAddress.substring(7) // Remove '::ffff:' prefix + } + + // Filter out pure IPv6 addresses (like ::1) + if (ipAddress.includes(':')) { + return null + } + + // Return IPv4 address as-is + return ipAddress +} + +/** + * Sanitize URL path by removing sensitive query parameters + * Prevents storing tokens and other sensitive data in audit logs + * @param {string} urlPath - Full URL path with query string + * @returns {string} Sanitized path without sensitive query parameters + */ +function sanitizeEndpointPath (urlPath) { + if (!urlPath) return urlPath + + try { + // List of sensitive query parameters to remove + const sensitiveParams = ['token', 'access_token', 'refresh_token', 'api_key', 'apikey', 'password', 'secret'] + + // Split URL into path and query string + const [path, queryString] = urlPath.split('?') + + // If no query string, return path as-is + if (!queryString) { + return path + } + + // Parse query parameters + const params = new URLSearchParams(queryString) + const sanitizedParams = new URLSearchParams() + + // Copy only non-sensitive parameters + for (const [key, value] of params.entries()) { + if (!sensitiveParams.includes(key.toLowerCase())) { + sanitizedParams.append(key, value) + } + } + + // Reconstruct URL + const sanitizedQuery = sanitizedParams.toString() + return sanitizedQuery ? `${path}?${sanitizedQuery}` : path + } catch (error) { + // If parsing fails, return path without query string as fallback + logger.debug('Failed to sanitize endpoint path, removing query string:', error) + const [path] = urlPath.split('?') + return path + } +} + +/** + * Extract actor ID from request (user or agent) + * @param {object} req - Express request object + * @returns {string|null} Actor ID (username or fog UUID) or null + */ +function extractActorId (req) { + if (!req) return null + + // Check if it's an agent endpoint + if (req.path && req.path.startsWith('/api/v3/agent/')) { + // Extract fog UUID from JWT token + try { + const authHeader = req.headers.authorization + if (authHeader) { + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() === 'bearer' && token) { + const tokenParts = token.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + return payload.sub || null + } + } + } + } catch (error) { + logger.debug('Failed to extract fog UUID from token:', error) + } + return null + } + + // Special handling for user authentication endpoints + if (req.path && req.path.startsWith('/api/v3/user/')) { + // For login endpoint: extract from req.body.email + if (req.path === '/api/v3/user/login' && req.body && req.body.email) { + return req.body.email + } + + // For refresh endpoint: extract from req.body.refreshToken + if (req.path === '/api/v3/user/refresh' && req.body && req.body.refreshToken) { + try { + const tokenParts = req.body.refreshToken.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + return payload.preferred_username || payload.email || payload.sub || null + } + } catch (error) { + logger.debug('Failed to extract username from refresh token:', error) + } + } + + // For logout endpoint: extract from access token in headers or kauth + if (req.path === '/api/v3/user/logout') { + // Try Keycloak middleware first + try { + if (req.kauth && req.kauth.grant && req.kauth.grant.access_token && + req.kauth.grant.access_token.content && req.kauth.grant.access_token.content.preferred_username) { + return req.kauth.grant.access_token.content.preferred_username + } + } catch (error) { + logger.debug('Failed to extract username from Keycloak middleware:', error) + } + + // Fallback: extract from Authorization header + try { + const authHeader = req.headers.authorization + if (authHeader) { + const username = extractUsernameFromToken(authHeader) + if (username) { + return username + } + } + } catch (error) { + logger.debug('Failed to extract username from access token:', error) + } + } + } + + // User endpoint - try Keycloak middleware first (for HTTP requests) + try { + if (req.kauth && req.kauth.grant && req.kauth.grant.access_token && + req.kauth.grant.access_token.content && req.kauth.grant.access_token.content.preferred_username) { + return req.kauth.grant.access_token.content.preferred_username + } + } catch (error) { + logger.debug('Failed to extract username from Keycloak middleware:', error) + } + + // Fallback: extract from token directly (for WebSocket connections or other endpoints) + try { + const authHeader = req.headers.authorization + if (authHeader) { + const username = extractUsernameFromToken(authHeader) + if (username) { + return username + } + } + } catch (error) { + logger.debug('Failed to extract username from token:', error) + } + + return null +} + +/** + * Determine status from HTTP status code + * @param {number} statusCode - HTTP status code + * @returns {string} 'SUCCESS' or 'FAILED' + */ +function determineStatus (statusCode) { + if (!statusCode) return 'FAILED' + return (statusCode >= 200 && statusCode < 300) ? 'SUCCESS' : 'FAILED' +} + +/** + * Determine status from WebSocket close code + * @param {number} closeCode - WebSocket close code + * @returns {string} 'SUCCESS' or 'FAILED' + */ +function determineWsStatus (closeCode) { + // Normal closure (1000) and going away (1001) are considered success + // Other codes indicate errors + if (closeCode === 1000 || closeCode === 1001) { + return 'SUCCESS' + } + return 'FAILED' +} + +/** + * Create an event record + * @param {object} eventData - Event data object + * @param {object} transaction - Database transaction + * @returns {Promise} Created event + */ +async function createEvent (eventData, transaction) { + const eventRecord = { + timestamp: eventData.timestamp || Date.now(), + eventType: eventData.eventType, + endpointType: eventData.endpointType, + actorId: eventData.actorId || null, + method: eventData.method || null, + resourceType: eventData.resourceType || null, + resourceId: eventData.resourceId || null, + endpointPath: eventData.endpointPath, + ipAddress: eventData.ipAddress || null, + status: eventData.status, + statusCode: eventData.statusCode || null, + statusMessage: eventData.statusMessage || null, + requestId: eventData.requestId || null + } + + return EventManager.create(eventRecord, transaction) +} + +/** + * Create event from HTTP request/response + * @param {object} req - Express request object + * @param {object} res - Express response object + * @param {number} startTime - Request start timestamp + * @returns {Promise} + */ +async function createHttpEvent (req, res, startTime) { + // Check if auditing is enabled + // Use config.get() which properly parses boolean strings from env vars + const auditEnabled = config.get('settings.eventAuditEnabled', true) + if (!auditEnabled) { + return + } + + // Only track non-GET methods + if (req.method === 'GET') { + return + } + + // Don't audit DELETE on events endpoint (handled explicitly in controller) + if (req.method === 'DELETE' && req.path === '/api/v3/events') { + return + } + + const captureIp = config.get('settings.eventCaptureIpAddress', true) + const endpointType = req.path.startsWith('/api/v3/agent/') ? 'agent' : 'user' + const actorId = extractActorId(req) + const resourceType = extractResourceType(req.path) + const resourceId = extractResourceId(req) + const status = determineStatus(res.statusCode) + + const eventData = { + timestamp: startTime, + eventType: 'HTTP', + endpointType: endpointType, + actorId: actorId, + method: req.method, + resourceType: resourceType, + resourceId: resourceId, + endpointPath: req.path, + ipAddress: captureIp ? extractIPv4Address(req) : null, + status: status, + statusCode: res.statusCode, + statusMessage: status === 'SUCCESS' ? 'Success' : `HTTP ${res.statusCode}`, + requestId: req.id || null + } + + // Use fake transaction for non-blocking event creation + await createEvent(eventData, { fakeTransaction: true }).catch(err => { + logger.error('Event logging failed (non-blocking):', err) + }) +} + +/** + * Create WebSocket connection event + * @param {object} connectionData - Connection data + * @returns {Promise} + */ +async function createWsConnectEvent (connectionData) { + // Check if auditing is enabled + // Use config.get() which properly parses boolean strings from env vars + const auditEnabled = config.get('settings.eventAuditEnabled', true) + if (!auditEnabled) { + return + } + + const captureIp = config.get('settings.eventCaptureIpAddress', true) + const endpointType = connectionData.endpointType || 'user' + // Sanitize path to remove sensitive query parameters (e.g., token) + const sanitizedPath = sanitizeEndpointPath(connectionData.path) + const resourceType = extractResourceType(sanitizedPath) + + const eventData = { + timestamp: connectionData.timestamp || Date.now(), + eventType: 'WS_CONNECT', + endpointType: endpointType, + actorId: connectionData.actorId || null, + method: 'WS', + resourceType: resourceType, + resourceId: connectionData.resourceId || null, + endpointPath: sanitizedPath, + ipAddress: captureIp ? (connectionData.ipAddress || null) : null, + status: 'SUCCESS', + statusCode: null, + statusMessage: 'WebSocket connection established', + requestId: null + } + + // Use fake transaction for non-blocking event creation + await createEvent(eventData, { fakeTransaction: true }).catch(err => { + logger.error('WebSocket connect event logging failed (non-blocking):', err) + }) +} + +/** + * Create WebSocket disconnection event + * @param {object} connectionData - Connection data + * @returns {Promise} + */ +async function createWsDisconnectEvent (connectionData) { + // Check if auditing is enabled + // Use config.get() which properly parses boolean strings from env vars + const auditEnabled = config.get('settings.eventAuditEnabled', true) + if (!auditEnabled) { + return + } + + const captureIp = config.get('settings.eventCaptureIpAddress', true) + const endpointType = connectionData.endpointType || 'user' + // Sanitize path to remove sensitive query parameters (e.g., token) + const sanitizedPath = sanitizeEndpointPath(connectionData.path) + const resourceType = extractResourceType(sanitizedPath) + const status = determineWsStatus(connectionData.closeCode) + + const eventData = { + timestamp: connectionData.timestamp || Date.now(), + eventType: 'WS_DISCONNECT', + endpointType: endpointType, + actorId: connectionData.actorId || null, + method: 'WS', + resourceType: resourceType, + resourceId: connectionData.resourceId || null, + endpointPath: sanitizedPath, + ipAddress: captureIp ? (connectionData.ipAddress || null) : null, + status: status, + statusCode: connectionData.closeCode || null, + statusMessage: status === 'SUCCESS' ? 'WebSocket connection closed normally' : `WebSocket closed with code ${connectionData.closeCode}`, + requestId: null + } + + // Use fake transaction for non-blocking event creation + await createEvent(eventData, { fakeTransaction: true }).catch(err => { + logger.error('WebSocket disconnect event logging failed (non-blocking):', err) + }) +} + +/** + * Parse time from query parameter (Unix timestamp or ISO 8601) + * @param {string|number} timeValue - Time value + * @returns {number|null} Unix timestamp in milliseconds or null + */ +function parseTime (timeValue) { + if (timeValue === undefined || timeValue === null || timeValue === '') return null + + if (typeof timeValue === 'number') { + return timeValue < 10000000000 ? timeValue * 1000 : timeValue + } + + if (typeof timeValue === 'string' && /^\d+$/.test(timeValue)) { + const timestamp = parseInt(timeValue) + return timestamp < 10000000000 ? timestamp * 1000 : timestamp + } + + try { + const date = new Date(timeValue) + if (!isNaN(date.getTime())) { + return date.getTime() + } + } catch (error) { + // Ignore parsing errors + } + + return null +} + +/** + * Normalize event payloads before returning to clients + * Ensures timestamp is always a number + * @param {object} event - Sequelize event instance + * @returns {object} normalized plain object + */ +function normalizeEventForResponse (event) { + const json = typeof event.toJSON === 'function' ? event.toJSON() : { ...event } + + if (json.timestamp !== undefined && json.timestamp !== null) { + const numericTimestamp = Number(json.timestamp) + if (!Number.isNaN(numericTimestamp)) { + json.timestamp = numericTimestamp + } + } + + return json +} + +/** + * List events with filters and pagination + * @param {object} params - Parameters containing raw query object + * @param {object} context - Optional context (req, user, etc.) + * @returns {Promise} Events list with pagination info + */ +async function listEvents (params = {}, context = {}, transaction) { + const query = params.query || {} + await Validator.validate(query, Validator.schemas.eventListQuery) + + const filters = {} + + if (query.startTime !== undefined) { + const startTime = parseTime(query.startTime) + if (startTime === null) { + throw new Errors.ValidationError('Invalid startTime format. Use Unix timestamp (seconds or milliseconds) or ISO 8601 format (e.g., 2023-10-01T12:00:00Z)') + } + filters.startTime = startTime + } + + if (query.endTime !== undefined) { + const endTime = parseTime(query.endTime) + if (endTime === null) { + throw new Errors.ValidationError('Invalid endTime format. Use Unix timestamp (seconds or milliseconds) or ISO 8601 format (e.g., 2023-10-01T12:00:00Z)') + } + filters.endTime = endTime + } + + if (filters.startTime && filters.endTime && filters.startTime > filters.endTime) { + throw new Errors.ValidationError('startTime must be before or equal to endTime') + } + + if (query.endpointType) { + filters.endpointType = query.endpointType + } + + if (query.resourceType) { + filters.resourceType = query.resourceType + } + + if (query.status) { + filters.status = query.status + } + + if (query.method) { + filters.method = Array.isArray(query.method) ? query.method : [query.method] + } + + if (query.actorId) { + filters.actorId = query.actorId + } + + if (query.eventType) { + filters.eventType = query.eventType + } + + let limit = 200 + if (query.limit !== undefined && query.limit !== null && query.limit !== '') { + const parsedLimit = parseInt(query.limit) + if (!isNaN(parsedLimit) && parsedLimit > 0) { + limit = Math.min(parsedLimit, 1000) + } + } + + let offset = 0 + if (query.offset !== undefined && query.offset !== null && query.offset !== '') { + const parsedOffset = parseInt(query.offset) + if (!isNaN(parsedOffset) && parsedOffset >= 0) { + offset = parsedOffset + } + } + + filters.limit = limit + filters.offset = offset + + const result = await EventManager.findAllWithFilters(filters, transaction) + + return { + events: result.events.map(normalizeEventForResponse), + total: result.total, + limit: result.limit, + offset: result.offset + } +} + +/** + * Delete events based on retention configuration + * @param {object} params - Parameters containing days + * @param {object} context - Additional context (req, etc.) + * @returns {Promise} Deletion summary + */ +async function deleteEvents (params = {}, context = {}, transaction) { + const body = params.body || {} + await Validator.validate(body, Validator.schemas.eventDeleteRequest) + + const { days } = body + const request = context.req || {} + + const cutoffTimestamp = days > 0 ? Date.now() - (days * 24 * 60 * 60 * 1000) : null + const deletedCount = await EventManager.deleteEventsOlderThanDays(days, transaction) + + setImmediate(async () => { + try { + // Use config.get() which properly parses boolean strings from env vars + const captureIp = config.get('settings.eventCaptureIpAddress', true) + const endpointType = request.path && request.path.startsWith('/api/v3/agent/') ? 'agent' : 'user' + const actorId = extractActorId(request) + + await createEvent({ + timestamp: Date.now(), + eventType: 'HTTP', + endpointType, + actorId, + method: 'DELETE', + resourceType: 'event', + resourceId: null, + endpointPath: '/api/v3/events', + ipAddress: captureIp ? extractIPv4Address(request) : null, + status: 'SUCCESS', + statusCode: 200, + statusMessage: days === 0 ? `Deleted all ${deletedCount} events` : `Deleted ${deletedCount} events older than ${days} days`, + requestId: request.id || null + }, { fakeTransaction: true }).catch(err => { + logger.error('Failed to create DELETE events audit record (non-blocking):', err) + }) + } catch (error) { + logger.error('Error creating DELETE events audit record (non-blocking):', error) + } + }) + + return { + deletedCount, + deletedBefore: cutoffTimestamp ? new Date(cutoffTimestamp).toISOString() : null, + deletedAt: new Date().toISOString(), + deletedAll: days === 0 + } +} + +module.exports = { + extractResourceType, + extractResourceId, + extractActorId, + extractUsernameFromToken, + extractIPv4Address, + determineStatus, + determineWsStatus, + createEvent: TransactionDecorator.generateTransaction(createEvent), + createHttpEvent, + createWsConnectEvent, + createWsDisconnectEvent, + listEvents: TransactionDecorator.generateTransaction(listEvents), + deleteEvents: TransactionDecorator.generateTransaction(deleteEvents) +} diff --git a/src/services/iofog-access-token-service.js b/src/services/iofog-access-token-service.js deleted file mode 100644 index db06da1e4..000000000 --- a/src/services/iofog-access-token-service.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const AppHelper = require('../helpers/app-helper') -const FogAccessTokenManager = require('../data/managers/iofog-access-token-manager') - -const Config = require('../config') - -const generateAccessToken = async function (transaction) { - while (true) { - const newAccessToken = AppHelper.generateRandomString(16) - const exists = await FogAccessTokenManager.findOne({ - token: newAccessToken - }, transaction) - if (!exists) { - const accessTokenExpiryTime = Date.now() + Config.get('Settings:FogTokenExpirationIntervalSeconds') * 1000 - return { - token: newAccessToken, - expirationTime: accessTokenExpiryTime - } - } - } -} - -async function updateAccessToken (fogUuid, newAccessToken, transaction) { - return FogAccessTokenManager.updateOrCreate({ - iofogUuid: fogUuid - }, { - iofogUuid: fogUuid, - token: newAccessToken.token, - expirationTime: newAccessToken.expirationTime - }, transaction) -} - -async function all (transaction) { - return FogAccessTokenManager.findAll(null, transaction) -} - -module.exports = { - generateAccessToken, - updateAccessToken, - all -} diff --git a/src/services/iofog-key-service.js b/src/services/iofog-key-service.js new file mode 100644 index 000000000..d34927c99 --- /dev/null +++ b/src/services/iofog-key-service.js @@ -0,0 +1,130 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const crypto = require('crypto') +const FogPublicKeyManager = require('../data/managers/iofog-public-key-manager') +const FogUsedTokenManager = require('../data/managers/fog-used-token-manager') +const SecretHelper = require('../helpers/secret-helper') +const jose = require('jose') + +/** + * Generate Ed25519 key pair and return as JWK strings + * @returns {Object} Object containing publicKey and privateKey as base64 encoded JWK strings + */ +const generateKeyPair = async function (transaction) { + // Generate Ed25519 key pair + const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519') + + // Convert to JWK format + const publicKeyJwk = publicKey.export({ format: 'jwk' }) + const privateKeyJwk = privateKey.export({ format: 'jwk' }) + + // Convert JWK to base64 encoded single line strings + const publicKeyBase64 = Buffer.from(JSON.stringify(publicKeyJwk)).toString('base64') + const privateKeyBase64 = Buffer.from(JSON.stringify(privateKeyJwk)).toString('base64') + + return { + publicKey: publicKeyBase64, + privateKey: privateKeyBase64 + } +} + +/** + * Store public key for a fog node + * @param {string} fogUuid - UUID of the fog node + * @param {string} publicKey - Public key as base64 encoded JWK string + * @param {Object} transaction - Sequelize transaction + * @returns {Promise} Promise resolving to the stored public key + */ +const storePublicKey = async function (fogUuid, publicKey, transaction) { + // Encrypt the public key using SecretHelper for better security and database compatibility + const encryptedPublicKey = await SecretHelper.encryptSecret(publicKey, fogUuid) + + // Store the encrypted public key + return FogPublicKeyManager.updateOrCreate(fogUuid, encryptedPublicKey, transaction) +} + +/** + * Get public key for a fog node + * @param {string} fogUuid - UUID of the fog node + * @param {Object} transaction - Sequelize transaction + * @returns {Promise} Promise resolving to the public key as base64 encoded JWK string + */ +const getPublicKey = async function (fogUuid, transaction) { + // Get the encrypted public key + const fogPublicKey = await FogPublicKeyManager.findByFogUuid(fogUuid, transaction) + + if (!fogPublicKey) { + return null + } + + // Decrypt the public key using SecretHelper for better security and database compatibility + return SecretHelper.decryptSecret(fogPublicKey.publicKey, fogUuid) +} + +/** + * Verify a JWT signed by a fog node + * @param {string} token - JWT token + * @param {string} fogUuid - UUID of the fog node + * @param {Object} transaction - Sequelize transaction + * @returns {Promise} Promise resolving to the verified JWT payload + */ +const verifyJWT = async function (token, fogUuid, transaction) { + try { + // Get the public key for the fog node + const publicKeyBase64 = await getPublicKey(fogUuid, transaction) + + if (!publicKeyBase64) { + throw new Error('Public key not found for fog node') + } + + // Convert base64 JWK string to JWK object + const publicKeyJwk = JSON.parse(Buffer.from(publicKeyBase64, 'base64').toString()) + + // Convert JWK to crypto key + const publicKey = crypto.createPublicKey({ + key: publicKeyJwk, + format: 'jwk' + }) + + // Verify the JWT using jose + const { payload } = await jose.jwtVerify(token, publicKey, { + algorithms: ['EdDSA'] + }) + + // Check if JTI is already used + const isUsed = await FogUsedTokenManager.isJtiUsed(payload.jti, transaction) + if (isUsed) { + throw new Error('JWT already used') + } + + // Store the JTI + await FogUsedTokenManager.storeJti(payload.jti, fogUuid, payload.exp, transaction) + + return payload + } catch (error) { + throw new Error(`JWT verification failed: ${error.message}`) + } +} + +async function all (transaction) { + return FogPublicKeyManager.findAll(null, transaction) +} + +module.exports = { + generateKeyPair, + storePublicKey, + getPublicKey, + verifyJWT, + all +} diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 0187cde3a..5fe1e23f5 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,8 +11,8 @@ * */ -const request = require('request-promise') - +const config = require('../config') +const fs = require('fs') const TransactionDecorator = require('../decorators/transaction-decorator') const AppHelper = require('../helpers/app-helper') const FogManager = require('../data/managers/iofog-manager') @@ -26,30 +26,254 @@ const HWInfoManager = require('../data/managers/hw-info-manager') const USBInfoManager = require('../data/managers/usb-info-manager') const CatalogService = require('./catalog-service') const MicroserviceManager = require('../data/managers/microservice-manager') +const ApplicationManager = require('../data/managers/application-manager') const TagsManager = require('../data/managers/tags-manager') const MicroserviceService = require('./microservices-service') const EdgeResourceService = require('./edge-resource-service') -const config = require('../config') const RouterManager = require('../data/managers/router-manager') const MicroserviceExtraHostManager = require('../data/managers/microservice-extra-host-manager') +const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') const RouterConnectionManager = require('../data/managers/router-connection-manager') +const CatalogItemImageManager = require('../data/managers/catalog-item-image-manager') const RouterService = require('./router-service') const Constants = require('../helpers/constants') const Op = require('sequelize').Op const lget = require('lodash/get') +const CertificateService = require('./certificate-service') +const logger = require('../logger') +const ServiceManager = require('../data/managers/service-manager') +const FogStates = require('../enums/fog-state') +const SecretManager = require('../data/managers/secret-manager') + +const SITE_CA_CERT = 'pot-site-ca' +const DEFAULT_ROUTER_LOCAL_CA = 'default-router-local-ca' +const SERVICE_ANNOTATION_TAG = 'service.datasance.com/tag' + +async function checkKubernetesEnvironment () { + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + return controlPlane && controlPlane.toLowerCase() === 'kubernetes' +} -async function createFogEndPoint (fogData, user, isCLI, transaction) { - await Validator.validate(fogData, Validator.schemas.iofogCreate) +async function getLocalCertificateHosts (fogData) { + const hosts = new Set() + const defaultHost = ['localhost', '127.0.0.1', 'host.docker.internal', 'host.containers.internal', 'iofog', 'service.local'] + // Add default hosts individually + defaultHost.forEach(host => hosts.add(host)) + if (fogData.host) hosts.add(fogData.host) + if (fogData.ipAddress) hosts.add(fogData.ipAddress) + if (fogData.ipAddressExternal) hosts.add(fogData.ipAddressExternal) + // if (isKubernetes) { + // return `router-local,router-local.${namespace},router-local.${namespace}.svc.cluster.local,127.0.0.1,localhost,host.docker.internal,host.containers.internal` + // } + return Array.from(hosts).join(',') || 'localhost' +} + +async function getSiteCertificateHosts (fogData) { + const hosts = new Set() + // const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + // const isFogDefaultRouter = fogUuid === defaultRouter.iofogUuid + // // Add existing hosts if isSystem and fog is default-router + // if (fogData.isSystem && isFogDefaultRouter) { + // if (fogData.host) hosts.add(fogData.host) + // if (fogData.ipAddress) hosts.add(fogData.ipAddress) + // if (fogData.ipAddressExternal) hosts.add(fogData.ipAddressExternal) + // } + // // Add default router host if not system or fog isSystem but not default-router + // if (!fogData.isSystem || (fogData.isSystem && !isFogDefaultRouter)) { + // // const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + // if (defaultRouter.host) hosts.add(defaultRouter.host) + // } + // Add upstream router hosts + // const upstreamRouters = (fogData.upstreamRouters || []).filter(uuid => uuid !== 'default-router') + // if (upstreamRouters.length) { + // for (const uuid of upstreamRouters) { + // const routerHost = await FogManager.findOne({ uuid: uuid }, transaction) + // if (routerHost.host) hosts.add(routerHost.host) + // if (routerHost.ipAddress) hosts.add(routerHost.ipAddress) + // } + // } + if (fogData.host) hosts.add(fogData.host) + if (fogData.ipAddress) hosts.add(fogData.ipAddress) + if (fogData.ipAddressExternal) hosts.add(fogData.ipAddressExternal) + return Array.from(hosts).join(',') || 'localhost' +} + +async function _handleRouterCertificates (fogData, uuid, isRouterModeChanged, transaction) { + logger.debug('Starting _handleRouterCertificates for fog: ' + JSON.stringify({ uuid: uuid, host: fogData.host })) + + // Check if we're in Kubernetes environment + const isKubernetes = await checkKubernetesEnvironment() + // const namespace = isKubernetes ? process.env.CONTROLLER_NAMESPACE : null + + // Helper to check CA existence + async function ensureCA (name, subject) { + logger.debug('Checking CA existence: ' + JSON.stringify({ name, subject })) + try { + await CertificateService.getCAEndpoint(name, transaction) + logger.debug('CA already exists: ' + name) + // CA exists + } catch (err) { + if (err.name === 'NotFoundError') { + logger.debug('CA not found, creating new CA: ' + JSON.stringify({ name, subject })) + await CertificateService.createCAEndpoint({ + name, + subject: `${subject}`, + expiration: 60, // months + type: 'self-signed' + }, transaction) + logger.debug('Successfully created CA: ' + name) + } else if (err.name === 'ConflictError') { + logger.debug('CA already exists (conflict): ' + name) + // Already exists, ignore + } else { + logger.error('Error in ensureCA - Name: ' + name + ', Subject: ' + subject + ', Error: ' + err.message + ', Type: ' + err.name + ', Code: ' + err.code) + logger.error('Stack trace: ' + err.stack) + throw err + } + } + } + // Helper to check cert existence + async function ensureCert (name, subject, hosts, ca, shouldRecreate = false) { + logger.debug('Checking certificate existence: ' + JSON.stringify({ name, subject, hosts, ca })) + try { + const existingCert = await CertificateService.getCertificateEndpoint(name, transaction) + if (shouldRecreate && existingCert) { + logger.debug('Certificate exists and needs recreation: ' + name) + await CertificateService.deleteCertificateEndpoint(name, transaction) + logger.debug('Deleted existing certificate: ' + name) + // Create new certificate + await CertificateService.createCertificateEndpoint({ + name, + subject: `${subject}`, + hosts, + ca + }, transaction) + logger.debug('Successfully recreated certificate: ' + name) + } else if (!existingCert) { + logger.debug('Certificate not found, creating new certificate: ' + JSON.stringify({ name, subject, hosts, ca })) + await CertificateService.createCertificateEndpoint({ + name, + subject: `${subject}`, + hosts, + ca + }, transaction) + logger.debug('Successfully created certificate: ' + name) + } else { + logger.debug('Certificate already exists: ' + name) + } + } catch (err) { + if (err.name === 'NotFoundError') { + logger.debug('Certificate not found, creating new certificate: ' + JSON.stringify({ name, subject, hosts, ca })) + await CertificateService.createCertificateEndpoint({ + name, + subject: `${subject}`, + hosts, + ca + }, transaction) + logger.debug('Successfully created certificate: ' + name) + } else if (err.name === 'ConflictError') { + logger.debug('Certificate already exists (conflict): ' + name) + // Already exists, ignore + } else { + logger.error('Error in ensureCert - Name: ' + name + ', Subject: ' + subject + ', Hosts: ' + hosts + ', CA: ' + JSON.stringify(ca) + ', Error: ' + err.message + ', Type: ' + err.name + ', Code: ' + err.code) + logger.error('Stack trace: ' + err.stack) + throw err + } + } + } + + try { + // Always ensure SITE_CA_CERT exists + logger.debug('Ensuring SITE_CA_CERT exists') + await ensureCA(SITE_CA_CERT, SITE_CA_CERT) + + // If routerMode is 'none', only ensure DEFAULT_ROUTER_LOCAL_CA and its signed certificate + if (fogData.routerMode === 'none') { + logger.debug('Router mode is none, ensuring DEFAULT_ROUTER_LOCAL_CA exists') + if (isKubernetes) { + await ensureCA(DEFAULT_ROUTER_LOCAL_CA, DEFAULT_ROUTER_LOCAL_CA) + } + logger.debug('Ensuring local-agent certificate signed by DEFAULT_ROUTER_LOCAL_CA') + const localHosts = await getLocalCertificateHosts(fogData) + let defaultRouterLocalCA + if (isKubernetes) { + defaultRouterLocalCA = DEFAULT_ROUTER_LOCAL_CA + } else { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + defaultRouterLocalCA = `${defaultRouter.iofogUuid}-local-ca` + } + + await ensureCert( + `${uuid}-local-agent`, + `${uuid}-local-agent`, + localHosts, + { type: 'direct', secretName: defaultRouterLocalCA }, + isRouterModeChanged + ) + logger.debug('Successfully completed _handleRouterCertificates for routerMode none') + return + } + + // For other router modes, ensure all other certificates + // Always ensure site-server cert exists + logger.debug('Ensuring site-server certificate exists') + const siteHosts = await getSiteCertificateHosts(fogData) + await ensureCert( + `${uuid}-site-server`, + `${uuid}-site-server`, + siteHosts, + { type: 'direct', secretName: SITE_CA_CERT }, + false + ) + + // Always ensure local-ca exists + logger.debug('Ensuring local-ca exists') + await ensureCA(`${uuid}-local-ca`, `${uuid}-local-ca`) + + // Always ensure local-server cert exists + logger.debug('Ensuring local-server certificate exists') + const localHosts = await getLocalCertificateHosts(fogData) + await ensureCert( + `${uuid}-local-server`, + `${uuid}-local-server`, + localHosts, + { type: 'direct', secretName: `${uuid}-local-ca` }, + isRouterModeChanged + ) + + // Always ensure local-agent cert exists + logger.debug('Ensuring local-agent certificate exists') + await ensureCert( + `${uuid}-local-agent`, + `${uuid}-local-agent`, + localHosts, + { type: 'direct', secretName: `${uuid}-local-ca` }, + isRouterModeChanged + ) + + logger.debug('Successfully completed _handleRouterCertificates') + } catch (error) { + logger.error('Certificate operation failed - UUID: ' + uuid + ', RouterMode: ' + fogData.routerMode + ', Error: ' + error.message + ', Type: ' + error.name + ', Code: ' + error.code) + logger.error('Stack trace: ' + error.stack) + } +} + +async function createFogEndPoint (fogData, isCLI, transaction) { + await Validator.validate(fogData, Validator.schemas.iofogCreate) let createFogData = { - uuid: AppHelper.generateRandomString(32), + uuid: AppHelper.generateUUID(), name: fogData.name, location: fogData.location, latitude: fogData.latitude, longitude: fogData.longitude, - gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + // gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, description: fogData.description, + networkInterface: fogData.networkInterface, dockerUrl: fogData.dockerUrl, + containerEngine: fogData.containerEngine, + deploymentType: fogData.deploymentType, diskLimit: fogData.diskLimit, diskDirectory: fogData.diskDirectory, memoryLimit: fogData.memoryLimit, @@ -65,14 +289,28 @@ async function createFogEndPoint (fogData, user, isCLI, transaction) { abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, fogTypeId: fogData.fogType, logLevel: fogData.logLevel, + edgeGuardFrequency: fogData.edgeGuardFrequency, dockerPruningFrequency: fogData.dockerPruningFrequency, availableDiskThreshold: fogData.availableDiskThreshold, isSystem: fogData.isSystem, - userId: user.id, host: fogData.host, routerId: null, timeZone: fogData.timeZone } + + if ((fogData.latitude || fogData.longitude) && (fogData.gpsMode !== 'dynamic' && fogData.gpsMode !== 'off')) { + createFogData.gpsMode = 'manual' + } else if (fogData.gpsMode === 'dynamic' && fogData.gpsDevice) { + createFogData.gpsMode = fogData.gpsMode + createFogData.gpsDevice = fogData.gpsDevice + } else if (!(fogData.latitude || fogData.longitude) && fogData.gpsMode === 'auto') { + createFogData.gpsMode = 'auto' + } else if (fogData.gpsMode === 'off') { + createFogData.gpsMode = 'off' + } else { + createFogData.gpsMode = undefined + } + createFogData = AppHelper.deleteUndefinedFields(createFogData) // Default router is edge @@ -82,20 +320,16 @@ async function createFogEndPoint (fogData, user, isCLI, transaction) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_MODE, fogData.routerMode)) } - if (fogData.isSystem && !!(await FogManager.findOne({ isSystem: true }, transaction))) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_SYSTEM_FOG)) - } + // // TODO: handle multiple system fogs a.k.a multi-remote-controller and multi interior routers + // if (fogData.isSystem && !!(await FogManager.findOne({ isSystem: true }, transaction))) { + // throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_SYSTEM_FOG)) + // } const existingFog = await FogManager.findOne({ name: createFogData.name }, transaction) if (existingFog) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, createFogData.name)) } - // Remove user if system fog - if (fogData.isSystem) { - fogData.userId = 0 - } - let defaultRouter, upstreamRouters if (fogData.routerMode === 'none') { const networkRouter = await RouterService.getNetworkRouter(fogData.networkRouter) @@ -110,36 +344,75 @@ async function createFogEndPoint (fogData, user, isCLI, transaction) { const fog = await FogManager.create(createFogData, transaction) - // Set tags + // Set tags (synchronously, as this is a simple DB op) await _setTags(fog, fogData.tags, transaction) - if (fogData.routerMode !== 'none') { - if (!fogData.host && !isCLI) { - throw new Errors.ValidationError(ErrorMessages.HOST_IS_REQUIRED) - } - - await RouterService.createRouterForFog(fogData, fog.uuid, user.id, upstreamRouters) - } - - const res = { - uuid: fog.uuid - } - - await ChangeTrackingService.create(fog.uuid, transaction) - - if (fogData.abstractedHardwareEnabled) { - await _createHalMicroserviceForFog(fog, null, user, transaction) - } - - if (fogData.bluetoothEnabled) { - await _createBluetoothMicroserviceForFog(fog, null, user, transaction) - } - - await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + // Return fog UUID immediately + const res = { uuid: fog.uuid } + + // Start background orchestration + setImmediate(() => { + (async () => { + try { + // --- Begin orchestration logic (previously inside runWithRetries) --- + await _handleRouterCertificates(fogData, createFogData.uuid, false, transaction) + + if (fogData.routerMode !== 'none') { + if (!fogData.host && !isCLI) { + throw new Errors.ValidationError(ErrorMessages.HOST_IS_REQUIRED) + } + await RouterService.createRouterForFog(fogData, fog.uuid, upstreamRouters) + + // Service Distribution Logic + const serviceTags = await _extractServiceTags(fogData.tags) + if (serviceTags.length > 0) { + const services = await _findMatchingServices(serviceTags, transaction) + if (services.length > 0) { + const routerName = `router-${fog.uuid.toLowerCase()}` + const routerMicroservice = await MicroserviceManager.findOne({ name: routerName }, transaction) + if (!routerMicroservice) { + throw new Errors.NotFoundError(`Router microservice not found: ${routerName}`) + } + let config = JSON.parse(routerMicroservice.config || '{}') + for (const service of services) { + const listenerConfig = _buildTcpListenerForFog(service, fog.uuid) + config = _mergeTcpListener(config, listenerConfig) + } + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { config: JSON.stringify(config) }, + transaction + ) + await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.microserviceConfig, transaction) + } + } + } - try { - await informKubelet(fog.uuid, 'POST') - } catch (e) {} + await ChangeTrackingService.create(fog.uuid, transaction) + if (fogData.abstractedHardwareEnabled) { + await _createHalMicroserviceForFog(fog, null, transaction) + } + if (fogData.bluetoothEnabled) { + await _createBluetoothMicroserviceForFog(fog, null, transaction) + } + await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + // --- End orchestration logic --- + // Set fog node as healthy + await FogManager.update({ uuid: fog.uuid }, { warningMessage: 'HEALTHY' }, transaction) + } catch (err) { + logger.error('Background orchestration failed in createFogEndPoint: ' + err.message) + // Set fog node as warning with error message + await FogManager.update( + { uuid: fog.uuid }, + { + daemonStatus: FogStates.WARNING, + warningMessage: `Background orchestration error: ${err.message}` + }, + transaction + ) + } + })() + }) return res } @@ -158,7 +431,7 @@ async function _setTags (fogModel, tagsArray, transaction) { } } -async function updateFogEndPoint (fogData, user, isCLI, transaction) { +async function updateFogEndPoint (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogUpdate) const queryFogData = { uuid: fogData.uuid } @@ -168,9 +441,12 @@ async function updateFogEndPoint (fogData, user, isCLI, transaction) { location: fogData.location, latitude: fogData.latitude, longitude: fogData.longitude, - gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + // gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, description: fogData.description, + networkInterface: fogData.networkInterface, dockerUrl: fogData.dockerUrl, + containerEngine: fogData.containerEngine, + deploymentType: fogData.deploymentType, diskLimit: fogData.diskLimit, diskDirectory: fogData.diskDirectory, memoryLimit: fogData.memoryLimit, @@ -188,10 +464,24 @@ async function updateFogEndPoint (fogData, user, isCLI, transaction) { fogTypeId: fogData.fogType, logLevel: fogData.logLevel, dockerPruningFrequency: fogData.dockerPruningFrequency, + edgeGuardFrequency: fogData.edgeGuardFrequency, host: fogData.host, availableDiskThreshold: fogData.availableDiskThreshold, timeZone: fogData.timeZone } + + if ((fogData.latitude || fogData.longitude) && (fogData.gpsMode !== 'dynamic' && fogData.gpsMode !== 'off')) { + updateFogData.gpsMode = 'manual' + } else if (fogData.gpsMode === 'dynamic' && fogData.gpsDevice) { + updateFogData.gpsMode = fogData.gpsMode + updateFogData.gpsDevice = fogData.gpsDevice + } else if (!(fogData.latitude || fogData.longitude) && fogData.gpsMode === 'auto') { + updateFogData.gpsMode = 'auto' + } else if (fogData.gpsMode === 'off') { + updateFogData.gpsMode = 'off' + } else { + updateFogData.gpsMode = undefined + } updateFogData = AppHelper.deleteUndefinedFields(updateFogData) const oldFog = await FogManager.findOne(queryFogData, transaction) @@ -199,35 +489,28 @@ async function updateFogEndPoint (fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // Update tags - await _setTags(oldFog, fogData.tags, transaction) - - // If using REST API and not system fog. You must be the fog's user to access it - if (!oldFog.isSystem && !isCLI && oldFog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) + // Prevent overwriting detected fogType (1 or 2) with "auto" (0) + // If fogType is being set to "auto" (0) but the agent has already detected its type (1 or 2), + // preserve the detected type to ensure getAgentMicroservices can find matching images + if (fogData.fogType === 0 && (oldFog.fogTypeId === 1 || oldFog.fogTypeId === 2)) { + updateFogData.fogTypeId = undefined + // Remove undefined fields again after modifying updateFogData + updateFogData = AppHelper.deleteUndefinedFields(updateFogData) } - // Set userId if moving fog from system to classic or from classic to system - if (!oldFog.isSystem && updateFogData.isSystem) { - updateFogData.userId = 0 - if (await FogManager.findOne({ isSystem: true }, transaction)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_SYSTEM_FOG)) - } - } else if (updateFogData.isSystem === false) { - updateFogData.userId = user.id - } + // Update tags + await _setTags(oldFog, fogData.tags, transaction) if (updateFogData.name) { const conflictQuery = isCLI ? { name: updateFogData.name, uuid: { [Op.not]: fogData.uuid } } - : { name: updateFogData.name, uuid: { [Op.not]: fogData.uuid }, userId: user.id } + : { name: updateFogData.name, uuid: { [Op.not]: fogData.uuid } } const conflict = await FogManager.findOne(conflictQuery, transaction) if (conflict) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, updateFogData.name)) } } - // Update router // Get all router config informations const router = await oldFog.getRouter() const host = fogData.host || lget(router, 'host') @@ -239,74 +522,157 @@ async function updateFogEndPoint (fogData, user, isCLI, transaction) { const edgeRouterPort = fogData.edgeRouterPort || (router ? router.edgeRouterPort : null) let networkRouter - // const isSystem = updateFogData.isSystem === undefined ? oldFog.isSystem : updateFogData.isSystem - // if (isSystem && routerMode !== 'interior') { - // throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_MODE, fogData.routerMode)) - // } - - if (routerMode === 'none') { - networkRouter = await RouterService.getNetworkRouter(fogData.networkRouter) - if (!networkRouter) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, !fogData.networkRouter ? Constants.DEFAULT_ROUTER_NAME : fogData.networkRouter)) - } - // Only delete previous router if there is a network router - if (router) { - // New router mode is none, delete existing router - await _deleteFogRouter(fogData, user.id, transaction) - } - } else { - const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) - const upstreamRouters = await RouterService.validateAndReturnUpstreamRouters(upstreamRoutersIofogUuid, oldFog.isSystem, defaultRouter) - if (!router) { - // Router does not exist yet - networkRouter = await RouterService.createRouterForFog(fogData, oldFog.uuid, user.id, upstreamRouters) - } else { - // Update existing router - networkRouter = await RouterService.updateRouter(router, { - messagingPort, interRouterPort, edgeRouterPort, isEdge: routerMode === 'edge', host - }, upstreamRouters, user.id) - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.routerChanged, transaction) - } + const isSystem = updateFogData.isSystem === undefined ? oldFog.isSystem : updateFogData.isSystem + if (isSystem && routerMode !== 'interior') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_MODE, fogData.routerMode)) } - updateFogData.routerId = networkRouter.id - // If router changed, set routerChanged flag - if (updateFogData.routerId !== oldFog.routerId || updateFogData.routerMode !== oldFog.routerMode) { - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.routerChanged, transaction) - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceList, transaction) + let isRouterModeChanged = false + const oldRouterMode = (router ? (router.isEdge ? 'edge' : 'interior') : 'none') + if (fogData.routerMode && fogData.routerMode !== oldRouterMode) { + if (fogData.routerMode === 'none' || oldRouterMode === 'none') { + isRouterModeChanged = true + } } await FogManager.update(queryFogData, updateFogData, transaction) await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.config, transaction) - let msChanged = false - - // Update Microservice extra hosts - if (updateFogData.host && updateFogData.host !== oldFog.host) { - await _updateMicroserviceExtraHosts(fogData.uuid, updateFogData.host, transaction) - } + // Return immediately + const res = { uuid: fogData.uuid } + + // Start background orchestration + setImmediate(() => { + (async () => { + try { + // --- Begin orchestration logic --- + const fog = await FogManager.findOne({ uuid: fogData.uuid }, transaction) + await _handleRouterCertificates(fogData, fog.uuid, isRouterModeChanged, transaction) + + if (routerMode === 'none') { + networkRouter = await RouterService.getNetworkRouter(fogData.networkRouter) + if (!networkRouter) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, !fogData.networkRouter ? Constants.DEFAULT_ROUTER_NAME : fogData.networkRouter)) + } + if (router) { + await _deleteFogRouter(fogData, transaction) + } + } else { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + const upstreamRouters = await RouterService.validateAndReturnUpstreamRouters(upstreamRoutersIofogUuid, oldFog.isSystem, defaultRouter) + if (!router) { + networkRouter = await RouterService.createRouterForFog(fogData, oldFog.uuid, upstreamRouters) + // --- Service Distribution Logic --- + const serviceTags = await _extractServiceTags(fogData.tags) + if (serviceTags.length > 0) { + const services = await _findMatchingServices(serviceTags, transaction) + if (services.length > 0) { + const routerName = `router-${fogData.uuid.toLowerCase()}` + const routerMicroservice = await MicroserviceManager.findOne({ name: routerName }, transaction) + if (!routerMicroservice) { + throw new Errors.NotFoundError(`Router microservice not found: ${routerName}`) + } + let config = JSON.parse(routerMicroservice.config || '{}') + for (const service of services) { + const listenerConfig = _buildTcpListenerForFog(service, fogData.uuid) + config = _mergeTcpListener(config, listenerConfig) + } + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { config: JSON.stringify(config) }, + transaction + ) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceConfig, transaction) + } + } + } else { + const existingConnectors = await _extractExistingTcpConnectors(fogData.uuid, transaction) + networkRouter = await RouterService.updateRouter(router, { + messagingPort, interRouterPort, edgeRouterPort, isEdge: routerMode === 'edge', host + }, upstreamRouters, fogData.containerEngine) + // --- Service Distribution Logic --- + const serviceTags = await _extractServiceTags(fogData.tags) + const routerName = `router-${fogData.uuid.toLowerCase()}` + const routerMicroservice = await MicroserviceManager.findOne({ name: routerName }, transaction) + if (!routerMicroservice) { + throw new Errors.NotFoundError(`Router microservice not found: ${routerName}`) + } + let config = JSON.parse(routerMicroservice.config || '{}') + if (serviceTags.length > 0) { + const services = await _findMatchingServices(serviceTags, transaction) + if (services.length > 0) { + for (const service of services) { + const listenerConfig = _buildTcpListenerForFog(service, fogData.uuid) + config = _mergeTcpListener(config, listenerConfig) + } + } + } + // Merge back existing connectors if any + if (existingConnectors && Object.keys(existingConnectors).length > 0) { + for (const connectorName in existingConnectors) { + const connectorObj = existingConnectors[connectorName] + config = _mergeTcpConnector(config, connectorObj) + } + } + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { config: JSON.stringify(config) }, + transaction + ) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceConfig, transaction) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.routerChanged, transaction) + } + } + updateFogData.routerId = networkRouter.id - if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { - await _deleteHalMicroserviceByFog(fogData, transaction) - msChanged = true - } - if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { - await _createHalMicroserviceForFog(fogData, oldFog, user, transaction) - msChanged = true - } + // If router changed, set routerChanged flag + if (updateFogData.routerId !== oldFog.routerId || updateFogData.routerMode !== oldFog.routerMode) { + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.routerChanged, transaction) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceList, transaction) + } - if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { - await _deleteBluetoothMicroserviceByFog(fogData, transaction) - msChanged = true - } - if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { - await _createBluetoothMicroserviceForFog(fogData, oldFog, user, transaction) - msChanged = true - } + let msChanged = false + if (updateFogData.host && updateFogData.host !== oldFog.host) { + await _updateMicroserviceExtraHosts(fogData.uuid, updateFogData.host, transaction) + } + if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { + await _deleteHalMicroserviceByFog(fogData, transaction) + msChanged = true + } + if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { + await _createHalMicroserviceForFog(fogData, oldFog, transaction) + msChanged = true + } + if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { + await _deleteBluetoothMicroserviceByFog(fogData, transaction) + msChanged = true + } + if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { + await _createBluetoothMicroserviceForFog(fogData, oldFog, transaction) + msChanged = true + } + if (msChanged) { + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + } + // --- End orchestration logic --- + // Set fog node as healthy + await FogManager.update({ uuid: fogData.uuid }, { warningMessage: 'HEALTHY' }, transaction) + } catch (err) { + logger.error('Background orchestration failed in updateFogEndPoint: ' + err.message) + await FogManager.update( + { uuid: fogData.uuid }, + { + daemonStatus: FogStates.WARNING, + warningMessage: `Background orchestration error: ${err.message}` + }, + transaction + ) + } + })() + }) - if (msChanged) { - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) - } + // Return immediately + return res } async function _updateMicroserviceExtraHosts (fogUuid, host, transaction) { @@ -333,7 +699,7 @@ async function _updateProxyRouters (fogId, router, transaction) { } } -async function _deleteFogRouter (fogData, userId, transaction) { +async function _deleteFogRouter (fogData, transaction) { const router = await RouterManager.findOne({ iofogUuid: fogData.uuid }, transaction) const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) @@ -359,7 +725,7 @@ async function _deleteFogRouter (fogData, userId, transaction) { } // Update router config - await RouterService.updateConfig(router.id, userId, transaction) + await RouterService.updateConfig(router.id, fogData.containerEngine, transaction) // Set routerChanged flag await ChangeTrackingService.update(router.iofogUuid, ChangeTrackingService.events.routerChanged, transaction) } @@ -380,9 +746,10 @@ async function _deleteFogRouter (fogData, userId, transaction) { // Delete router msvc const routerCatalog = await CatalogService.getRouterCatalogItem(transaction) await MicroserviceManager.delete({ catalogItemId: routerCatalog.id, iofogUuid: fogData.uuid }, transaction) + // await ApplicationManager.delete({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) } -async function deleteFogEndPoint (fogData, user, isCLI, transaction) { +async function deleteFogEndPoint (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogDelete) const queryFogData = { uuid: fogData.uuid } @@ -392,18 +759,9 @@ async function deleteFogEndPoint (fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) - } - - await _deleteFogRouter(fogData, user.id, transaction) + await _deleteFogRouter(fogData, transaction) await _processDeleteCommand(fog, transaction) - - try { - await informKubelet(fog.uuid, 'DELETE') - } catch (e) {} } function _getRouterUuid (router, defaultRouter) { @@ -453,14 +811,33 @@ async function _getFogEdgeResources (fog, transaction) { return resources.map(EdgeResourceService.buildGetObject) } +async function _getFogVolumeMounts (fog, transaction) { + const volumeMountAttributes = [ + 'name', + 'version', + 'configMapName', + 'secretName' + ] + const volumeMounts = await fog.getVolumeMounts({ attributes: volumeMountAttributes }) + return volumeMounts.map(vm => { + return { + name: vm.name, + version: vm.version, + configMapName: vm.configMapName, + secretName: vm.secretName + } + }) +} + async function _getFogExtraInformation (fog, transaction) { const routerConfig = await _getFogRouterConfig(fog, transaction) const edgeResources = await _getFogEdgeResources(fog, transaction) + const volumeMounts = await _getFogVolumeMounts(fog, transaction) // Transform to plain JS object if (fog.toJSON && typeof fog.toJSON === 'function') { fog = fog.toJSON() } - return { ...fog, tags: _mapTags(fog), ...routerConfig, edgeResources } + return { ...fog, tags: _mapTags(fog), ...routerConfig, edgeResources, volumeMounts } } // Map tags to string array @@ -469,37 +846,62 @@ function _mapTags (fog) { return fog.tags ? fog.tags.map(t => t.value) : [] } -async function getFog (fogData, user, isCLI, transaction) { +/** + * Extracts service-related tags from fog node tags + * @param {Array} fogTags - Array of tags from fog node + * @returns {Array} Array of service tags (e.g., ["all", "foo", "bar"]) + */ +async function _extractServiceTags (fogTags) { + if (!fogTags || !Array.isArray(fogTags)) { + return [] + } + + // Filter tags that start with SERVICE_ANNOTATION_TAG + const serviceTags = fogTags + .filter(tag => tag.startsWith(SERVICE_ANNOTATION_TAG)) + .map(tag => { + // Extract the value after the colon + const parts = tag.split(':') + return parts.length > 1 ? parts[1].trim() : '' + }) + .filter(tag => tag !== '') // Remove empty tags + + // If we have "all" tag, return just that + if (serviceTags.includes('all')) { + return ['all'] + } + + return serviceTags +} + +async function getFog (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGet) - const queryFogData = fogData.uuid ? { uuid: fogData.uuid } : { name: fogData.name, userId: user.id } + const queryFogData = fogData.uuid ? { uuid: fogData.uuid } : { name: fogData.name } const fog = await FogManager.findOneWithTags(queryFogData, transaction) if (!fog) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) - } - return _getFogExtraInformation(fog, transaction) } -async function getFogEndPoint (fogData, user, isCLI, transaction) { - return getFog(fogData, user, isCLI, transaction) +async function getFogEndPoint (fogData, isCLI, transaction) { + return getFog(fogData, isCLI, transaction) } -async function getFogListEndPoint (filters, user, isCLI, isSystem, transaction) { +// async function getFogListEndPoint (filters, isCLI, isSystem, transaction) { +async function getFogListEndPoint (filters, isCLI, transaction) { await Validator.validate(filters, Validator.schemas.iofogFilters) - // If listing system agent through REST API, make sure user is authenticated - if (isSystem && !isCLI && !lget(user, 'id')) { - throw new Errors.AuthenticationError('Unauthorized') - } + // // If listing system agent through REST API, make sure user is authenticated + // if (isSystem && !isCLI && !lget('id')) { + // throw new Errors.AuthenticationError('Unauthorized') + // } - const queryFogData = isSystem ? { isSystem } : (isCLI ? {} : { userId: user.id, isSystem: false }) + // const queryFogData = isSystem ? { isSystem } : (isCLI ? {} : { isSystem: false }) + const queryFogData = {} let fogs = await FogManager.findAllWithTags(queryFogData, transaction) fogs = _filterFogs(fogs, filters) @@ -512,14 +914,14 @@ async function getFogListEndPoint (filters, user, isCLI, isSystem, transaction) } } -async function generateProvisioningKeyEndPoint (fogData, user, isCLI, transaction) { +async function generateProvisioningKeyEndPoint (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGenerateProvision) const queryFogData = { uuid: fogData.uuid } const newProvision = { iofogUuid: fogData.uuid, - provisionKey: AppHelper.generateRandomString(8), + provisionKey: AppHelper.generateUUID(), expirationTime: new Date().getTime() + (20 * 60 * 1000) } @@ -528,20 +930,58 @@ async function generateProvisioningKeyEndPoint (fogData, user, isCLI, transactio throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) - } - const provisioningKeyData = await FogProvisionKeyManager.updateOrCreate({ iofogUuid: fogData.uuid }, newProvision, transaction) + const devMode = process.env.DEV_MODE || config.get('server.devMode') + const sslCert = process.env.SSL_CERT || config.get('server.ssl.path.cert') + const intermedKey = process.env.INTERMEDIATE_CERT || config.get('server.ssl.path.intermediateCert') + const sslCertBase64 = config.get('server.ssl.base64.cert') + const intermedKeyBase64 = config.get('server.ssl.base64.intermediateCert') + const hasFileBasedSSL = !devMode && sslCert + const hasBase64SSL = !devMode && sslCertBase64 + let caCert = '' + + if (!devMode) { + if (hasFileBasedSSL) { + try { + if (intermedKey) { + // Check if intermediate certificate file exists before trying to read it + if (fs.existsSync(intermedKey)) { + const certData = fs.readFileSync(intermedKey) + caCert = Buffer.from(certData).toString('base64') + } else { + // Intermediate certificate file doesn't exist, don't provide any CA cert + // Let the system's default trust store handle validation + logger.info(`Intermediate certificate file not found at path: ${intermedKey}, not providing CA certificate`) + caCert = '' + } + } else { + // No intermediate certificate path provided, don't provide any CA cert + // Let the system's default trust store handle validation + caCert = '' + } + } catch (error) { + throw new Errors.ValidationError('Failed to read SSL certificate file') + } + } + if (hasBase64SSL) { + if (intermedKeyBase64) { + caCert = intermedKeyBase64 + } else { + // No intermediate certificate base64 provided, don't provide any CA cert + // Let the system's default trust store handle validation + caCert = '' + } + } + } return { key: provisioningKeyData.provisionKey, - expirationTime: provisioningKeyData.expirationTime + expirationTime: provisioningKeyData.expirationTime, + caCert: caCert } } -async function setFogVersionCommandEndPoint (fogVersionData, user, isCLI, transaction) { +async function setFogVersionCommandEndPoint (fogVersionData, isCLI, transaction) { await Validator.validate(fogVersionData, Validator.schemas.iofogSetVersionCommand) const queryFogData = { uuid: fogVersionData.uuid } @@ -556,11 +996,6 @@ async function setFogVersionCommandEndPoint (fogVersionData, user, isCLI, transa throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, queryFogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, queryFogData.uuid)) - } - if (!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') { throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_ROLLBACK) } @@ -568,12 +1003,12 @@ async function setFogVersionCommandEndPoint (fogVersionData, user, isCLI, transa throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_UPGRADE) } - await generateProvisioningKeyEndPoint({ uuid: fogVersionData.uuid }, user, isCLI, transaction) + await generateProvisioningKeyEndPoint({ uuid: fogVersionData.uuid }, isCLI, transaction) await FogVersionCommandManager.updateOrCreate({ iofogUuid: fogVersionData.uuid }, newVersionCommand, transaction) await ChangeTrackingService.update(fogVersionData.uuid, ChangeTrackingService.events.version, transaction) } -async function setFogRebootCommandEndPoint (fogData, user, isCLI, transaction) { +async function setFogRebootCommandEndPoint (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogReboot) const queryFogData = { uuid: fogData.uuid } @@ -583,15 +1018,10 @@ async function setFogRebootCommandEndPoint (fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) - } - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.reboot, transaction) } -async function getHalHardwareInfoEndPoint (uuidObj, user, isCLI, transaction) { +async function getHalHardwareInfoEndPoint (uuidObj, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet) const fog = await FogManager.findOne({ @@ -601,17 +1031,12 @@ async function getHalHardwareInfoEndPoint (uuidObj, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, uuidObj.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, uuidObj.uuid)) - } - return HWInfoManager.findOne({ iofogUuid: uuidObj.uuid }, transaction) } -async function getHalUsbInfoEndPoint (uuidObj, user, isCLI, transaction) { +async function getHalUsbInfoEndPoint (uuidObj, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet) const fog = await FogManager.findOne({ @@ -621,11 +1046,6 @@ async function getHalUsbInfoEndPoint (uuidObj, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, uuidObj.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, uuidObj.uuid)) - } - return USBInfoManager.findOne({ iofogUuid: uuidObj.uuid }, transaction) @@ -662,27 +1082,57 @@ async function _processDeleteCommand (fog, transaction) { for (const microservice of microservices) { await MicroserviceService.deleteMicroserviceWithRoutesAndPortMappings(microservice, transaction) } - + await ApplicationManager.delete({ name: `system-${fog.uuid.toLowerCase()}` }, transaction) await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.deleteNode, transaction) + // Delete router-related secrets if they exist + const secretNames = [ + `${fog.uuid}-site-server`, + `${fog.uuid}-local-ca`, + `${fog.uuid}-local-server`, + `${fog.uuid}-local-agent` + ] + + for (const secretName of secretNames) { + const secret = await SecretManager.findOne({ name: secretName }, transaction) + if (secret) { + await SecretManager.delete({ name: secretName }, transaction) + } + } await FogManager.delete({ uuid: fog.uuid }, transaction) } -async function _createHalMicroserviceForFog (fogData, oldFog, user, transaction) { +async function _createHalMicroserviceForFog (fogData, oldFog, transaction) { const halItem = await CatalogService.getHalCatalogItem(transaction) const halMicroserviceData = { - uuid: AppHelper.generateRandomString(32), - name: `Hal for Fog ${fogData.uuid}`, + uuid: AppHelper.generateUUID(), + name: `hal-${fogData.uuid.toLowerCase()}`, config: '{}', catalogItemId: halItem.id, iofogUuid: fogData.uuid, - rootHostAccess: true, + hostNetworkMode: true, + isPrivileged: true, logSize: Constants.MICROSERVICE_DEFAULT_LOG_SIZE, - userId: oldFog ? oldFog.userId : user.id, + schedule: 1, configLastUpdated: Date.now() } + let application + try { + application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + } catch (error) { + const systemApplicationData = { + name: `system-${fogData.uuid.toLowerCase()}`, + isActivated: true, + isSystem: true + } + await ApplicationManager.create(systemApplicationData, transaction) + application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + } + halMicroserviceData.applicationId = application.id await MicroserviceManager.create(halMicroserviceData, transaction) + await MicroserviceStatusManager.create({ microserviceUuid: halMicroserviceData.uuid }, transaction) + await MicroserviceExecStatusManager.create({ microserviceUuid: halMicroserviceData.uuid }, transaction) } async function _deleteHalMicroserviceByFog (fogData, transaction) { @@ -692,25 +1142,43 @@ async function _deleteHalMicroserviceByFog (fogData, transaction) { catalogItemId: halItem.id } + const application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + deleteHalMicroserviceData.applicationId = application.id await MicroserviceManager.delete(deleteHalMicroserviceData, transaction) } -async function _createBluetoothMicroserviceForFog (fogData, oldFog, user, transaction) { +async function _createBluetoothMicroserviceForFog (fogData, oldFog, transaction) { const bluetoothItem = await CatalogService.getBluetoothCatalogItem(transaction) const bluetoothMicroserviceData = { - uuid: AppHelper.generateRandomString(32), - name: `Bluetooth for Fog ${fogData.uuid}`, + uuid: AppHelper.generateUUID(), + name: `ble-${fogData.uuid.toLowerCase()}`, config: '{}', catalogItemId: bluetoothItem.id, iofogUuid: fogData.uuid, - rootHostAccess: true, + hostNetworkMode: true, + isPrivileged: true, logSize: Constants.MICROSERVICE_DEFAULT_LOG_SIZE, - userId: oldFog ? oldFog.userId : user.id, + schedule: 1, configLastUpdated: Date.now() } + let application + try { + application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + } catch (error) { + const systemApplicationData = { + name: `system-${fogData.uuid.toLowerCase()}`, + isActivated: true, + isSystem: true + } + await ApplicationManager.create(systemApplicationData, transaction) + application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + } + bluetoothMicroserviceData.applicationId = application.id await MicroserviceManager.create(bluetoothMicroserviceData, transaction) + await MicroserviceStatusManager.create({ microserviceUuid: bluetoothMicroserviceData.uuid }, transaction) + await MicroserviceExecStatusManager.create({ microserviceUuid: bluetoothMicroserviceData.uuid }, transaction) } async function _deleteBluetoothMicroserviceByFog (fogData, transaction) { @@ -719,24 +1187,13 @@ async function _deleteBluetoothMicroserviceByFog (fogData, transaction) { iofogUuid: fogData.uuid, catalogItemId: bluetoothItem.id } + const application = await ApplicationManager.findOne({ name: `system-${fogData.uuid.toLowerCase()}` }, transaction) + deleteBluetoothMicroserviceData.applicationId = application.id await MicroserviceManager.delete(deleteBluetoothMicroserviceData, transaction) } -const informKubelet = function (iofogUuid, method) { - const kubeletUri = config.get('Kubelet:Uri') - const options = { - uri: kubeletUri + '/node', - qs: { - uuid: iofogUuid - }, - method: method - } - - return request(options) -} - -async function setFogPruneCommandEndPoint (fogData, user, isCLI, transaction) { +async function setFogPruneCommandEndPoint (fogData, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogPrune) const queryFogData = { uuid: fogData.uuid } @@ -746,12 +1203,289 @@ async function setFogPruneCommandEndPoint (fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } - // If using REST API and not system fog. You must be the fog's user to access it - if (!fog.isSystem && !isCLI && fog.userId !== user.id) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.prune, transaction) +} + +async function enableNodeExecEndPoint (execData, isCLI, transaction) { + await Validator.validate(execData, Validator.schemas.enableNodeExec) + const fog = await FogManager.findOne({ uuid: execData.uuid }, transaction) + if (!fog) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, execData.uuid)) } - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.prune, transaction) + const debugMicroserviceData = { + uuid: AppHelper.generateUUID(), + name: `debug-${execData.uuid.toLowerCase()}`, + config: '{}', + iofogUuid: execData.uuid, + ipcMode: 'host', + pidMode: 'host', + hostNetworkMode: true, + isPrivileged: true, + logSize: Constants.MICROSERVICE_DEFAULT_LOG_SIZE, + schedule: 0, + execEnabled: true, + configLastUpdated: Date.now() + } + + if (execData.image) { + const images = [ + { fogTypeId: 1, containerImage: execData.image }, + { fogTypeId: 2, containerImage: execData.image } + ] + debugMicroserviceData.images = images + } else { + const debugCatalog = await CatalogService.getDebugCatalogItem(transaction) + debugMicroserviceData.catalogItemId = debugCatalog.id + } + + let application + try { + application = await ApplicationManager.findOne({ name: `system-${execData.uuid.toLowerCase()}` }, transaction) + } catch (error) { + const systemApplicationData = { + name: `system-${execData.uuid.toLowerCase()}`, + isActivated: true, + isSystem: true + } + await ApplicationManager.create(systemApplicationData, transaction) + application = await ApplicationManager.findOne({ name: `system-${execData.uuid.toLowerCase()}` }, transaction) + } + debugMicroserviceData.applicationId = application.id + let microservice + + // Check if microservice already exists + const existingMicroservice = await MicroserviceManager.findOneWithCategory({ name: `debug-${execData.uuid.toLowerCase()}` }, transaction) + + if (existingMicroservice) { + // Update existing microservice + const updateData = { + ipcMode: debugMicroserviceData.ipcMode, + pidMode: debugMicroserviceData.pidMode, + hostNetworkMode: debugMicroserviceData.hostNetworkMode, + isPrivileged: debugMicroserviceData.isPrivileged, + logSize: debugMicroserviceData.logSize, + schedule: debugMicroserviceData.schedule, + configLastUpdated: debugMicroserviceData.configLastUpdated, + execEnabled: debugMicroserviceData.execEnabled + } + + if (execData.image) { + updateData.images = debugMicroserviceData.images + } else { + updateData.catalogItemId = debugMicroserviceData.images + } + + microservice = await MicroserviceManager.updateAndFind( + { uuid: existingMicroservice.uuid }, + updateData, + transaction + ) + + if (execData.image) { + const images = [ + { fogTypeId: 1, containerImage: execData.image }, + { fogTypeId: 2, containerImage: execData.image } + ] + await _updateImages(images, existingMicroservice.uuid, transaction) + } + + await ChangeTrackingService.update(execData.uuid, ChangeTrackingService.events.microserviceList, transaction) + await ChangeTrackingService.update(execData.uuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + return microservice + } else { + // Create new microservice + try { + const microservice = await MicroserviceManager.create(debugMicroserviceData, transaction) + await MicroserviceStatusManager.create({ microserviceUuid: debugMicroserviceData.uuid }, transaction) + await MicroserviceExecStatusManager.create({ microserviceUuid: debugMicroserviceData.uuid }, transaction) + + if (execData.image) { + const images = [ + { fogTypeId: 1, containerImage: execData.image }, + { fogTypeId: 2, containerImage: execData.image } + ] + await _createMicroserviceImages(microservice, images, transaction) + } + + await ChangeTrackingService.update(execData.uuid, ChangeTrackingService.events.microserviceList, transaction) + await ChangeTrackingService.update(execData.uuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + + return microservice + } catch (error) { + logger.error(`Error in enableNodeExecEndPoint: ${error.message}`) + throw error + } + } +} + +async function disableNodeExecEndPoint (fogData, isCLI, transaction) { + await Validator.validate(fogData, Validator.schemas.disableNodeExec) + + try { + const fog = await FogManager.findOne({ uuid: fogData.uuid }, transaction) + if (!fog) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) + } + + const microservice = await MicroserviceManager.findOne({ name: `debug-${fogData.uuid.toLowerCase()}` }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, fogData.uuid)) + } + + await MicroserviceManager.delete({ uuid: microservice.uuid }, transaction) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceList, transaction) + await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + } catch (error) { + logger.error(`Error in disableNodeExecEndPoint: ${error.message}`) + throw error + } +} + +/** + * Finds services that match the fog node's service tags + * @param {Array} serviceTags - Array of service tags from fog node + * @param {Object} transaction - Database transaction + * @returns {Promise>} Array of matching services + */ +async function _findMatchingServices (serviceTags, transaction) { + if (!serviceTags || serviceTags.length === 0) { + return [] + } + + // If 'all' tag is present, get all services + if (serviceTags.includes('all')) { + return ServiceManager.findAllWithTags({}, transaction) + } + + // For each service tag, find matching services + const servicesPromises = serviceTags.map(async (tag) => { + const queryData = { + '$tags.value$': `${tag}` + } + return ServiceManager.findAllWithTags(queryData, transaction) + }) + + // Wait for all queries to complete + const servicesArrays = await Promise.all(servicesPromises) + + // Flatten arrays and remove duplicates based on service name + const seen = new Set() + const uniqueServices = servicesArrays + .flat() + .filter(service => { + if (seen.has(service.name)) { + return false + } + seen.add(service.name) + return true + }) + + return uniqueServices +} + +/** + * Builds TCP listener configuration for a service on a specific fog node + * @param {Object} service - Service object containing name and bridgePort + * @param {string} fogNodeUuid - UUID of the fog node + * @returns {Object} TCP listener configuration + */ +function _buildTcpListenerForFog (service, fogNodeUuid) { + return { + name: `${service.name}-listener`, + port: service.bridgePort.toString(), + address: service.name, + siteId: fogNodeUuid + } +} + +/** + * Gets the router microservice configuration for a fog node + * @param {string} fogNodeUuid - UUID of the fog node + * @param {Object} transaction - Database transaction + * @returns {Promise} Router microservice configuration + */ +async function _getRouterMicroserviceConfig (fogNodeUuid, transaction) { + const routerName = `router-${fogNodeUuid.toLowerCase()}` + const routerMicroservice = await MicroserviceManager.findOne({ name: routerName }, transaction) + if (!routerMicroservice) { + throw new Errors.NotFoundError(`Router microservice not found: ${routerName}`) + } + const routerConfig = JSON.parse(routerMicroservice.config || '{}') + return routerConfig +} + +/** + * Extracts existing TCP connectors from router configuration + * @param {string} fogNodeUuid - UUID of the fog node + * @param {Object} transaction - Database transaction + * @returns {Promise} Object containing TCP connectors + */ +async function _extractExistingTcpConnectors (fogNodeUuid, transaction) { + const routerConfig = await _getRouterMicroserviceConfig(fogNodeUuid, transaction) + // Return empty object if no bridges or tcpConnectors exist + if (!routerConfig.bridges || !routerConfig.bridges.tcpConnectors) { + return {} + } + + return routerConfig.bridges.tcpConnectors +} + +/** + * Merges a single TCP connector into router configuration + * @param {Object} routerConfig - Base router configuration + * @param {Object} connectorObj - TCP connector object (must have 'name' property) + * @returns {Object} Updated router configuration + */ +function _mergeTcpConnector (routerConfig, connectorObj) { + if (!connectorObj || !connectorObj.name) { + throw new Error('Connector object must have a name property') + } + if (!routerConfig.bridges) { + routerConfig.bridges = {} + } + if (!routerConfig.bridges.tcpConnectors) { + routerConfig.bridges.tcpConnectors = {} + } + routerConfig.bridges.tcpConnectors[connectorObj.name] = connectorObj + return routerConfig +} + +/** + * Merges a single TCP listener into router configuration + * @param {Object} routerConfig - Base router configuration + * @param {Object} listenerObj - TCP listener object (must have 'name' property) + * @returns {Object} Updated router configuration + */ +function _mergeTcpListener (routerConfig, listenerObj) { + if (!listenerObj || !listenerObj.name) { + throw new Error('Listener object must have a name property') + } + if (!routerConfig.bridges) { + routerConfig.bridges = {} + } + if (!routerConfig.bridges.tcpListeners) { + routerConfig.bridges.tcpListeners = {} + } + routerConfig.bridges.tcpListeners[listenerObj.name] = listenerObj + return routerConfig +} + +async function _createMicroserviceImages (microservice, images, transaction) { + const newImages = [] + for (const img of images) { + const newImg = Object.assign({}, img) + newImg.microserviceUuid = microservice.uuid + newImages.push(newImg) + } + return CatalogItemImageManager.bulkCreate(newImages, transaction) +} + +async function _updateImages (images, microserviceUuid, transaction) { + await CatalogItemImageManager.delete({ + microserviceUuid: microserviceUuid + }, transaction) + return _createMicroserviceImages({ uuid: microserviceUuid }, images, transaction) } module.exports = { @@ -766,5 +1500,16 @@ module.exports = { getHalHardwareInfoEndPoint: TransactionDecorator.generateTransaction(getHalHardwareInfoEndPoint), getHalUsbInfoEndPoint: TransactionDecorator.generateTransaction(getHalUsbInfoEndPoint), getFog: getFog, - setFogPruneCommandEndPoint: TransactionDecorator.generateTransaction(setFogPruneCommandEndPoint) + setFogPruneCommandEndPoint: TransactionDecorator.generateTransaction(setFogPruneCommandEndPoint), + enableNodeExecEndPoint: TransactionDecorator.generateTransaction(enableNodeExecEndPoint), + disableNodeExecEndPoint: TransactionDecorator.generateTransaction(disableNodeExecEndPoint), + _extractServiceTags, + _findMatchingServices: TransactionDecorator.generateTransaction(_findMatchingServices), + _buildTcpListenerForFog, + _getRouterMicroserviceConfig: TransactionDecorator.generateTransaction(_getRouterMicroserviceConfig), + _extractExistingTcpConnectors: TransactionDecorator.generateTransaction(_extractExistingTcpConnectors), + _mergeTcpConnector, + _mergeTcpListener, + checkKubernetesEnvironment, + _handleRouterCertificates: TransactionDecorator.generateTransaction(_handleRouterCertificates) } diff --git a/src/services/kubelet-access-token-service.js b/src/services/kubelet-access-token-service.js deleted file mode 100644 index 478806e5f..000000000 --- a/src/services/kubelet-access-token-service.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const AppHelper = require('../helpers/app-helper') -const KubeletAccessTokenManager = require('../data/managers/kubelet-access-token-manager') - -const Config = require('../config') - -const generateAccessToken = async function (transaction) { - while (true) { - const newAccessToken = AppHelper.generateAccessToken() - const exists = await KubeletAccessTokenManager.findOne({ - token: newAccessToken - }, transaction) - if (!exists) { - const accessTokenExpiryTime = Date.now() + Config.get('Settings:KubeletTokenExpirationIntervalSeconds') * 9999999 - return { - token: newAccessToken, - expirationTime: accessTokenExpiryTime - } - } - } -} - -async function updateAccessToken (userId, newAccessToken, transaction) { - return KubeletAccessTokenManager.updateOrCreate({ - userId: userId - }, { - userId: userId, - token: newAccessToken.token, - expirationTime: newAccessToken.expirationTime - }, transaction) -} - -module.exports = { - generateAccessToken, - updateAccessToken -} diff --git a/src/services/kubelet-service.js b/src/services/kubelet-service.js deleted file mode 100644 index bd6569cf3..000000000 --- a/src/services/kubelet-service.js +++ /dev/null @@ -1,432 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const moment = require('moment') - -const AppHelper = require('../helpers/app-helper') -const ErrorMessages = require('../helpers/error-messages') -const Errors = require('../helpers/errors') -const ApplicationService = require('./application-service') -const FogManager = require('../data/managers/iofog-manager') -const IOFogService = require('./iofog-service') -const KubeletAccessTokenService = require('./kubelet-access-token-service') -const logger = require('../logger') -const MicroservicesService = require('./microservices-service') -const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') -// const SchedulerAccessTokenService = require('./scheduler-access-token-service') -const TransactionDecorator = require('../decorators/transaction-decorator') - -const NODE_CAPACITY = 100 - -const processPodPayload = function (createPodData, fogNodeUuid) { - const msMetadata = JSON.parse(createPodData.metadata.annotations.microservices) - const applicationDescription = { - metadata: createPodData, - node: fogNodeUuid - } - - const applicationData = { - name: createPodData.metadata.name, - isActivated: true, - description: Buffer.from(JSON.stringify(applicationDescription)).toString('base64') - } - - const microservices = microservicesTopologicalOrder(msMetadata) - - return { - applicationData, - microservices - } -} - -const kubeletCreatePod = async function (createPodData, fogNodeUuid, user, transaction) { - const podPayload = processPodPayload(createPodData, fogNodeUuid) - const { applicationData, microservices } = podPayload - - const applications = await ApplicationService.getAllApplicationsEndPoint(false, transaction) - let application = applications.applications.find((application) => application.name === applicationData.name) - if (!application) { - application = await ApplicationService.createApplicationEndPoint(applicationData, user, false, transaction) - } - - const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(application.id, user, false, transaction) - - const microservicesIds = [] - for (const ms of microservices) { - const name = `${applicationData.name}-${ms.name}` - const existingMicroservice = existingMicroservices.microservices.find((it) => it.name === name) - if (existingMicroservice) { - microservicesIds.push(existingMicroservice.uuid) - continue - } - - ms.routes = ms.routes || [] - ms.routes = ms.routes.map((route) => { - if (!route.startsWith('@')) { - return route - } - const routeId = route.substr(1) * 1 - const idx = microservices.findIndex((it) => it.originalIndex === routeId) - return microservicesIds[idx] - }) - - const microserviceData = { - name: name, - config: ms.config, - catalogItemId: ms['catalog-item-id'], - applicationId: application.id, - iofogUuid: fogNodeUuid, - rootHostAccess: ms['host-access'], - volumeMappings: ms['volume-mappings'] || [], - ports: ms.ports || [], - routes: ms.routes || [] - } - if (ms.env && ms.env.length > 0) { - microserviceData.env = ms.env - } - if (ms.cmd && ms.cmd.length > 0) { - microserviceData.cmd = ms.cmd - } - - const microservice = await MicroservicesService.createMicroserviceEndPoint(microserviceData, user, false, transaction) - microservicesIds.push(microservice.uuid) - } -} - -const kubeletUpdatePod = async function (uploadPodData, fogNodeUuid, user, transaction) { - // Not supported yet. - // const podPayload = processPodPayload(uploadPodData, fogNodeUuid) - // const { applicationData, microservices } = podPayload - - // const applications = await ApplicationService.getAllApplicationsEndPoint(false, transaction) - // const application = applications.applications.find((application) => application.name === applicationData.name) - // if (!application) { - // throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, applicationData.name)) - // } - - // const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(application.id, user, false, transaction) - // const msDup = [].concat(microservices) - // const toDelete = [] - // existingMicroservices.forEach((ms) => { - // const name = `${applicationData.name}-${ms.name}` - // const idx = msDup.findIndex((it) => it.name === name) - - // if (!idx) { - // toDelete.push(ms) - // } else { - // toUpdate.push(msDup[idx]) - // msDup = msDup.splice(idx, 1) - // } - // }) - - // msDup.map((ms) => { - // const name = `${applicationData.name}-${ms.name}` - - // const microserviceData = { - // name: name, - // config: ms.config, - // catalogItemId: ms['catalog-item-id'], - // applicationId: application.id, - // iofogUuid: fogNodeUuid, - // rootHostAccess: ms['host-access'], - // volumeMappings: ms['volume-mappings'] || [], - // ports: ms.ports || [], - // routes: ms.routes || [] - // } - // if (ms.env && ms.env.length > 0) { - // microserviceData.env = ms.env - // } - // if (ms.cmd && ms.cmd.length > 0) { - // microserviceData.cmd = ms.cmd - // } - - // return microserviceData - // }) -} - -const kubeletDeletePod = async function (podData, fogNodeUuid, user, transaction) { - const applicationName = podData.metadata.name - - const applications = await ApplicationService.getAllApplicationsEndPoint(false, transaction) - const application = applications.applications.find((application) => application.name === applicationName) - if (!application) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogNodeUuid)) - } - - const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(application.id, user, false, transaction) - existingMicroservices.microservices.forEach(async (ms) => { - await MicroservicesService.deleteMicroserviceEndPoint(ms.uuid, { withCleanup: true }, user, false, transaction) - }) - - await ApplicationService.deleteApplicationEndPoint(application.id, user, false, transaction) -} - -const kubeletGetPod = async function (namespace, name, fogNodeUuid, user, transaction) { - const application = await ApplicationService.getApplicationByName(name, user, false, transaction) - - return JSON.parse(Buffer.from(application.description, 'base64').toString('utf8')).metadata -} - -const kubeletGetContainerLogs = async function (namespace, podName, containerName, tail, fogNodeUuid, user, transaction) { - // Not supported yet -} - -const kubeletGetPodStatus = async function (namespace, name, fogNodeUuid, user, transaction) { - const fog = await FogManager.findOne({ uuid: fogNodeUuid }, transaction) - const changeFrequency = (fog && fog.changeFrequency) || 60 - - const application = await ApplicationService.getApplicationByName(name, user, false, transaction) - const microservices = await MicroservicesService.listMicroservicesEndPoint(application.id, user, false, transaction) - const pod = JSON.parse(Buffer.from(application.description, 'base64').toString('utf8')).metadata - - for (const ms of microservices.microservices) { - const status = await MicroserviceStatusManager.findOne({ microserviceUuid: ms.uuid }, transaction) - ms.status = status.dataValues - ms.status.alive = moment().diff(moment(ms.status.updated_at), 'seconds') <= (changeFrequency * 2) - } - - const phase = microservices.microservices.every((ms) => ms.status.status === 'RUNNING') ? 'Running' : 'Pending' - const alive = microservices.microservices.every((ms) => ms.status.alive) - const status = { - phase: phase, - startTime: (alive && phase === 'Running') ? moment(microservices.microservices[0].startTime).utc().toISOString() : null, - conditions: [ - { - Type: 'PodInitialized', - Status: 'True' - }, - { - Type: 'PodReady', - Status: (alive && phase === 'Running') ? 'True' : 'False' - }, - { - Type: 'PodScheduled', - Status: 'True' - } - ], - containerStatuses: [] - } - - status.containerStatuses = pod.spec.containers.map((c) => { - const microservice = microservices.microservices.find((ms) => ms.name === `${name}-${c.name}`) - - const containerState = {} - if (microservice.status.status === 'RUNNING') { - containerState.running = { startedAt: moment(microservice.status.startTime).utc().toISOString() } - } else { - containerState.waiting = { reason: microservice.status.status } - } - - return { - name: c.name, - imageID: microservice.uuid, - ready: alive && microservice.status.status === 'RUNNING', - restartCount: 0, - state: containerState, - containerId: microservice.status.containerId - } - }) - - return status -} - -const kubeletGetPods = async function (fogNodeUuid, user, transaction) { - const applications = await ApplicationService.getAllApplicationsEndPoint(false, transaction) - const pods = applications.applications - .reduce((prev, application) => { - try { - const podsInfo = JSON.parse(Buffer.from(application.description, 'base64').toString('utf8')) - if (podsInfo.node === fogNodeUuid) { - prev = prev.concat(podsInfo.metadata) - } - } catch (err) { - logger.error(err) - } - - return prev - }, []) - - return pods -} - -const kubeletGetCapacity = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - - return { - cpu: node.cpuLimit, - memory: `${(node.memoryLimit).toFixed(0)}Mi`, - pods: `${NODE_CAPACITY}` - } -} - -const kubeletGetAllocatable = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - - const pods = await kubeletGetPods(fogNodeUuid, user, transaction) - const allocatablePods = NODE_CAPACITY - (pods || []).length - - return { - cpu: node.cpuLimit - node.cpuUsage, - memory: `${(node.memoryLimit - node.memoryUsage).toFixed(0)}Mi`, - pods: allocatablePods < 0 ? 0 : allocatablePods - } -} - -const kubeletGetNodeConditions = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - const now = moment().utc().toISOString() - const lastStatusTime = node.lastStatusTime ? moment(node.lastStatusTime).utc().toISOString() : null - return [ - { - type: 'Ready', - status: node.daemonStatus === 'RUNNING' ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: node.daemonStatus - }, - { - type: 'OutOfDisk', - status: node.diskUsage >= node.diskLimit ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}` - }, - { - type: 'MemoryPressure', - status: (node.memoryUsage / node.memoryLimit) >= 0.9 ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.memoryUsage}, Limit: ${node.memoryLimit}` - }, - { - type: 'DiskPressure', - status: (node.diskUsage / node.diskLimit) >= 0.9 ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}` - }, - { - type: 'NetworkUnavailable', - status: 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: '' - } - ] -} - -const kubeletGetNodeAddresses = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - return [ - { - type: 'InternalIP', - address: node.ipAddress ? node.ipAddress : '0.0.0.0' - }, - { - type: 'ExternalIP', - address: node.ipAddressExternal ? node.ipAddressExternal : '0.0.0.0' - } - ] -} - -const kubeletGetVkToken = async function (userId, transaction) { - const newAccessToken = await KubeletAccessTokenService.generateAccessToken(transaction) - await KubeletAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) - - return { - userId: userId, - token: newAccessToken.token - } -} - -const kubeletGetSchedulerToken = async function (transaction) { - // Not implemented yet. - userId is undefined - // const newAccessToken = await SchedulerAccessTokenService.generateAccessToken(transaction) - // await SchedulerAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) - - // return { - // userId: userId, - // token: newAccessToken.token - // } -} - -const microservicesTopologicalOrder = function (msMetadata) { - const microservices = [] - const graph = [] - msMetadata.forEach((ms, i) => { - graph[i] = { - edges: [] - } - - if (!ms.routes) { - return - } - - ms.routes.forEach((route) => { - if (route.startsWith('@')) { - graph[i].edges.push(route.substr(1)) - } - }) - }) - - const stack = msMetadata.reduce((prev, ms, i) => { - if (graph[i].edges.length === 0) { - return prev.concat(i) - } - - return prev - }, []) - - while (stack.length > 0) { - const n = stack.pop() - microservices.push(n) - graph.forEach((node, i) => { - if (!node.edges.length) { - return - } - - node.edges = node.edges.filter((e) => `${e}` !== `${n}`) - if (!node.edges.length) { - stack.push(i) - } - }) - } - - const hasCircuit = !!graph.filter((node) => !!node.edges.length).length - if (hasCircuit) { - throw new Error('Circular dependency!!!') - } - - return microservices.map((idx) => Object.assign({ originalIndex: idx }, msMetadata[idx])) -} - -module.exports = { - kubeletCreatePod: TransactionDecorator.generateTransaction(kubeletCreatePod), - kubeletUpdatePod: TransactionDecorator.generateTransaction(kubeletUpdatePod), - kubeletDeletePod: TransactionDecorator.generateTransaction(kubeletDeletePod), - kubeletGetPod: TransactionDecorator.generateTransaction(kubeletGetPod), - kubeletGetContainerLogs: TransactionDecorator.generateTransaction(kubeletGetContainerLogs), - kubeletGetPodStatus: TransactionDecorator.generateTransaction(kubeletGetPodStatus), - kubeletGetPods: TransactionDecorator.generateTransaction(kubeletGetPods), - kubeletGetCapacity: TransactionDecorator.generateTransaction(kubeletGetCapacity), - kubeletGetAllocatable: TransactionDecorator.generateTransaction(kubeletGetAllocatable), - kubeletGetNodeConditions: TransactionDecorator.generateTransaction(kubeletGetNodeConditions), - kubeletGetNodeAddresses: TransactionDecorator.generateTransaction(kubeletGetNodeAddresses), - kubeletGetVkToken: TransactionDecorator.generateTransaction(kubeletGetVkToken), - kubeletGetSchedulerToken: TransactionDecorator.generateTransaction(kubeletGetSchedulerToken) -} diff --git a/src/services/microservice-ports/default.js b/src/services/microservice-ports/default.js deleted file mode 100644 index 8d9e88685..000000000 --- a/src/services/microservice-ports/default.js +++ /dev/null @@ -1,538 +0,0 @@ -/* only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const MicroservicePortManager = require('../../data/managers/microservice-port-manager') -const MicroserviceManager = require('../../data/managers/microservice-manager') -const ConfigManager = require('../../data/managers/config-manager') -const ChangeTrackingService = require('../change-tracking-service') -const AppHelper = require('../../helpers/app-helper') -const Errors = require('../../helpers/errors') -const ErrorMessages = require('../../helpers/error-messages') -const CatalogService = require('../../services/catalog-service') -const Op = require('sequelize').Op -const FogManager = require('../../data/managers/iofog-manager') -const RouterManager = require('../../data/managers/router-manager') -const MicroservicePublicPortManager = require('../../data/managers/microservice-public-port-manager') -const MicroserviceExtraHostManager = require('../../data/managers/microservice-extra-host-manager') -const controllerConfig = require('../../config') -const Proxy = require('./proxy') - -const { DEFAULT_ROUTER_NAME, DEFAULT_PROXY_HOST, RESERVED_PORTS } = require('../../helpers/constants') - -const lget = require('lodash/get') - -async function _checkForDuplicatePorts (agent, localPort, transaction) { - if (RESERVED_PORTS.find(port => port === localPort)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_RESERVED, localPort)) - } - - const microservices = await agent.getMicroservice() - for (const microservice of microservices) { - const ports = await microservice.getPorts() - if (ports.find(port => port.portExternal === localPort)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_NOT_AVAILABLE, localPort)) - } - } - - const publicPorts = await MicroservicePublicPortManager.findAll({ hostId: agent.uuid }, transaction) - if (publicPorts.find(port => port.publicPort === localPort)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_NOT_AVAILABLE, localPort)) - } -} - -function _createDefaultPublicPortRange () { - const defaultPortRangeStr = process.env.PUBLIC_PORTS_RANGE || controllerConfig.get('PublicPorts:Range') - const [startStr, endStr] = defaultPortRangeStr.split('-') - let start = parseInt(startStr) - let end = parseInt(endStr) - if (!start || Number.isNaN(start)) { start = 6000 } - if (!end || Number.isNaN(end) || end < start) { - end = start + 1000 - } - const size = end - start - const availablePorts = new Array(size) - for (let i = 0; i < size; ++i) { - availablePorts[i] = start + i - } - - return availablePorts -} - -// Validate port and populate mapping.publicHost, mapping.publicPort, mapping.localAgent -async function validatePortMapping (agent, mapping, availablePublicPortsByHost, transaction) { - await _checkForDuplicatePorts(agent, mapping.external, transaction) - - if (mapping.public) { - const isTcp = mapping.public.protocol ? mapping.public.protocol.toLowerCase() === 'tcp' : false - // Validate link protocol - for (const scheme of mapping.public.schemes) { - // !isTcp === HTTP link protocol will be used - if (!scheme.startsWith('http') && !isTcp) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.WRONG_PUBLIC_LINK_PROTOCOL, mapping.internal, scheme, 'http')) - } - } - let host - if (mapping.public.router && mapping.public.router.host && mapping.public.router.host !== DEFAULT_ROUTER_NAME) { - host = await FogManager.findOne({ uuid: mapping.public.host }, transaction) - if (!host) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_HOST, mapping.public.host)) - } - } else { - host = await FogManager.findOne({ isSystem: true }, transaction) - if (!host) { - const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) - if (!defaultRouter) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_HOST, DEFAULT_ROUTER_NAME)) - } - } - } - - // We have found a host - if (host) { - // Port is defined by user - if (mapping.public.router && mapping.public.router.port) { - await _checkForDuplicatePorts(host, mapping.public.router.port, transaction) - mapping.publicPort = mapping.public.router.port - } else { - // Assign next available public port - const currentPublicPorts = (await MicroservicePublicPortManager.findAll({ hostId: host.uuid }, transaction)).map(p => p.publicPort) - // Default range 6000 -> 7000 - availablePublicPortsByHost[host.uuid] = availablePublicPortsByHost[host.uuid] || _createDefaultPublicPortRange() - availablePublicPortsByHost[host.uuid] = availablePublicPortsByHost[host.uuid].filter(port => !currentPublicPorts.includes(port)) - if (availablePublicPortsByHost[host.uuid].length === 0) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NO_AVAILABLE_PUBLIC_PORT, host.isSystem ? DEFAULT_ROUTER_NAME : host.name)) - } - mapping.publicPort = availablePublicPortsByHost[host.uuid].shift() - } - } else { - const hostId = 'default-router' - // We don't have a host. A.k.a, there is no System agent, but there is a default router. A.k.a: We're running on K8s - // Port is defined by user - if (mapping.public.router && mapping.public.router.port) { - // Alls ports are shared by the same proxy msvc - const publicPorts = await MicroservicePublicPortManager.findAll({ hostId: null }, transaction) - if (publicPorts.find(port => port.publicPort === mapping.public.router.port)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_NOT_AVAILABLE, mapping.public.router.port)) - } - mapping.publicPort = mapping.public.router.port - } else { - // Port - // Alls ports are shared by the same proxy msvc - const currentPublicPorts = (await MicroservicePublicPortManager.findAll({ hostId: null }, transaction)).map(p => p.publicPort) - availablePublicPortsByHost[hostId] = availablePublicPortsByHost[hostId] || _createDefaultPublicPortRange() - availablePublicPortsByHost[hostId] = availablePublicPortsByHost[hostId].filter(port => !currentPublicPorts.includes(port)) - if (availablePublicPortsByHost[hostId].length === 0) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NO_AVAILABLE_PUBLIC_PORT, host.isSystem ? DEFAULT_ROUTER_NAME : host.name)) - } - mapping.publicPort = availablePublicPortsByHost[hostId].shift() - } - } - - mapping.publicHost = host - mapping.localAgent = agent - } -} - -async function validatePortMappings (microserviceData, transaction) { - if (!microserviceData.ports || microserviceData.ports.length === 0) { - return - } - - const localAgent = await FogManager.findOne({ uuid: microserviceData.iofogUuid }, transaction) - if (!localAgent) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microserviceData.iofogUuid)) - } - - // Will be filled by validatePortMapping - // In memory store to keep track of newly allocated ports - const availablePublicPortsByHost = {} - for (const mapping of microserviceData.ports) { - await validatePortMapping(localAgent, mapping, availablePublicPortsByHost, transaction) - } -} - -async function validatePublicPortAppHostTemplate (extraHost, templateArgs, msvc, transaction) { - if (templateArgs.length !== 5) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, templateArgs.join('.'))) - } - - const ports = await MicroservicePortManager.findAllPublicPorts({ microserviceUuid: msvc.uuid }, transaction) - for (const port of ports) { - if (port.publicPort.publicPort === +(templateArgs[4])) { - const fog = await FogManager.findOne({ uuid: port.publicPort.hostId }, transaction) - extraHost.publicPort = port.publicPort.publicPort - extraHost.targetFogUuid = fog.uuid - extraHost.value = fog.host || fog.ipAddress - return extraHost - } - } - - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_HOST_TEMPLATE, templateArgs[4])) -} - -async function movePublicPortsToNewFog (updatedMicroservice, user, transaction) { - const portMappings = await updatedMicroservice.getPorts() - for (const portMapping of portMappings) { - if (portMapping.isProxy) { - Proxy.moveProxyPortsToNewFog(updatedMicroservice, portMapping, user, transaction) - continue - } else if (!portMapping.isPublic) { - continue - } - - const publicPort = await portMapping.getPublicPort() - const localMapping = `amqp:${publicPort.queueName}=>${publicPort.protocol}:${portMapping.portExternal}` - const localProxy = await MicroserviceManager.findOne({ uuid: publicPort.localProxyId }, transaction) - await _updateOrDeleteProxyMicroservice(localProxy.uuid, localMapping, transaction) - - const destAgent = await FogManager.findOne({ uuid: updatedMicroservice.iofogUuid }, transaction) - const destAgentsRouter = destAgent.routerId ? await RouterManager.findOne({ id: destAgent.routerId }, transaction) : await RouterManager.findOne({ iofogUuid: destAgent.uuid }, transaction) - const networkRouter = { - host: destAgentsRouter.host, - port: destAgentsRouter.messagingPort - } - const newProxy = await _createOrUpdateProxyMicroservice(localMapping, networkRouter, updatedMicroservice.iofogUuid, localProxy.catalogItemId, user, transaction) - publicPort.localProxyId = newProxy.uuid - await MicroservicePublicPortManager.updateOrCreate({ id: publicPort.id }, publicPort.toJSON(), transaction) - } -} - -async function createPortMapping (microservice, portMappingData, user, transaction) { - if (!microservice.iofogUuid) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) - } - - const msPorts = await MicroservicePortManager.findOne({ - microserviceUuid: microservice.uuid, - [Op.or]: - [ - { - portInternal: portMappingData.internal - }, - { - portExternal: portMappingData.external - } - ] - }, transaction) - if (msPorts) { - throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_ALREADY_EXISTS) - } - - portMappingData.protocol = portMappingData.protocol || '' - - if (portMappingData.public) { - return _createPublicPortMapping(microservice, portMappingData, user, transaction) - } else if (portMappingData.proxy) { - return Proxy.createProxyPortMapping(microservice, portMappingData, user, transaction) - } else { - return _createSimplePortMapping(microservice, portMappingData, user, transaction) - } -} - -async function _createOrUpdateProxyMicroservice (mapping, networkRouter, hostUuid, proxyCatalogId, user, transaction) { - const existingProxy = await MicroserviceManager.findOne({ catalogItemId: proxyCatalogId, iofogUuid: hostUuid }, transaction) - if (existingProxy) { - const config = JSON.parse(existingProxy.config || '{}') - config.mappings = (config.mappings || []).concat(mapping) - existingProxy.config = JSON.stringify(config) - await MicroserviceManager.updateIfChanged({ uuid: existingProxy.uuid }, { config: JSON.stringify(config) }, transaction) - await ChangeTrackingService.update(hostUuid, ChangeTrackingService.events.microserviceConfig, transaction) - return existingProxy - } - - const proxyMicroserviceData = { - uuid: AppHelper.generateRandomString(32), - name: 'Proxy', - config: JSON.stringify({ - mappings: [mapping], - networkRouter: networkRouter - }), - catalogItemId: proxyCatalogId, - iofogUuid: hostUuid, - rootHostAccess: true, - registryId: 1, - userId: user.id - } - const res = await MicroserviceManager.create(proxyMicroserviceData, transaction) - await ChangeTrackingService.update(hostUuid, ChangeTrackingService.events.microserviceCommon, transaction) - return res -} - -async function _createPublicPortMapping (microservice, portMappingData, user, transaction) { - const isTcp = portMappingData.public.protocol ? portMappingData.public.protocol.toLowerCase() === 'tcp' : false - const isUdp = portMappingData.protocol.toLowerCase() === 'udp' - const localAgent = portMappingData.localAgent // This is populated by validating the ports - const localAgentsRouter = localAgent.routerId ? await RouterManager.findOne({ id: localAgent.routerId }, transaction) : await RouterManager.findOne({ iofogUuid: localAgent.uuid }, transaction) - const localNetworkRouter = { - host: localAgentsRouter.host, - port: localAgentsRouter.messagingPort - } - - // create proxy microservices - const queueName = AppHelper.generateRandomString(32) - const proxyCatalog = await CatalogService.getProxyCatalogItem(transaction) - const localMapping = `amqp:${queueName}=>${isTcp ? 'tcp' : 'http'}:${portMappingData.external}` - const remoteMapping = `${isTcp ? 'tcp' : 'http'}:${portMappingData.publicPort}=>amqp:${queueName}` - - const localProxy = !portMappingData.public.disabled ? await _createOrUpdateProxyMicroservice( - localMapping, - localNetworkRouter, - microservice.iofogUuid, - proxyCatalog.id, - user, - transaction) : null - - let remoteProxy - if (portMappingData.publicHost) { - const hostRouter = portMappingData.publicHost.routerId ? await RouterManager.findOne({ id: portMappingData.publicHost.routerId }, transaction) : await RouterManager.findOne({ iofogUuid: portMappingData.publicHost.uuid }, transaction) - const remoteNetworkRouter = { - host: hostRouter.host, - port: hostRouter.messagingPort - } - - remoteProxy = !portMappingData.public.disabled ? await _createOrUpdateProxyMicroservice( - remoteMapping, - remoteNetworkRouter, - portMappingData.publicHost.uuid, - proxyCatalog.id, - user, - transaction) : null - } - - const mappingData = { - isPublic: true, - isProxy: false, - portInternal: portMappingData.internal, - portExternal: portMappingData.external, - isUdp, - userId: microservice.userId, - microserviceUuid: microservice.uuid - } - const port = await MicroservicePortManager.create(mappingData, transaction) - const schemes = portMappingData.public && portMappingData.public.schemes - - const publicPort = { - portId: port.id, - hostId: portMappingData.publicHost ? portMappingData.publicHost.uuid : null, - localProxyId: localProxy.uuid, - remoteProxyId: portMappingData.publicHost ? remoteProxy.uuid : null, - publicPort: portMappingData.publicPort, - queueName, - schemes: JSON.stringify(schemes), - isTcp - } - await MicroservicePublicPortManager.create(publicPort, transaction) - - // Look for related extraHosts to update - const relatedExtraHosts = await MicroserviceExtraHostManager.findAll({ microserviceUuid: microservice.uuid, publicPort: publicPort.publicPort }, transaction) - for (const extraHost of relatedExtraHosts) { - extraHost.targetFogUuid = publicPort.hostId - extraHost.value = portMappingData.publicHost.host - await extraHost.save() - await MicroserviceExtraHostManager.updateOriginMicroserviceChangeTracking(extraHost, transaction) - } -} - -async function _deletePortMapping (microservice, portMapping, user, transaction) { - if (portMapping.isPublic) { - await _deletePublicPortMapping(microservice, portMapping, transaction) - } else if (portMapping.isProxy) { - return Proxy.deleteProxyPortMapping(microservice, portMapping, user, transaction) - } else { - await _deleteSimplePortMapping(microservice, portMapping, user, transaction) - } -} - -async function _updateOrDeleteProxyMicroservice (proxyId, proxyMapping, transaction) { - const proxy = await MicroserviceManager.findOne({ uuid: proxyId }, transaction) - if (!proxy) { - return - } - - const config = JSON.parse(proxy.config || '{}') - config.mappings = (config.mappings || []).filter(mapping => mapping !== proxyMapping) - if (config.mappings.length === 0) { - await MicroserviceManager.delete({ uuid: proxy.uuid }, transaction) - await ChangeTrackingService.update(proxy.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) - } else { - await MicroserviceManager.updateIfChanged({ uuid: proxy.uuid }, { config: JSON.stringify(config) }, transaction) - await ChangeTrackingService.update(proxy.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) - } -} - -async function _deletePublicPortMapping (microservice, portMapping, transaction) { - const publicPort = await portMapping.getPublicPort() - if (publicPort) { - const protocol = publicPort.isTcp ? 'tcp' : 'http' - const localMapping = `amqp:${publicPort.queueName}=>${protocol}:${portMapping.portExternal}` - const remoteMapping = `${protocol}:${publicPort.publicPort}=>amqp:${publicPort.queueName}` - - await _updateOrDeleteProxyMicroservice(publicPort.localProxyId, localMapping, transaction) - await _updateOrDeleteProxyMicroservice(publicPort.remoteProxyId, remoteMapping, transaction) - } - - await MicroservicePortManager.delete({ id: portMapping.id }, transaction) -} - -async function _createSimplePortMapping (microservice, portMappingData, user, transaction) { - // create port mapping - const mappingData = { - isPublic: false, - isProxy: false, - portInternal: portMappingData.internal, - portExternal: portMappingData.external, - userId: microservice.userId, - isUdp: portMappingData.protocol.toLowerCase() === 'udp', - microserviceUuid: microservice.uuid - } - - await MicroservicePortManager.create(mappingData, transaction) - await switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, false, transaction) -} - -async function _deleteSimplePortMapping (microservice, msPorts, user, transaction) { - await MicroservicePortManager.delete({ id: msPorts.id }, transaction) - - const updateRebuildMs = { - rebuild: true - } - await MicroserviceManager.update({ uuid: microservice.uuid }, updateRebuildMs, transaction) - await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) -} - -async function _buildPortsList (portsPairs, transaction) { - const res = [] - for (const ports of portsPairs) { - const portMappingResponseData = { - internal: ports.portInternal, - external: ports.portExternal, - protocol: ports.isUdp ? 'udp' : 'tcp' - } - await buildPublicPortMapping(ports, portMappingResponseData, transaction) - res.push(portMappingResponseData) - } - return res -} - -function _buildLink (protocol, host, port) { - return `${protocol || 'http'}://${host}:${port}` -} - -async function buildPublicPortMapping (pm, mapping, transaction) { - if (pm.isProxy) { - return Proxy.buildProxyPortMapping(pm, mapping, transaction) - } else if (!pm.isPublic) { - return - } - - const publicPortMapping = await pm.getPublicPort() - if (!publicPortMapping) { - return - } - - const port = publicPortMapping.publicPort - const hostRouter = publicPortMapping.hostId ? await RouterManager.findOne({ iofogUuid: publicPortMapping.hostId }, transaction) : { host: lget(await ConfigManager.findOne({ key: DEFAULT_PROXY_HOST }, transaction), 'value', 'undefined-proxy-host') } - const hostFog = publicPortMapping.hostId ? await FogManager.findOne({ uuid: publicPortMapping.hostId }, transaction) : { uuid: DEFAULT_ROUTER_NAME } - const schemes = JSON.parse(publicPortMapping.schemes) - mapping.public = { - links: schemes.map(s => _buildLink(s, hostRouter.host, port)), - protocol: publicPortMapping.protocol, - schemes, - enabled: !!publicPortMapping.localProxyId - } - mapping.public.router = { - port: port, - host: hostFog.isSystem ? DEFAULT_ROUTER_NAME : hostFog.uuid - } -} - -async function switchOnUpdateFlagsForMicroservicesForPortMapping (microservice, isPublic, transaction) { - const updateRebuildMs = { - rebuild: true - } - await MicroserviceManager.update({ uuid: microservice.uuid }, updateRebuildMs, transaction) - - if (isPublic) { - await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction) - } else { - await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) - } -} - -async function listPortMappings (microserviceUuid, user, isCLI, transaction) { - const where = isCLI - ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } - const microservice = await MicroserviceManager.findOne(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const portsPairs = await MicroservicePortManager.findAll({ microserviceUuid: microserviceUuid }, transaction) - return _buildPortsList(portsPairs, transaction) -} - -async function deletePortMapping (microserviceUuid, internalPort, user, isCLI, transaction) { - const where = isCLI - ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } - - const microservice = await MicroserviceManager.findOne(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - if (!internalPort) { - throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_INTERNAL_PORT_NOT_PROVIDED) - } - - const msPorts = await MicroservicePortManager.findOne({ - microserviceUuid: microservice.uuid, - portInternal: internalPort - }, transaction) - if (!msPorts) { - throw new Errors.NotFoundError('port mapping not exists') - } - - await _deletePortMapping(microservice, msPorts, user, transaction) -} - -async function deletePortMappings (microservice, user, transaction) { - const portMappings = await MicroservicePortManager.findAll({ microserviceUuid: microservice.uuid }, transaction) - for (const ports of portMappings) { - await _deletePortMapping(microservice, ports, user, transaction) - } -} - -async function getPortMappings (microserviceUuid, transaction) { - return MicroservicePortManager.findAll({ microserviceUuid: microserviceUuid }, transaction) -} - -function listAllPublicPorts (user, transaction) { - return MicroservicePortManager.findAllPublicPorts(transaction) -} - -module.exports = { - validatePublicPortAppHostTemplate: validatePublicPortAppHostTemplate, - validatePortMappings: validatePortMappings, - validatePortMapping: validatePortMapping, - movePublicPortsToNewFog: movePublicPortsToNewFog, - switchOnUpdateFlagsForMicroservicesForPortMapping: switchOnUpdateFlagsForMicroservicesForPortMapping, - createPortMapping: createPortMapping, - buildPublicPortMapping: buildPublicPortMapping, - listPortMappings: listPortMappings, - deletePortMapping: deletePortMapping, - deletePortMappings: deletePortMappings, - getPortMappings: getPortMappings, - listAllPublicPorts: listAllPublicPorts -} diff --git a/src/services/microservice-ports/factory.js b/src/services/microservice-ports/factory.js deleted file mode 100644 index e40c52e71..000000000 --- a/src/services/microservice-ports/factory.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const config = require('../../config') - -function createMicroservicePortProvider () { - let provider = process.env.MSVC_PORT_PROVIDER || config.get('PublicPorts:Provider', 'default') - - return require(`./${provider}`) -} - -module.exports = createMicroservicePortProvider() diff --git a/src/services/microservice-ports/microservice-port.js b/src/services/microservice-ports/microservice-port.js new file mode 100644 index 000000000..e866f191b --- /dev/null +++ b/src/services/microservice-ports/microservice-port.js @@ -0,0 +1,211 @@ +/* only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const MicroservicePortManager = require('../../data/managers/microservice-port-manager') +const MicroserviceManager = require('../../data/managers/microservice-manager') +const ChangeTrackingService = require('../change-tracking-service') +const AppHelper = require('../../helpers/app-helper') +const Errors = require('../../helpers/errors') +const ErrorMessages = require('../../helpers/error-messages') +const Op = require('sequelize').Op +const FogManager = require('../../data/managers/iofog-manager') + +const { RESERVED_PORTS } = require('../../helpers/constants') + +async function _checkForDuplicatePorts (agent, localPort, transaction) { + if (RESERVED_PORTS.find(port => port === localPort)) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_RESERVED, localPort)) + } + + const microservices = await agent.getMicroservice() + for (const microservice of microservices) { + const ports = await microservice.getPorts() + if (ports.find(port => port.portExternal === localPort)) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.PORT_NOT_AVAILABLE, localPort)) + } + } +} + +// Validate port and populate, mapping.localAgent +async function validatePortMapping (agent, mapping, transaction) { + await _checkForDuplicatePorts(agent, mapping.external, transaction) +} + +async function validatePortMappings (microserviceData, transaction) { + if (!microserviceData.ports || microserviceData.ports.length === 0) { + return + } + + const localAgent = await FogManager.findOne({ uuid: microserviceData.iofogUuid }, transaction) + if (!localAgent) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microserviceData.iofogUuid)) + } + + // Will be filled by validatePortMapping + for (const mapping of microserviceData.ports) { + await validatePortMapping(localAgent, mapping, transaction) + } +} + +async function createPortMapping (microservice, portMappingData, transaction) { + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) + } + + const msPorts = await MicroservicePortManager.findOne({ + microserviceUuid: microservice.uuid, + [Op.or]: [] + }, transaction) + if (msPorts) { + throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_ALREADY_EXISTS) + } + + portMappingData.protocol = portMappingData.protocol || '' + + return _createSimplePortMapping(microservice, portMappingData, transaction) +} + +async function _deletePortMapping (microservice, portMapping, transaction) { + await _deleteSimplePortMapping(microservice, portMapping, transaction) +} + +async function _createSimplePortMapping (microservice, portMappingData, transaction) { + // create port mapping + const mappingData = { + portInternal: portMappingData.internal, + portExternal: portMappingData.external, + isUdp: portMappingData.protocol.toLowerCase() === 'udp', + microserviceUuid: microservice.uuid + } + + await MicroservicePortManager.create(mappingData, transaction) + await switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) +} + +async function _deleteSimplePortMapping (microservice, msPorts, transaction) { + await MicroservicePortManager.delete({ id: msPorts.id }, transaction) + + const updateRebuildMs = { + rebuild: true + } + await MicroserviceManager.update({ uuid: microservice.uuid }, updateRebuildMs, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) +} + +async function _buildPortsList (portsPairs, transaction) { + const res = [] + for (const ports of portsPairs) { + const portMappingResponseData = { + internal: ports.portInternal, + external: ports.portExternal, + protocol: ports.isUdp ? 'udp' : 'tcp' + } + res.push(portMappingResponseData) + } + return res +} + +async function switchOnUpdateFlagsForMicroservicesForPortMapping (microservice, transaction) { + const updateRebuildMs = { + rebuild: true + } + await MicroserviceManager.update({ uuid: microservice.uuid }, updateRebuildMs, transaction) + + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) +} + +async function listPortMappings (microserviceUuid, isCLI, transaction) { + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const portsPairs = await MicroservicePortManager.findAll({ microserviceUuid }, transaction) + return _buildPortsList(portsPairs, transaction) +} + +async function deletePortMapping (microserviceUuid, internalPort, isCLI, transaction) { + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findMicroserviceOnGet(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + if (!internalPort) { + throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_INTERNAL_PORT_NOT_PROVIDED) + } + + const msPorts = await MicroservicePortManager.findOne({ + microserviceUuid: microservice.uuid, + portInternal: internalPort + }, transaction) + if (!msPorts) { + throw new Errors.NotFoundError('port mapping not exists') + } + + await _deletePortMapping(microservice, msPorts, transaction) +} + +async function deleteSystemPortMapping (microserviceUuid, internalPort, isCLI, transaction) { + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + if (!internalPort) { + throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_INTERNAL_PORT_NOT_PROVIDED) + } + + const msPorts = await MicroservicePortManager.findOne({ + microserviceUuid: microservice.uuid, + portInternal: internalPort + }, transaction) + if (!msPorts) { + throw new Errors.NotFoundError('port mapping not exists') + } + + await _deletePortMapping(microservice, msPorts, transaction) +} + +async function deletePortMappings (microservice, transaction) { + const portMappings = await MicroservicePortManager.findAll({ microserviceUuid: microservice.uuid }, transaction) + for (const ports of portMappings) { + await _deletePortMapping(microservice, ports, transaction) + } +} + +async function getPortMappings (microserviceUuid, transaction) { + return MicroservicePortManager.findAll({ microserviceUuid }, transaction) +} + +module.exports = { + validatePortMappings, + validatePortMapping, + switchOnUpdateFlagsForMicroservicesForPortMapping, + createPortMapping, + listPortMappings, + deletePortMapping, + deleteSystemPortMapping, + deletePortMappings, + getPortMappings +} diff --git a/src/services/microservice-ports/proxy.js b/src/services/microservice-ports/proxy.js deleted file mode 100644 index f6767c71b..000000000 --- a/src/services/microservice-ports/proxy.js +++ /dev/null @@ -1,224 +0,0 @@ -/* only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed - * ******************************************************************************* - * * Copyright (c) 2022 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ -const crypto = require('crypto') - -const AppHelper = require('../../helpers/app-helper') -const CatalogService = require('../catalog-service') -const ChangeTrackingService = require('../change-tracking-service') -const MicroserviceManager = require('../../data/managers/microservice-manager') -const MicroservicePortManager = require('../../data/managers/microservice-port-manager') -const MicroserviceProxyPortManager = require('../../data/managers/microservice-proxy-port-manager') -const ProxyBrokerClient = require('../../helpers/proxy-broker-client') - -async function _createOrUpdatePortRouterMicroservice (existingProxy, localPort, protocol, portRouterServerConfig, hostUuid, portRouterCatalogId, microserviceUuid, user, transaction) { - const proxyConfig = { - name: `${microserviceUuid}_${localPort}`, - server_addr: portRouterServerConfig.host, - server_port: portRouterServerConfig.adminPort, - local_port: localPort, - remote_port: portRouterServerConfig.proxyPort, - type: protocol, - proxy_token: portRouterServerConfig.proxyToken, - server_token: portRouterServerConfig.serverToken, - port_uuid: portRouterServerConfig.portUUID - } - - if (existingProxy) { - const config = JSON.parse(existingProxy.config || '{}') - config.proxies = (config.proxies || []).concat(proxyConfig) - existingProxy.config = JSON.stringify(config) - await MicroserviceManager.updateIfChanged({ uuid: existingProxy.uuid }, { config: JSON.stringify(config) }, transaction) - await ChangeTrackingService.update(hostUuid, ChangeTrackingService.events.microserviceConfig, transaction) - return existingProxy - } - - const proxyMicroserviceData = { - uuid: AppHelper.generateRandomString(32), - name: 'Proxy', - config: JSON.stringify({ - fetch_config_interval: 5, - proxies: [proxyConfig] - }), - catalogItemId: portRouterCatalogId, - iofogUuid: hostUuid, - rootHostAccess: true, - registryId: 1, - userId: user.id - } - const res = await MicroserviceManager.create(proxyMicroserviceData, transaction) - await ChangeTrackingService.update(hostUuid, ChangeTrackingService.events.microserviceCommon, transaction) - return res -} - -async function createProxyPortMapping (microservice, portMappingData, user, transaction) { - const protocol = portMappingData.protocol || 'tcp' - - // create proxy microservices - const portRouterCatalog = await CatalogService.getPortRouterCatalogItem(transaction) - - const existingProxy = await MicroserviceManager.findOne({ catalogItemId: portRouterCatalog.id, iofogUuid: microservice.iofogUuid }, transaction) - let serverToken = crypto.randomUUID() - if (existingProxy) { - const config = JSON.parse(existingProxy.config || '{}') - serverToken = config.proxies[0].server_token - } - - const portRouterServerConfig = await _portRouterServerConfig(serverToken) - - const localPortRouter = await _createOrUpdatePortRouterMicroservice( - existingProxy, - portMappingData.external, - protocol, - portRouterServerConfig, - microservice.iofogUuid, - portRouterCatalog.id, - microservice.uuid, - user, - transaction) - - const mappingData = { - isPublic: false, - isProxy: true, - portInternal: portMappingData.internal, - portExternal: portMappingData.external, - isUdp: protocol === 'udp', - userId: microservice.userId, - microserviceUuid: microservice.uuid - } - const port = await MicroservicePortManager.create(mappingData, transaction) - - const publicPort = { - portId: port.id, - host: portRouterServerConfig.host, - adminPort: portRouterServerConfig.adminPort, - localProxyId: localPortRouter.uuid, - publicPort: portRouterServerConfig.proxyPort, - proxyToken: portRouterServerConfig.proxyToken, - portUUID: portRouterServerConfig.portUUID, - serverToken: portRouterServerConfig.serverToken, - protocol - } - await MicroserviceProxyPortManager.create(publicPort, transaction) - - return { - proxy: { - host: publicPort.host, - port: publicPort.publicPort, - protocol: publicPort.protocol - } - } -} - -async function buildProxyPortMapping (pm, mapping, transaction) { - const proxyPortMapping = await pm.getProxyPort() - if (!proxyPortMapping) { - return - } - - mapping.proxy = { - host: proxyPortMapping.host, - port: proxyPortMapping.publicPort, - protocol: proxyPortMapping.protocol - } -} - -async function deleteProxyPortMapping (microservice, portMapping, user, transaction) { - const proxyPort = await portMapping.getProxyPort() - if (proxyPort) { - await _updateOrDeleteProxyMicroservice(proxyPort, false, transaction) - } - - await MicroservicePortManager.delete({ id: portMapping.id }, transaction) -} - -async function _updateOrDeleteProxyMicroservice (proxyPort, isMove, transaction) { - const proxy = await MicroserviceManager.findOne({ uuid: proxyPort.localProxyId }, transaction) - if (!proxy) { - return - } - - const config = JSON.parse(proxy.config || '{}') - config.proxies = (config.proxies || []).filter(mapping => mapping.remote_port !== proxyPort.publicPort) - const removeServerToken = config.proxies.length === 0 - - if (!isMove) { - await ProxyBrokerClient.deallocatePort(proxyPort.portUUID) - if (removeServerToken) { - await ProxyBrokerClient.revokeServerToken(proxyPort.serverToken) - } - } - - if (config.proxies.length === 0) { - await MicroserviceManager.delete({ uuid: proxy.uuid }, transaction) - await ChangeTrackingService.update(proxy.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) - } else { - await MicroserviceManager.updateIfChanged({ uuid: proxy.uuid }, { config: JSON.stringify(config) }, transaction) - await ChangeTrackingService.update(proxy.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) - } -} - -async function moveProxyPortsToNewFog (updatedMicroservice, portMapping, user, transaction) { - const proxyPort = await portMapping.getProxyPort() - const localProxy = await MicroserviceManager.findOne({ uuid: proxyPort.localProxyId }, transaction) - await _updateOrDeleteProxyMicroservice(proxyPort, true, transaction) - - const existingProxy = await MicroserviceManager.findOne({ catalogItemId: localProxy.catalogItemId, iofogUuid: updatedMicroservice.iofogUuid }, transaction) - let serverToken = crypto.randomUUID() - if (existingProxy) { - const config = JSON.parse(existingProxy.config || '{}') - serverToken = config.proxies[0].server_token - } - - const portRouterServerConfig = { - host: proxyPort.host, - adminPort: proxyPort.adminPort, - serverToken, - proxyPort: proxyPort.proxyPort, - proxyToken: proxyPort.proxyToken, - portUUID: proxyPort.portUUID - } - - const newProxy = await _createOrUpdatePortRouterMicroservice( - existingProxy, - portMapping.external, - portMapping.protocol, - portRouterServerConfig, - updatedMicroservice.iofogUuid, - localProxy.catalogItemId, - updatedMicroservice.uuid, - user, - transaction) - - proxyPort.localProxyId = newProxy.uuid - proxyPort.serverToken = portRouterServerConfig.serverToken - await MicroserviceProxyPortManager.updateOrCreate({ id: proxyPort.id }, proxyPort.toJSON(), transaction) -} - -async function _portRouterServerConfig (serverToken) { - const allocatedPort = await ProxyBrokerClient.allocatePort(serverToken) - return { - host: allocatedPort.serverAddr, - adminPort: allocatedPort.serverPort, - serverToken, - proxyPort: allocatedPort.proxyPort, - proxyToken: allocatedPort.proxyToken, - portUUID: allocatedPort.portUUID - } -} - -module.exports = { - createProxyPortMapping: createProxyPortMapping, - deleteProxyPortMapping: deleteProxyPortMapping, - buildProxyPortMapping: buildProxyPortMapping, - moveProxyPortsToNewFog: moveProxyPortsToNewFog -} diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 66ae6c0c1..90b6d779e 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -1,6 +1,6 @@ /* only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,11 +14,17 @@ const TransactionDecorator = require('../decorators/transaction-decorator') const MicroserviceManager = require('../data/managers/microservice-manager') const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') const MicroserviceArgManager = require('../data/managers/microservice-arg-manager') +const MicroserviceCdiDevManager = require('../data/managers/microservice-cdi-device-manager') +const MicroserviceCapAddManager = require('../data/managers/microservice-cap-add-manager') +const MicroserviceCapDropManager = require('../data/managers/microservice-cap-drop-manager') const MicroserviceEnvManager = require('../data/managers/microservice-env-manager') -const MicroservicePortService = require('../services/microservice-ports/factory') +const MicroservicePortService = require('../services/microservice-ports/microservice-port') +const MicroserviceHealthCheckManager = require('../data/managers/microservice-healthcheck-manager') const CatalogItemImageManager = require('../data/managers/catalog-item-image-manager') const RegistryManager = require('../data/managers/registry-manager') +// const RouterManager = require('../data/managers/router-manager') const MicroserviceStates = require('../enums/microservice-state') const VolumeMappingManager = require('../data/managers/volume-mapping-manager') const ChangeTrackingService = require('./change-tracking-service') @@ -30,27 +36,61 @@ const ApplicationManager = require('../data/managers/application-manager') const CatalogService = require('../services/catalog-service') const RoutingManager = require('../data/managers/routing-manager') const RoutingService = require('../services/routing-service') +const ServiceManager = require('../data/managers/service-manager') +const ServiceServices = require('./services-service') +const ConfigMapManager = require('../data/managers/config-map-manager') +const SecretManager = require('../data/managers/secret-manager') +const VolumeMountService = require('./volume-mount-service') + const Op = require('sequelize').Op const FogManager = require('../data/managers/iofog-manager') const MicroserviceExtraHostManager = require('../data/managers/microservice-extra-host-manager') const { VOLUME_MAPPING_DEFAULT } = require('../helpers/constants') const constants = require('../helpers/constants') const isEqual = require('lodash/isEqual') +const TagsManager = require('../data/managers/tags-manager') +const logger = require('../logger') + +async function _setPubTags (microserviceModel, tagsArray, transaction) { + if (tagsArray) { + let tags = [] + for (const tag of tagsArray) { + let tagModel = await TagsManager.findOne({ value: tag }, transaction) + if (!tagModel) { + tagModel = await TagsManager.create({ value: tag }, transaction) + } + tags.push(tagModel) + } + await microserviceModel.setPubTags(tags) + } +} + +async function _setSubTags (microserviceModel, tagsArray, transaction) { + if (tagsArray) { + let tags = [] + for (const tag of tagsArray) { + let tagModel = await TagsManager.findOne({ value: tag }, transaction) + if (!tagModel) { + tagModel = await TagsManager.create({ value: tag }, transaction) + } + tags.push(tagModel) + } + await microserviceModel.setSubTags(tags) + } +} -async function listMicroservicesEndPoint (opt, user, isCLI, transaction) { +async function listMicroservicesEndPoint (opt, isCLI, transaction) { // API retro compatibility const { applicationName, flowId } = opt - let application = await _validateApplication(applicationName, user, isCLI, transaction) + let application = await _validateApplication(applicationName, isCLI, transaction) if (flowId) { // _validateApplication wil try by ID if it fails finding by name - application = await _validateApplication(flowId, user, isCLI, transaction) + application = await _validateApplication(flowId, isCLI, transaction) } const where = application ? { applicationId: application.id, delete: false } : { delete: false, applicationId: { [Op.ne]: null } } - if (!isCLI) { - where.userId = user.id - } + const microservices = await MicroserviceManager.findAllExcludeFields(where, transaction) const res = await Promise.all(microservices.map(async (microservice) => { @@ -62,9 +102,45 @@ async function listMicroservicesEndPoint (opt, user, isCLI, transaction) { } } -async function getMicroserviceEndPoint (microserviceUuid, user, isCLI, transaction) { +async function listSystemMicroservicesEndPoint (opt, isCLI, transaction) { + const { applicationName, flowId } = opt + let application = await _validateSystemApplication(applicationName, isCLI, transaction) + + if (flowId) { + // _validateApplication wil try by ID if it fails finding by name + application = await _validateSystemApplication(flowId, isCLI, transaction) + } + const where = application ? { applicationId: application.id, delete: false } : { delete: false, applicationId: { [Op.ne]: null } } + + const microservices = await MicroserviceManager.findAllSystemExcludeFields(where, transaction) + const res = await Promise.all(microservices.map(async (microservice) => { + return _buildGetMicroserviceResponse(microservice.dataValues, transaction) + })) + + return { + microservices: res + } +} + +async function getMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + if (!isCLI) { + await _validateMicroserviceOnGet(microserviceUuid, transaction) + } + + const microservice = await MicroserviceManager.findOneExcludeFields({ + uuid: microserviceUuid, delete: false + }, transaction) + + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + return _buildGetMicroserviceResponse(microservice.dataValues, transaction) +} + +async function getSystemMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { if (!isCLI) { - await _validateMicroserviceOnGet(user.id, microserviceUuid, transaction) + await _validateSystemMicroserviceOnGet(microserviceUuid, transaction) } const microservice = await MicroserviceManager.findOneExcludeFields({ @@ -75,6 +151,11 @@ async function getMicroserviceEndPoint (microserviceUuid, user, isCLI, transacti throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) } + const app = await ApplicationManager.findOne({ id: microservice.applicationId }, transaction) + if (!app.isSystem) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + return _buildGetMicroserviceResponse(microservice.dataValues, transaction) } @@ -99,7 +180,7 @@ function _validateImagesAgainstCatalog (catalogItem, images) { } } -async function _validateLocalAppHostTemplate (extraHost, templateArgs, msvc, transaction) { +async function _validateLocalAppHostTemplate (extraHost, templateArgs, msvc, fogUuid, transaction) { if (templateArgs.length !== 4) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, templateArgs.join('.'))) } @@ -107,17 +188,21 @@ async function _validateLocalAppHostTemplate (extraHost, templateArgs, msvc, tra if (!fog) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_HOST_TEMPLATE, templateArgs[2])) } + if (fogUuid !== fog.uuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_APPS_TEMPLATE, msvc.name)) + } + extraHost.targetFogUuid = fog.uuid - extraHost.value = fog.host || fog.ipAddress + extraHost.value = `iofog_${msvc.uuid}` return extraHost } -async function _validateAppHostTemplate (extraHost, templateArgs, userId, transaction) { +async function _validateAppHostTemplate (extraHost, templateArgs, fogUuid, transaction) { if (templateArgs.length < 4) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, templateArgs.join('.'))) } - const application = await ApplicationManager.findOne({ name: templateArgs[1], userId }, transaction) + const application = await ApplicationManager.findOne({ name: templateArgs[1] }, transaction) if (!application) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_HOST_TEMPLATE, templateArgs[1])) } @@ -127,22 +212,19 @@ async function _validateAppHostTemplate (extraHost, templateArgs, userId, transa } extraHost.templateType = 'Apps' extraHost.targetMicroserviceUuid = msvc.uuid - if (templateArgs[3] === 'public') { - return MicroservicePortService.validatePublicPortAppHostTemplate(extraHost, templateArgs, msvc, transaction) - } if (templateArgs[3] === 'local') { - return _validateLocalAppHostTemplate(extraHost, templateArgs, msvc, transaction) + return _validateLocalAppHostTemplate(extraHost, templateArgs, msvc, fogUuid, transaction) } throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, templateArgs.join('.'))) } -async function _validateAgentHostTemplate (extraHost, templateArgs, userId, transaction) { +async function _validateAgentHostTemplate (extraHost, templateArgs, transaction) { if (templateArgs.length !== 2) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, templateArgs.join('.'))) } extraHost.templateType = 'Agents' - const fog = await FogManager.findOne({ name: templateArgs[1], userId }, transaction) + const fog = await FogManager.findOne({ name: templateArgs[1] }, transaction) if (!fog) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_HOST_TEMPLATE, templateArgs[1])) } @@ -152,7 +234,10 @@ async function _validateAgentHostTemplate (extraHost, templateArgs, userId, tran return extraHost } -async function _validateExtraHost (extraHostData, user, transaction) { +async function _validateExtraHost (extraHostData, fogUuid, transaction) { + if (extraHostData.name === 'service.local') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, 'Extra Host name cannot be service.local')) + } const extraHost = { templateType: 'Litteral', name: extraHostData.name, @@ -166,20 +251,20 @@ async function _validateExtraHost (extraHostData, user, transaction) { const templateArgs = template.split('.') extraHost.templateType = templateArgs[0] if (templateArgs[0] === 'Apps') { - return _validateAppHostTemplate(extraHost, templateArgs, user.id, transaction) + return _validateAppHostTemplate(extraHost, templateArgs, fogUuid, transaction) } else if (templateArgs[0] === 'Agents') { - return _validateAgentHostTemplate(extraHost, templateArgs, user.id, transaction) + return _validateAgentHostTemplate(extraHost, templateArgs, transaction) } throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_HOST_TEMPLATE, template)) } -async function _validateExtraHosts (microserviceData, user, transaction) { +async function _validateExtraHosts (microserviceData, fogUuid, transaction) { if (!microserviceData.extraHosts || microserviceData.extraHosts.length === 0) { return [] } const extraHosts = [] for (const extraHost of microserviceData.extraHosts) { - extraHosts.push(await _validateExtraHost(extraHost, user, transaction)) + extraHosts.push(await _validateExtraHost(extraHost, fogUuid, transaction)) } return extraHosts } @@ -197,20 +282,17 @@ function _validateImageFogType (microserviceData, fog, images) { } } -async function _findFog (microserviceData, user, isCLI, transaction) { +async function _findFog (microserviceData, isCLI, transaction) { const fogConditions = {} if (microserviceData.iofogUuid) { fogConditions.uuid = microserviceData.iofogUuid } else { - if (!isCLI) { - fogConditions.userId = user.id - } fogConditions.name = microserviceData.agentName } return FogManager.findOne(fogConditions, transaction) } -async function createMicroserviceEndPoint (microserviceData, user, isCLI, transaction) { +async function createMicroserviceEndPoint (microserviceData, isCLI, transaction) { // API Retro compatibility if (!microserviceData.application) { microserviceData.application = microserviceData.flowId @@ -218,7 +300,7 @@ async function createMicroserviceEndPoint (microserviceData, user, isCLI, transa await Validator.validate(microserviceData, Validator.schemas.microserviceCreate) // find fog - const fog = await _findFog(microserviceData, user, isCLI, transaction) + const fog = await _findFog(microserviceData, isCLI, transaction) if (!fog) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microserviceData.iofogUuid || microserviceData.agentName)) } @@ -229,10 +311,14 @@ async function createMicroserviceEndPoint (microserviceData, user, isCLI, transa // validate images if (microserviceData.catalogItemId) { // validate catalog item - const catalogItem = await CatalogService.getCatalogItem(microserviceData.catalogItemId, user, isCLI, transaction) + const catalogItem = await CatalogService.getCatalogItem(microserviceData.catalogItemId, isCLI, transaction) _validateImagesAgainstCatalog(catalogItem, microserviceData.images || []) microserviceData.images = catalogItem.images _validateImageFogType(microserviceData, fog, catalogItem.images) + // use catalog item's registryId if it is set + if (catalogItem.registryId) { + microserviceData.registryId = catalogItem.registryId + } } else { _validateImageFogType(microserviceData, fog, microserviceData.images) } @@ -242,75 +328,130 @@ async function createMicroserviceEndPoint (microserviceData, user, isCLI, transa } // validate extraHosts - const extraHosts = await _validateExtraHosts(microserviceData, user, transaction) + const extraHosts = await _validateExtraHosts(microserviceData, fog.uuid, transaction) await MicroservicePortService.validatePortMappings(microserviceData, transaction) _validateVolumeMappings(microserviceData.volumeMappings) - const microservice = await _createMicroservice({ ...microserviceData, iofogUuid: fog.uuid }, user, isCLI, transaction) + const microservice = await _createMicroservice({ ...microserviceData, iofogUuid: fog.uuid }, isCLI, transaction) if (!microserviceData.catalogItemId) { await _createMicroserviceImages(microservice, microserviceData.images, transaction) } - const publicPorts = [] - const proxyPorts = [] + // const publicPorts = [] + // const proxyPorts = [] if (microserviceData.ports) { for (const mapping of microserviceData.ports) { - const res = await MicroservicePortService.createPortMapping(microservice, mapping, user, transaction) - if (res) { - if (res.publicLinks) { - publicPorts.push({ - internal: mapping.internal, - external: mapping.external, - publicLinks: res.publicLinks - }) - } else if (res.proxy) { - proxyPorts.push({ - internal: mapping.internal, - external: mapping.external, - proxy: res.proxy - }) - } - } + // const res = await MicroservicePortService.createPortMapping(microservice, mapping, transaction) + await MicroservicePortService.createPortMapping(microservice, mapping, transaction) + // if (res) { + // if (res.publicLinks) { + // publicPorts.push({ + // internal: mapping.internal, + // external: mapping.external, + // publicLinks: res.publicLinks + // }) + // } else if (res.proxy) { + // proxyPorts.push({ + // internal: mapping.internal, + // external: mapping.external, + // proxy: res.proxy + // }) + // } + // } } } for (const extraHost of extraHosts) { - await _createExtraHost(microservice, extraHost, user, transaction) + await _createExtraHost(microservice, extraHost, transaction) } if (microserviceData.env) { for (const env of microserviceData.env) { - await _createEnv(microservice, env, user, transaction) + await _createEnv(microservice, env, transaction) } } if (microserviceData.cmd) { for (const arg of microserviceData.cmd) { - await _createArg(microservice, arg, user, transaction) + await _createArg(microservice, arg, transaction) + } + } + if (microserviceData.cdiDevices) { + for (const cdiDevices of microserviceData.cdiDevices) { + await _createCdiDevices(microservice, cdiDevices, transaction) + } + } + if (microserviceData.healthCheck) { + const healthCheckData = { + microserviceUuid: microservice.uuid, + ..._processHealthCheckForDB(microserviceData.healthCheck) + } + await MicroserviceHealthCheckManager.create(healthCheckData, transaction) + } + if (microserviceData.capAdd) { + for (const capAdd of microserviceData.capAdd) { + await _createCapAdd(microservice, capAdd, transaction) + } + } + if (microserviceData.capDrop) { + for (const capDrop of microserviceData.capDrop) { + await _createCapDrop(microservice, capDrop, transaction) } } if (microserviceData.volumeMappings) { await _createVolumeMappings(microservice, microserviceData.volumeMappings, transaction) } + if (microserviceData.pubTags) { + await _setPubTags(microservice, microserviceData.pubTags, transaction) + } + + if (microserviceData.subTags) { + await _setSubTags(microservice, microserviceData.subTags, transaction) + const fogsNeedUpdate = new Set() + for (const tag of microserviceData.subTags) { + try { + const where = { + delete: false, + '$pubTags.value$': tag + } + // Get fog nodes with microservices for the given pubTag + const response = await MicroserviceManager.findAllExcludeFields(where, transaction, { attributes: ['iofogUuid'] }) + if (response.length > 0) { + response.forEach(ms => ms.iofogUuid && fogsNeedUpdate.add(ms.iofogUuid)) + } + } catch (error) { + logger.error(`Checking fog nodes list for pubTag "${tag.value}":`, error.message) + } + } + for (const fog of fogsNeedUpdate) { + try { + await ChangeTrackingService.update(fog, ChangeTrackingService.events.microserviceFull, transaction) + } catch (error) { + logger.error(`Updating change tracking for fog "${fog.value}":`, error.message) + } + } + } + if (microserviceData.iofogUuid) { await _updateChangeTracking(false, microserviceData.iofogUuid, transaction) } await _createMicroserviceStatus(microservice, transaction) + await _createMicroserviceExecStatus(microservice, transaction) const res = { uuid: microservice.uuid, name: microservice.name } - if (publicPorts.length) { - res.publicPorts = publicPorts - } - if (proxyPorts.length) { - res.proxies = proxyPorts - } + // if (publicPorts.length) { + // res.publicPorts = publicPorts + // } + // if (proxyPorts.length) { + // res.proxies = proxyPorts + // } return res } @@ -327,6 +468,118 @@ function _validateVolumeMappings (volumeMappings) { } } +function _validateKeyPath (data, keyPath, resourceName, resourceType, volumeMountName) { + if (!keyPath || keyPath === '') { + return true // No key path to validate + } + + // ConfigMap and Secret keys are always flat - they're strings that can contain slashes + // The key path can be: + // 1. A full key name: "foo/bar/baz.conf" - maps to the file + // 2. A prefix of a key: "foo" - maps to the directory containing keys starting with "foo/" + // 3. A nested prefix: "foo/bar" - maps to the directory containing keys starting with "foo/bar/" + // + // For validation, we check if there's at least one key in the data that starts with the given keyPath + // This allows mapping entire directories (prefixes) or specific files (exact match) + + // First, check for exact match (full file path) + if (data[keyPath] !== undefined && data[keyPath] !== null) { + return true // Exact key exists + } + + // If no exact match, check if any key starts with the keyPath followed by '/' + // This validates that the keyPath is a valid prefix for directory mapping + // Handle trailing slash: if keyPath already ends with '/', use it as-is; otherwise add '/' + const keyPathWithSlash = keyPath.endsWith('/') ? keyPath : keyPath + '/' + const hasMatchingKey = Object.keys(data).some(key => key.startsWith(keyPathWithSlash)) + + if (hasMatchingKey) { + return true // Key path is a valid prefix for directory mapping + } + + // No exact match and no keys with this prefix - key path is invalid + if (resourceType === 'Secret') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SECRET_KEY_NOT_FOUND_IN_VOLUME_MOUNT, keyPath, resourceName, volumeMountName)) + } else { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_KEY_NOT_FOUND_IN_VOLUME_MOUNT, keyPath, resourceName, volumeMountName)) + } +} + +async function _validateVolumeMountReference (hostDestination, fogUuid, transaction) { + if (!hostDestination || typeof hostDestination !== 'string') { + return // No validation needed if hostDestination is empty or not a string + } + + // Check if hostDestination starts with $VolumeMount/ + if (!hostDestination.startsWith('$VolumeMount/')) { + return // Not a volume mount reference, skip validation + } + + // Parse the volume mount reference: $VolumeMount// + const withoutPrefix = hostDestination.substring('$VolumeMount/'.length) + if (!withoutPrefix || withoutPrefix === '') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MOUNT_REFERENCE_FOR_VOLUME_MAPPING, 'Volume mount reference must include a volume mount name')) + } + + // Split by '/' to separate volume mount name and optional key path + const parts = withoutPrefix.split('/') + const volumeMountName = parts[0] + const keyPath = parts.length > 1 ? parts.slice(1).join('/') : null + + if (!volumeMountName || volumeMountName === '') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MOUNT_REFERENCE_FOR_VOLUME_MAPPING, 'Volume mount name cannot be empty')) + } + + // Validate volume mount exists + let volumeMount + try { + volumeMount = await VolumeMountService.getVolumeMountEndpoint(volumeMountName, transaction) + } catch (error) { + if (error instanceof Errors.NotFoundError) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.VOLUME_MOUNT_NOT_FOUND, volumeMountName)) + } + throw error + } + + // If key path is provided, validate it exists in the secret/configmap + if (keyPath && keyPath !== '') { + if (volumeMount.secretName) { + const secret = await SecretManager.getSecret(volumeMount.secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, volumeMount.secretName)) + } + _validateKeyPath(secret.data, keyPath, volumeMount.secretName, 'Secret', volumeMountName) + } else if (volumeMount.configMapName) { + const configMap = await ConfigMapManager.getConfigMap(volumeMount.configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, volumeMount.configMapName)) + } + _validateKeyPath(configMap.data, keyPath, volumeMount.configMapName, 'ConfigMap', volumeMountName) + } else { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MOUNT_REFERENCE_FOR_VOLUME_MAPPING, `Volume mount ${volumeMountName} does not have a secret or configmap associated`)) + } + } + + // Check if volume mount is linked to the fog node + let linkedFogUuids + try { + linkedFogUuids = await VolumeMountService.findVolumeMountedFogNodes(volumeMountName, transaction) + } catch (error) { + // If volume mount doesn't exist (shouldn't happen at this point), rethrow + throw error + } + + // If fog node is not linked, link it + if (!linkedFogUuids.includes(fogUuid)) { + try { + await VolumeMountService.linkVolumeMountEndpoint(volumeMountName, [fogUuid], transaction) + } catch (error) { + logger.error(`Failed to link volume mount ${volumeMountName} to fog node ${fogUuid}:`, error.message) + throw new Errors.ValidationError(`Failed to link volume mount ${volumeMountName} to fog node: ${error.message}`) + } + } +} + async function _updateRelatedExtraHostTargetFog (extraHost, newFogUuid, transaction) { const fog = await FogManager.findOne({ uuid: newFogUuid }, transaction) if (!fog) { @@ -342,16 +595,20 @@ async function _updateRelatedExtraHostTargetFog (extraHost, newFogUuid, transact async function _updateRelatedExtraHosts (updatedMicroservice, transaction) { const extraHosts = await MicroserviceExtraHostManager.findAll({ targetMicroserviceUuid: updatedMicroservice.uuid }, transaction) for (const extraHost of extraHosts) { - if (!extraHost.publicPort) { - // Local port, update target fog and host if microservice moved - if (extraHost.targetFogUuid !== updatedMicroservice.iofogUuid) { - await _updateRelatedExtraHostTargetFog(extraHost, updatedMicroservice.iofogUuid, transaction) - } + // if (!extraHost.publicPort) { + // // Local port, update target fog and host if microservice moved + // if (extraHost.targetFogUuid !== updatedMicroservice.iofogUuid) { + // await _updateRelatedExtraHostTargetFog(extraHost, updatedMicroservice.iofogUuid, transaction) + // } + // } + // Local port, update target fog and host if microservice moved + if (extraHost.targetFogUuid !== updatedMicroservice.iofogUuid) { + await _updateRelatedExtraHostTargetFog(extraHost, updatedMicroservice.iofogUuid, transaction) } } } -async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, user, isCLI, transaction, changeTrackingEnabled = true) { +async function updateSystemMicroserviceEndPoint (microserviceUuid, microserviceData, isCLI, transaction, changeTrackingEnabled = true) { await Validator.validate(microserviceData, Validator.schemas.microserviceUpdate) let needStatusReset = false const query = isCLI @@ -359,30 +616,46 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u uuid: microserviceUuid } : { - uuid: microserviceUuid, - userId: user.id + uuid: microserviceUuid } + const newFog = await _findFog(microserviceData, isCLI, transaction) || {} // validate extraHosts - const extraHosts = microserviceData.extraHosts ? await _validateExtraHosts(microserviceData, user, transaction) : null + const extraHosts = microserviceData.extraHosts ? await _validateExtraHosts(microserviceData, newFog.uuid, transaction) : null const config = _validateMicroserviceConfig(microserviceData.config) - const newFog = await _findFog(microserviceData, user, isCLI, transaction) || {} + const annotations = _validateMicroserviceAnnotations(microserviceData.annotations) + + // const newFog = await _findFog(microserviceData, isCLI, transaction) || {} const microserviceToUpdate = { name: microserviceData.name, config: config, + annotations: annotations, images: microserviceData.images, catalogItemId: microserviceData.catalogItemId, rebuild: microserviceData.rebuild, iofogUuid: newFog.uuid, - rootHostAccess: microserviceData.rootHostAccess, + hostNetworkMode: microserviceData.hostNetworkMode, + isPrivileged: microserviceData.isPrivileged, + cpuSetCpus: microserviceData.cpuSetCpus, + memoryLimit: microserviceData.memoryLimit, + schedule: microserviceData.schedule, + pidMode: microserviceData.pidMode, + ipcMode: microserviceData.ipcMode, + cdiDevices: microserviceData.cdiDevices, + capAdd: microserviceData.capAdd, + capDrop: microserviceData.capDrop, + runAsUser: microserviceData.runAsUser, + platform: microserviceData.platform, + runtime: microserviceData.runtime, logSize: (microserviceData.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1, registryId: microserviceData.registryId, volumeMappings: microserviceData.volumeMappings, env: microserviceData.env, cmd: microserviceData.cmd, - ports: microserviceData.ports + ports: microserviceData.ports, + healthCheck: microserviceData.healthCheck } const microserviceDataUpdate = AppHelper.deleteUndefinedFields(microserviceToUpdate) @@ -406,7 +679,7 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u } if (microserviceDataUpdate.ports) { - await _updatePorts(microserviceDataUpdate.ports, microservice, user, transaction) + await _updateSystemPorts(microserviceDataUpdate.ports, microservice, transaction) } if (microserviceDataUpdate.iofogUuid && microservice.iofogUuid !== microserviceDataUpdate.iofogUuid) { @@ -431,15 +704,11 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u needStatusReset = true } - if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) - } - // Validate images vs catalog item const iofogUuid = microserviceDataUpdate.iofogUuid || microservice.iofogUuid if (microserviceDataUpdate.catalogItemId) { - const catalogItem = await CatalogService.getCatalogItem(microserviceDataUpdate.catalogItemId, user, isCLI, transaction) + const catalogItem = await CatalogService.getSystemCatalogItem(microserviceDataUpdate.catalogItemId, isCLI, transaction) _validateImagesAgainstCatalog(catalogItem, microserviceDataUpdate.images || []) if (microserviceDataUpdate.catalogItemId !== undefined && microserviceDataUpdate.catalogItemId !== microservice.catalogItemId) { // Catalog item changed or removed, set rebuild flag @@ -449,6 +718,11 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u await _deleteImages(microserviceUuid, transaction) microserviceDataUpdate.registryId = catalogItem.registryId || 1 } + } else { + // use catalog item's registryId if it is set + if (catalogItem.registryId) { + microserviceDataUpdate.registryId = catalogItem.registryId + } } } else if (!microservice.catalogItemId && microserviceDataUpdate.images && microserviceDataUpdate.images.length === 0) { // No catalog, and no image @@ -461,8 +735,7 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u } if (microserviceDataUpdate.name) { - const userId = isCLI ? microservice.userId : user.id - await _checkForDuplicateName(microserviceDataUpdate.name, { id: microserviceUuid }, userId, microservice.applicationId || microservice.application, transaction) + await _checkForDuplicateName(microserviceDataUpdate.name, { id: microserviceUuid }, microservice.applicationId || microservice.application, transaction) } // validate fog node @@ -475,12 +748,12 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u // Validate image type let images = [] if (microserviceDataUpdate.catalogItemId) { - const catalogItem = await CatalogService.getCatalogItem(microserviceDataUpdate.catalogItemId, user, isCLI, transaction) + const catalogItem = await CatalogService.getSystemCatalogItem(microserviceDataUpdate.catalogItemId, isCLI, transaction) images = catalogItem.images } else if (microserviceDataUpdate.images) { images = microserviceDataUpdate.images } else if (microservice.catalogItemId) { - const catalogItem = await CatalogService.getCatalogItem(microservice.catalogItemId, user, isCLI, transaction) + const catalogItem = await CatalogService.getSystemCatalogItem(microservice.catalogItemId, isCLI, transaction) images = catalogItem.images } else { images = await microservice.getImages() @@ -490,17 +763,30 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u // Set rebuild flag if needed microserviceDataUpdate.rebuild = microserviceDataUpdate.rebuild || !!( - (microserviceDataUpdate.rootHostAccess !== undefined && microservice.rootHostAccess !== microserviceDataUpdate.rootHostAccess) || + (microserviceDataUpdate.hostNetworkMode !== undefined && microservice.hostNetworkMode !== microserviceDataUpdate.hostNetworkMode) || + (microserviceDataUpdate.isPrivileged !== undefined && microservice.isPrivileged !== microserviceDataUpdate.isPrivileged) || + microserviceDataUpdate.pidMode || + microserviceDataUpdate.ipcMode || + microserviceDataUpdate.cpuSetCpus || + microserviceDataUpdate.memoryLimit || microserviceDataUpdate.env || microserviceDataUpdate.cmd || + microserviceDataUpdate.cdiDevices || + microserviceDataUpdate.annotations || + microserviceDataUpdate.capAdd || + microserviceDataUpdate.capDrop || + microserviceDataUpdate.runAsUser || + microserviceDataUpdate.platform || + microserviceDataUpdate.runtime || microserviceDataUpdate.volumeMappings || microserviceDataUpdate.ports || + microserviceDataUpdate.schedule || extraHosts ) const updatedMicroservice = await MicroserviceManager.updateAndFind(query, microserviceDataUpdate, transaction) if (extraHosts) { - await _updateExtraHosts(extraHosts, microserviceUuid, user, transaction) + await _updateExtraHosts(extraHosts, microserviceUuid, transaction) } // Update extra hosts that reference this microservice @@ -518,8 +804,29 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u await _updateArg(microserviceDataUpdate.cmd, microserviceUuid, transaction) } - if (microserviceDataUpdate.iofogUuid && microserviceDataUpdate.iofogUuid !== microservice.iofogUuid) { - await MicroservicePortService.movePublicPortsToNewFog(updatedMicroservice, user, transaction) + if (microserviceDataUpdate.cdiDevices) { + await _updateCdiDevices(microserviceDataUpdate.cdiDevices, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.capAdd) { + await _updateCapAdd(microserviceDataUpdate.capAdd, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.healthCheck) { + await MicroserviceHealthCheckManager.delete({ + microserviceUuid: microservice.uuid + }, transaction) + const healthCheckData = { + microserviceUuid: microservice.uuid, + ..._processHealthCheckForDB(microserviceDataUpdate.healthCheck) + } + if (healthCheckData.test && healthCheckData.test.length > 0) { + await MicroserviceHealthCheckManager.create(healthCheckData, transaction) + } + } + + if (microserviceDataUpdate.capDrop) { + await _updateCapDrop(microserviceDataUpdate.capDrop, microserviceUuid, transaction) } if (needStatusReset) { @@ -551,137 +858,682 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, u } } -/** - * checks if microservice image is updated - * @param {*} microserviceDataUpdateImages - * @param {*} catalogImages - */ -const _checkIfMicroserviceImagesAreEqual = (microserviceDataUpdateImages, catalogImages) => { - const oldMicroservicesImages = [] - for (const images of catalogImages) { - oldMicroservicesImages.push(images.containerImage) - } - const newMicroserviceImages = [] - for (const images of microserviceDataUpdateImages) { - newMicroserviceImages.push(images.containerImage) - } - return isEqual(newMicroserviceImages, oldMicroservicesImages) -} - -async function deleteMicroserviceEndPoint (microserviceUuid, microserviceData, user, isCLI, transaction) { - const where = isCLI +async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, isCLI, transaction, changeTrackingEnabled = true) { + await Validator.validate(microserviceData, Validator.schemas.microserviceUpdate) + let needStatusReset = false + const query = isCLI ? { uuid: microserviceUuid } : { - uuid: microserviceUuid, - userId: user.id + uuid: microserviceUuid } - const microservice = await MicroserviceManager.findOneWithStatusAndCategory(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - if (!isCLI && microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_DELETE, microserviceUuid)) - } - - await deleteMicroserviceWithRoutesAndPortMappings(microservice, transaction) - - await _updateChangeTracking(false, microservice.iofogUuid, transaction) -} + const newFog = await _findFog(microserviceData, isCLI, transaction) || {} + // validate extraHosts + const extraHosts = microserviceData.extraHosts ? await _validateExtraHosts(microserviceData, newFog.uuid, transaction) : null -async function deleteNotRunningMicroservices (fog, transaction) { - const microservices = await MicroserviceManager.findAllWithStatuses({ iofogUuid: fog.uuid }, transaction) - microservices - .filter((microservice) => microservice.delete) - .filter((microservice) => microservice.microserviceStatus.status === MicroserviceStates.UNKNOWN || - microservice.microserviceStatus.status === MicroserviceStates.STOPPING || - microservice.microserviceStatus.status === MicroserviceStates.DELETING || - microservice.microserviceStatus.status === MicroserviceStates.MARKED_FOR_DELETION) - .forEach(async (microservice) => { await deleteMicroserviceWithRoutesAndPortMappings(microservice, transaction) }) -} + const config = _validateMicroserviceConfig(microserviceData.config) -async function createRouteEndPoint (sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { - // Print deprecated warning - const sourceMsvc = await MicroserviceManager.findOne({ uuid: sourceMicroserviceUuid }, transaction) - if (!sourceMsvc) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_SOURCE_MICROSERVICE_UUID, sourceMicroserviceUuid)) - } - const destMsvc = await MicroserviceManager.findOne({ uuid: destMicroserviceUuid }, transaction) - if (!destMsvc) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_SOURCE_MICROSERVICE_UUID, destMsvc)) - } + const annotations = _validateMicroserviceAnnotations(microserviceData.annotations) - const application = await ApplicationManager.findOne({ id: sourceMsvc.applicationId }, transaction) - if (!application) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, sourceMsvc.applicationId)) + // const newFog = await _findFog(microserviceData, isCLI, transaction) || {} + const microserviceToUpdate = { + name: microserviceData.name, + config: config, + annotations: annotations, + images: microserviceData.images, + catalogItemId: microserviceData.catalogItemId, + rebuild: microserviceData.rebuild, + iofogUuid: newFog.uuid, + hostNetworkMode: microserviceData.hostNetworkMode, + isPrivileged: microserviceData.isPrivileged, + cpuSetCpus: microserviceData.cpuSetCpus, + memoryLimit: microserviceData.memoryLimit, + schedule: microserviceData.schedule, + pidMode: microserviceData.pidMode, + ipcMode: microserviceData.ipcMode, + cdiDevices: microserviceData.cdiDevices, + capAdd: microserviceData.capAdd, + capDrop: microserviceData.capDrop, + runAsUser: microserviceData.runAsUser, + platform: microserviceData.platform, + runtime: microserviceData.runtime, + logSize: (microserviceData.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1, + registryId: microserviceData.registryId, + volumeMappings: microserviceData.volumeMappings, + env: microserviceData.env, + cmd: microserviceData.cmd, + ports: microserviceData.ports, + healthCheck: microserviceData.healthCheck } - return RoutingService.createRouting({ application: application.name, from: sourceMsvc.name, to: destMsvc.name, name: `r-${sourceMsvc.name}-${destMsvc.name}` }, user, isCLI, transaction) -} + const microserviceDataUpdate = AppHelper.deleteUndefinedFields(microserviceToUpdate) -async function deleteRouteEndPoint (sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { - // Print deprecated warning + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) - const route = await RoutingManager.findOnePopulated({ - sourceMicroserviceUuid: sourceMicroserviceUuid, - destMicroserviceUuid: destMicroserviceUuid + const microserviceImages = await CatalogItemImageManager.findAll({ + microservice_uuid: microserviceUuid }, transaction) - if (!route) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.ROUTE_NOT_FOUND)) - } - return RoutingService.deleteRouting(route.application.name, route.name, user, isCLI, transaction) -} - -async function createPortMappingEndPoint (microserviceUuid, portMappingData, user, isCLI, transaction) { - await Validator.validate(portMappingData, Validator.schemas.portsCreate) - - const where = isCLI - ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } - - const microservice = await MicroserviceManager.findOne(where, transaction) if (!microservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) } - - const agent = await FogManager.findOne({ uuid: microservice.iofogUuid }, transaction) - if (!agent) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microservice.iofogUuid)) + if (microserviceDataUpdate.registryId) { + const registry = await RegistryManager.findOne({ id: microserviceDataUpdate.registryId }, transaction) + if (!registry) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, microserviceDataUpdate.registryId)) + } + } else { + microserviceDataUpdate.registryId = microservice.registryId } - await MicroservicePortService.validatePortMapping(agent, portMappingData, {}, transaction) - return MicroservicePortService.createPortMapping(microservice, portMappingData, user, transaction) -} - -async function _createExtraHost (microservice, extraHostData, user, transaction) { - const msExtraHostData = { - ...extraHostData, - microserviceUuid: microservice.uuid + if (microserviceDataUpdate.ports) { + await _updatePorts(microserviceDataUpdate.ports, microservice, transaction) } - await MicroserviceExtraHostManager.create(msExtraHostData, transaction) -} + if (microserviceDataUpdate.iofogUuid && microservice.iofogUuid !== microserviceDataUpdate.iofogUuid) { + // Moving to new agent + // make sure all ports are available + const ports = await microservice.getPorts() + const data = { + ports: [], + iofogUuid: microserviceDataUpdate.iofogUuid + } -async function _createEnv (microservice, envData, user, transaction) { - if (!microservice.iofogUuid) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) - } + for (const port of ports) { + data.ports.push({ + internal: port.portInternal, + external: port.portExternal + }) + } - const msEnvData = { - key: envData.key, - value: envData.value, - userId: microservice.userId, + if (data.ports.length) { + await MicroservicePortService.validatePortMappings(data, transaction) + } + needStatusReset = true + } + + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + // Validate images vs catalog item + + const iofogUuid = microserviceDataUpdate.iofogUuid || microservice.iofogUuid + if (microserviceDataUpdate.catalogItemId) { + const catalogItem = await CatalogService.getCatalogItem(microserviceDataUpdate.catalogItemId, isCLI, transaction) + _validateImagesAgainstCatalog(catalogItem, microserviceDataUpdate.images || []) + if (microserviceDataUpdate.catalogItemId !== undefined && microserviceDataUpdate.catalogItemId !== microservice.catalogItemId) { + // Catalog item changed or removed, set rebuild flag + microserviceDataUpdate.rebuild = true + // If catalog item is set, set registry and msvc images + if (microserviceDataUpdate.catalogItemId) { + await _deleteImages(microserviceUuid, transaction) + microserviceDataUpdate.registryId = catalogItem.registryId || 1 + } + } else { + // use catalog item's registryId if it is set + if (catalogItem.registryId) { + microserviceDataUpdate.registryId = catalogItem.registryId + } + } + } else if (!microservice.catalogItemId && microserviceDataUpdate.images && microserviceDataUpdate.images.length === 0) { + // No catalog, and no image + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.MICROSERVICE_DOES_NOT_HAVE_IMAGES, microserviceData.name)) + } else if (microserviceDataUpdate.images && microserviceDataUpdate.images.length > 0 && !_checkIfMicroserviceImagesAreEqual(microserviceDataUpdate.images, microserviceImages)) { + // No catalog, and images + await _updateImages(microserviceDataUpdate.images, microserviceUuid, transaction) + // Images updated, set rebuild flag to true + microserviceDataUpdate.rebuild = true + } + + if (microserviceDataUpdate.name) { + await _checkForDuplicateName(microserviceDataUpdate.name, { id: microserviceUuid }, microservice.applicationId || microservice.application, transaction) + } + + // validate fog node + if (iofogUuid) { + const fog = await FogManager.findOne({ uuid: iofogUuid }, transaction) + if (!fog || fog.length === 0) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, iofogUuid)) + } + + // Validate image type + let images = [] + if (microserviceDataUpdate.catalogItemId) { + const catalogItem = await CatalogService.getCatalogItem(microserviceDataUpdate.catalogItemId, isCLI, transaction) + images = catalogItem.images + } else if (microserviceDataUpdate.images) { + images = microserviceDataUpdate.images + } else if (microservice.catalogItemId) { + const catalogItem = await CatalogService.getCatalogItem(microservice.catalogItemId, isCLI, transaction) + images = catalogItem.images + } else { + images = await microservice.getImages() + } + _validateImageFogType(microserviceData, fog, images) + } + + // Set rebuild flag if needed + microserviceDataUpdate.rebuild = microserviceDataUpdate.rebuild || !!( + (microserviceDataUpdate.hostNetworkMode !== undefined && microservice.hostNetworkMode !== microserviceDataUpdate.hostNetworkMode) || + (microserviceDataUpdate.isPrivileged !== undefined && microservice.isPrivileged !== microserviceDataUpdate.isPrivileged) || + microserviceDataUpdate.pidMode || + microserviceDataUpdate.ipcMode || + microserviceDataUpdate.cpuSetCpus || + microserviceDataUpdate.memoryLimit || + microserviceDataUpdate.env || + microserviceDataUpdate.cmd || + microserviceDataUpdate.cdiDevices || + microserviceDataUpdate.capAdd || + microserviceDataUpdate.capDrop || + microserviceDataUpdate.annotations || + microserviceDataUpdate.runAsUser || + microserviceDataUpdate.platform || + microserviceDataUpdate.runtime || + microserviceDataUpdate.volumeMappings || + microserviceDataUpdate.ports || + microserviceDataUpdate.schedule || + extraHosts + ) + const updatedMicroservice = await MicroserviceManager.updateAndFind(query, microserviceDataUpdate, transaction) + + if (extraHosts) { + await _updateExtraHosts(extraHosts, microserviceUuid, transaction) + } + + // Update extra hosts that reference this microservice + await _updateRelatedExtraHosts(updatedMicroservice, transaction) + + if (microserviceDataUpdate.volumeMappings) { + await _updateVolumeMappings(microserviceDataUpdate.volumeMappings, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.env) { + await _updateEnv(microserviceDataUpdate.env, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.cmd) { + await _updateArg(microserviceDataUpdate.cmd, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.cdiDevices) { + await _updateCdiDevices(microserviceDataUpdate.cdiDevices, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.capAdd) { + await _updateCapAdd(microserviceDataUpdate.capAdd, microserviceUuid, transaction) + } + + if (microserviceDataUpdate.healthCheck) { + await MicroserviceHealthCheckManager.delete({ + microserviceUuid: microservice.uuid + }, transaction) + const healthCheckData = { + microserviceUuid: microservice.uuid, + ..._processHealthCheckForDB(microserviceDataUpdate.healthCheck) + } + if (healthCheckData.test && healthCheckData.test.length > 0) { + await MicroserviceHealthCheckManager.create(healthCheckData, transaction) + } + } + + if (microserviceDataUpdate.capDrop) { + await _updateCapDrop(microserviceDataUpdate.capDrop, microserviceUuid, transaction) + } + + const existingService = await ServiceManager.findOne({ type: `microservice`, resource: microservice.uuid }, transaction) + if (microserviceDataUpdate.iofogUuid && microserviceDataUpdate.iofogUuid !== microservice.iofogUuid && existingService) { + await ServiceServices.moveMicroserviceTcpBridgeToNewFog(existingService, microserviceDataUpdate.iofogUuid, microservice.iofogUuid, transaction) + } + + // Update tags + if (microserviceData.pubTags) { + await _setPubTags(microservice, microserviceData.pubTags, transaction) + } + + if (microserviceData.subTags) { + await _setSubTags(microservice, microserviceData.subTags, transaction) + const fogsNeedUpdate = new Set() + for (const tag of microserviceData.subTags) { + try { + const where = { + delete: false, + '$pubTags.value$': tag + } + // Get fog nodes with microservices for the given pubTag + const response = await MicroserviceManager.findAllExcludeFields(where, transaction, { attributes: ['iofogUuid'] }) + if (response.length > 0) { + response.forEach(ms => ms.iofogUuid && fogsNeedUpdate.add(ms.iofogUuid)) + } + } catch (error) { + logger.error(`Checking fog nodes list for pubTag "${tag.value}":`, error.message) + } + } + for (const fog of fogsNeedUpdate) { + try { + await ChangeTrackingService.update(fog, ChangeTrackingService.events.microserviceFull, transaction) + } catch (error) { + logger.error(`Updating change tracking for fog "${fog.value}":`, error.message) + } + } + } + + if (needStatusReset) { + const microserviceStatus = { + status: MicroserviceStates.QUEUED, + operatingDuration: 0, + startTime: 0, + cpuUsage: 0, + memoryUsage: 0, + containerId: '', + percentage: 0, + errorMessage: '' + } + await MicroserviceStatusManager.update({ + microserviceUuid: microservice.uuid + }, microserviceStatus, transaction) + } + + if (changeTrackingEnabled) { + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceRouting, transaction) + await ChangeTrackingService.update(updatedMicroservice.iofogUuid, ChangeTrackingService.events.microserviceRouting, transaction) + await _updateChangeTracking(true, microservice.iofogUuid, transaction) + await _updateChangeTracking(true, updatedMicroservice.iofogUuid, transaction) + } else { + return { + microserviceIofogUuid: microservice.iofogUuid, + updatedMicroserviceIofogUuid: updatedMicroservice.iofogUuid + } + } +} + +async function updateMicroserviceConfigEndPoint (microserviceUuid, config, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = _validateMicroserviceConfig(JSON.stringify(config)) + await MicroserviceManager.update(query, { config: microserviceConfig }, transaction) + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) + return { + uuid: microserviceUuid + } +} + +async function getMicroserviceConfigEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = JSON.parse(microservice.config) + return { + config: microserviceConfig + } +} + +async function deleteMicroserviceConfigEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = {} + await MicroserviceManager.update(query, { config: JSON.stringify(microserviceConfig) }, transaction) + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) + return { + uuid: microserviceUuid + } +} + +async function getSystemMicroserviceConfigEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (!microservice.catalogItem || microservice.catalogItem.category !== 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = JSON.parse(microservice.config) + return { + config: microserviceConfig + } +} + +async function updateSystemMicroserviceConfigEndPoint (microserviceUuid, config, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (!microservice.catalogItem || microservice.catalogItem.category !== 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = _validateMicroserviceConfig(JSON.stringify(config)) + await MicroserviceManager.update(query, { config: microserviceConfig }, transaction) + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) + return { + uuid: microserviceUuid + } +} + +async function deleteSystemMicroserviceConfigEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findOneWithCategory(query, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (!microservice.catalogItem || microservice.catalogItem.category !== 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + const microserviceConfig = {} + await MicroserviceManager.update(query, { config: JSON.stringify(microserviceConfig) }, transaction) + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) + return { + uuid: microserviceUuid + } +} + +async function rebuildMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + + const check = await MicroserviceManager.findOneWithCategory(query, transaction) + if (check.catalogItem && check.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + const microservice = await MicroserviceManager.updateAndFind(query, { rebuild: true }, transaction) + + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + return { + uuid: microserviceUuid, + rebuild: true + } +} + +async function rebuildSystemMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const query = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + + const microservice = await MicroserviceManager.updateAndFind(query, { rebuild: true }, transaction) + + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + const iofogUuid = microservice.iofogUuid + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + return { + uuid: microserviceUuid, + rebuild: true + } +} + +/** + * checks if microservice image is updated + * @param {*} microserviceDataUpdateImages + * @param {*} catalogImages + */ +const _checkIfMicroserviceImagesAreEqual = (microserviceDataUpdateImages, catalogImages) => { + const oldMicroservicesImages = [] + for (const images of catalogImages) { + oldMicroservicesImages.push(images.containerImage) + } + const newMicroserviceImages = [] + for (const images of microserviceDataUpdateImages) { + newMicroserviceImages.push(images.containerImage) + } + return isEqual(newMicroserviceImages, oldMicroservicesImages) +} + +async function deleteMicroserviceEndPoint (microserviceUuid, microserviceData, isCLI, transaction) { + const where = isCLI + ? { + uuid: microserviceUuid + } + : { + uuid: microserviceUuid + } + + const microservice = await MicroserviceManager.findOneWithStatusAndCategory(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (!isCLI && microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_DELETE, microserviceUuid)) + } + + const existingService = await ServiceManager.findOne({ type: `microservice`, resource: microservice.uuid }, transaction) + if (existingService) { + logger.info(`Deleting service ${existingService.name}`) + await ServiceServices.deleteServiceEndpoint(existingService.name, transaction) + } + await deleteMicroserviceWithRoutesAndPortMappings(microservice, transaction) + await _updateChangeTracking(false, microservice.iofogUuid, transaction) +} + +async function deleteNotRunningMicroservices (fog, transaction) { + const microservices = await MicroserviceManager.findAllWithStatuses({ iofogUuid: fog.uuid }, transaction) + microservices + .filter((microservice) => microservice.delete) + .filter((microservice) => microservice.microserviceStatus.status === MicroserviceStates.UNKNOWN || + microservice.microserviceStatus.status === MicroserviceStates.STOPPING || + microservice.microserviceStatus.status === MicroserviceStates.DELETING || + microservice.microserviceStatus.status === MicroserviceStates.MARKED_FOR_DELETION) + .forEach(async (microservice) => { await deleteMicroserviceWithRoutesAndPortMappings(microservice, transaction) }) +} + +async function createRouteEndPoint (sourceMicroserviceUuid, destMicroserviceUuid, isCLI, transaction) { + // Print deprecated warning + const sourceMsvc = await MicroserviceManager.findMicroserviceOnGet({ uuid: sourceMicroserviceUuid }, transaction) + if (!sourceMsvc) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_SOURCE_MICROSERVICE_UUID, sourceMicroserviceUuid)) + } + const destMsvc = await MicroserviceManager.findMicroserviceOnGet({ uuid: destMicroserviceUuid }, transaction) + if (!destMsvc) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_SOURCE_MICROSERVICE_UUID, destMsvc)) + } + + const application = await ApplicationManager.findOne({ id: sourceMsvc.applicationId }, transaction) + if (!application) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, sourceMsvc.applicationId)) + } + + return RoutingService.createRouting({ application: application.name, from: sourceMsvc.name, to: destMsvc.name, name: `r-${sourceMsvc.name}-${destMsvc.name}` }, isCLI, transaction) +} + +async function deleteRouteEndPoint (sourceMicroserviceUuid, destMicroserviceUuid, isCLI, transaction) { + // Print deprecated warning + + const route = await RoutingManager.findOnePopulated({ + sourceMicroserviceUuid: sourceMicroserviceUuid, + destMicroserviceUuid: destMicroserviceUuid + }, transaction) + if (!route) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.ROUTE_NOT_FOUND)) + } + + return RoutingService.deleteRouting(route.application.name, route.name, isCLI, transaction) +} + +async function createPortMappingEndPoint (microserviceUuid, portMappingData, isCLI, transaction) { + await Validator.validate(portMappingData, Validator.schemas.portsCreate) + await _validateMicroserviceOnGet(microserviceUuid, transaction) + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const agent = await FogManager.findOne({ uuid: microservice.iofogUuid }, transaction) + if (!agent) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microservice.iofogUuid)) + } + await MicroservicePortService.validatePortMapping(agent, portMappingData, {}, transaction) + + return MicroservicePortService.createPortMapping(microservice, portMappingData, transaction) +} + +async function createSystemPortMappingEndPoint (microserviceUuid, portMappingData, isCLI, transaction) { + await Validator.validate(portMappingData, Validator.schemas.portsCreate) + + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const agent = await FogManager.findOne({ uuid: microservice.iofogUuid }, transaction) + if (!agent) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, microservice.iofogUuid)) + } + await MicroservicePortService.validatePortMapping(agent, portMappingData, {}, transaction) + + return MicroservicePortService.createPortMapping(microservice, portMappingData, transaction) +} + +async function _createExtraHost (microservice, extraHostData, transaction) { + const msExtraHostData = { + ...extraHostData, + microserviceUuid: microservice.uuid + } + + await MicroserviceExtraHostManager.create(msExtraHostData, transaction) +} + +async function _createEnv (microservice, envData, transaction) { + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) + } + + const msEnvData = { + key: envData.key, + value: envData.value, microserviceUuid: microservice.uuid } + // Handle valueFromSecret + if (envData.valueFromSecret) { + const [secretName, dataKey] = envData.valueFromSecret.split('/') + if (!secretName || !dataKey) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_SECRET_REFERENCE, envData.valueFromSecret)) + } + const secret = await SecretManager.getSecret(secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, secretName)) + } + if (!secret.data[dataKey]) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SECRET_KEY_NOT_FOUND, dataKey, secretName)) + } + // If it's a TLS secret, decode the base64 value + if (secret.type === 'tls') { + try { + msEnvData.value = Buffer.from(secret.data[dataKey], 'base64').toString('utf-8') + } catch (error) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_BASE64_VALUE, dataKey, secretName)) + } + } else { + msEnvData.value = secret.data[dataKey] + } + msEnvData.valueFromSecret = envData.valueFromSecret + } + + // Handle valueFromConfigMap + if (envData.valueFromConfigMap) { + const [configMapName, dataKey] = envData.valueFromConfigMap.split('/') + if (!configMapName || !dataKey) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONFIGMAP_REFERENCE, envData.valueFromConfigMap)) + } + const configMap = await ConfigMapManager.getConfigMap(configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, configMapName)) + } + if (!configMap.data[dataKey]) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_KEY_NOT_FOUND, dataKey, configMapName)) + } + msEnvData.value = configMap.data[dataKey] + msEnvData.valueFromConfigMap = envData.valueFromConfigMap + } + await MicroserviceEnvManager.create(msEnvData, transaction) - await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, false, transaction) + await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) } -async function _createArg (microservice, arg, user, transaction) { +async function _createArg (microservice, arg, transaction) { if (!microservice.iofogUuid) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) } @@ -692,35 +1544,179 @@ async function _createArg (microservice, arg, user, transaction) { } await MicroserviceArgManager.create(msArgData, transaction) - await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, false, transaction) + await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) } -async function deletePortMappingEndPoint (microserviceUuid, internalPort, user, isCLI, transaction) { - return MicroservicePortService.deletePortMapping(microserviceUuid, internalPort, user, isCLI, transaction) +async function _createCdiDevices (microservice, cdiDevices, transaction) { + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) + } + + const msCdiDevicesData = { + cdiDevices: cdiDevices, + microserviceUuid: microservice.uuid + } + + await MicroserviceCdiDevManager.create(msCdiDevicesData, transaction) + await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) } -async function listPortMappingsEndPoint (microserviceUuid, user, isCLI, transaction) { - return MicroservicePortService.listPortMappings(microserviceUuid, user, isCLI, transaction) +async function _createCapAdd (microservice, capAdd, transaction) { + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) + } + + const msCapAddData = { + capAdd: capAdd, + microserviceUuid: microservice.uuid + } + + await MicroserviceCapAddManager.create(msCapAddData, transaction) + await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) +} + +async function _createCapDrop (microservice, capDrop, transaction) { + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)) + } + + const msCapDropData = { + capDrop: capDrop, + microserviceUuid: microservice.uuid + } + + await MicroserviceCapDropManager.create(msCapDropData, transaction) + await MicroservicePortService.switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, transaction) +} + +async function deletePortMappingEndPoint (microserviceUuid, internalPort, isCLI, transaction) { + return MicroservicePortService.deletePortMapping(microserviceUuid, internalPort, isCLI, transaction) +} + +async function deleteSystemPortMappingEndPoint (microserviceUuid, internalPort, isCLI, transaction) { + return MicroservicePortService.deleteSystemPortMapping(microserviceUuid, internalPort, isCLI, transaction) +} + +async function listPortMappingsEndPoint (microserviceUuid, isCLI, transaction) { + return MicroservicePortService.listPortMappings(microserviceUuid, isCLI, transaction) } async function getReceiverMicroservices (microservice, transaction) { + // 1. Get existing routes (app-level routing) const routes = await RoutingManager.findAll({ sourceMicroserviceUuid: microservice.uuid }, transaction) - return routes.map(route => route.destMicroserviceUuid) -} + let receiverMicroservices = routes.map(route => route.destMicroserviceUuid) + + // 2. Check if the microservice has pubTags and fetch microservices associated with those tags + if (microservice.pubTags) { + for (const tag of microservice.pubTags) { + try { + const where = { + delete: false, + '$subTags.value$': tag.value + } + // Get microservices for the given pubTag + const response = await MicroserviceManager.findAllExcludeFields(where, transaction, { attributes: ['uuid'] }) + if (response.length > 0) { + const tagMicroservices = response.map(ms => ms.uuid) + // Add the microservices' UUIDs to the receiver list (filtering duplicates and removing the current microservice's UUID) + receiverMicroservices = [ + ...new Set([ + ...receiverMicroservices, + ...tagMicroservices.filter(uuid => uuid !== microservice.uuid) // Remove the current microservice's UUID + ]) + ] + } + } catch (error) { + logger.error(`Checking microservices for pubTag "${tag.value}":`, error.message) + } + } + } + return receiverMicroservices +} + +async function isMicroserviceConsumer (microservice, transaction) { + // Step 1: App-level routing check + const routes = await RoutingManager.findAll({ destMicroserviceUuid: microservice.uuid }, transaction) + + if (routes.length > 0) { + return true + } + + // Step 2: Subtag-based routing check + if (microservice.subTags) { + for (const tag of microservice.subTags) { + try { + const where = { + delete: false, + '$pubTags.value$': tag.value + } + const result = await MicroserviceManager.findAllExcludeFields(where, transaction, { attributes: ['uuid'] }) + + if (result.length > 0) { + return true + } + } catch (error) { + logger.error(`Checking microservices for subTag "${tag.value}":`, error.message) + } + } + } + return false +} + +async function isMicroserviceRouter (microservice, transaction) { + if (microservice.name === `router-${microservice.iofogUuid.toLowerCase()}`) { + const app = await ApplicationManager.findOne({ id: microservice.applicationId }, transaction) + if (app.isSystem === true) { + return true + } + } + return false +} + +async function createVolumeMappingEndPoint (microserviceUuid, volumeMappingData, isCLI, transaction) { + await Validator.validate(volumeMappingData, Validator.schemas.volumeMappings) + + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findMicroserviceOnGet(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const type = volumeMappingData.type || VOLUME_MAPPING_DEFAULT + + const volumeMapping = await VolumeMappingManager.findOne({ + microserviceUuid: microserviceUuid, + hostDestination: volumeMappingData.hostDestination, + containerDestination: volumeMappingData.containerDestination, + type + }, transaction) + if (volumeMapping) { + throw new Errors.ValidationError(ErrorMessages.VOLUME_MAPPING_ALREADY_EXISTS) + } + + _validateVolumeMappings([volumeMappingData]) -async function isMicroserviceConsumer (microservice, transaction) { - const routes = await RoutingManager.findAll({ destMicroserviceUuid: microservice.uuid }, transaction) + const volumeMappingObj = { + microserviceUuid: microserviceUuid, + hostDestination: volumeMappingData.hostDestination, + containerDestination: volumeMappingData.containerDestination, + accessMode: volumeMappingData.accessMode, + type + } - return !!(routes && routes.length > 0) + return VolumeMappingManager.create(volumeMappingObj, transaction) } -async function createVolumeMappingEndPoint (microserviceUuid, volumeMappingData, user, isCLI, transaction) { +async function createSystemVolumeMappingEndPoint (microserviceUuid, volumeMappingData, isCLI, transaction) { await Validator.validate(volumeMappingData, Validator.schemas.volumeMappings) const where = isCLI ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } + : { uuid: microserviceUuid } const microservice = await MicroserviceManager.findOne(where, transaction) if (!microservice) { @@ -752,10 +1748,31 @@ async function createVolumeMappingEndPoint (microserviceUuid, volumeMappingData, return VolumeMappingManager.create(volumeMappingObj, transaction) } -async function deleteVolumeMappingEndPoint (microserviceUuid, volumeMappingUuid, user, isCLI, transaction) { +async function deleteVolumeMappingEndPoint (microserviceUuid, volumeMappingUuid, isCLI, transaction) { + const where = isCLI + ? { uuid: microserviceUuid } + : { uuid: microserviceUuid } + + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const volumeMappingWhere = { + uuid: volumeMappingUuid, + microserviceUuid: microserviceUuid + } + + const affectedRows = await VolumeMappingManager.delete(volumeMappingWhere, transaction) + if (affectedRows === 0) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MAPPING_UUID, volumeMappingUuid)) + } +} + +async function deleteSystemVolumeMappingEndPoint (microserviceUuid, volumeMappingUuid, isCLI, transaction) { const where = isCLI ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } + : { uuid: microserviceUuid } const microservice = await MicroserviceManager.findOne(where, transaction) if (!microservice) { @@ -773,10 +1790,10 @@ async function deleteVolumeMappingEndPoint (microserviceUuid, volumeMappingUuid, } } -async function listVolumeMappingsEndPoint (microserviceUuid, user, isCLI, transaction) { +async function listVolumeMappingsEndPoint (microserviceUuid, isCLI, transaction) { const where = isCLI ? { uuid: microserviceUuid } - : { uuid: microserviceUuid, userId: user.id } + : { uuid: microserviceUuid } const microservice = await MicroserviceManager.findOne(where, transaction) if (!microservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) @@ -798,19 +1815,62 @@ function _validateMicroserviceConfig (config) { return result } -async function _createMicroservice (microserviceData, user, isCLI, transaction) { +function _validateMicroserviceAnnotations (annotations) { + let result + if (annotations) { + result = annotations.split('\\"').join('"').split('"').join('\"') // eslint-disable-line no-useless-escape + } + return result +} + +function _validateMicroserviceHealthCheck (healthCheck) { + let result + if (healthCheck) { + // Convert the health check object to a JSON string for database storage + result = JSON.stringify(healthCheck) + } + return result +} + +function _processHealthCheckForDB (healthCheckData) { + if (!healthCheckData) return null + + return { + test: _validateMicroserviceHealthCheck(healthCheckData.test), + interval: healthCheckData.interval, + timeout: healthCheckData.timeout, + startPeriod: healthCheckData.startPeriod, + startInterval: healthCheckData.startInterval, + retries: healthCheckData.retries + } +} + +async function _createMicroservice (microserviceData, isCLI, transaction) { const config = _validateMicroserviceConfig(microserviceData.config) + const annotations = _validateMicroserviceAnnotations(microserviceData.annotations) let newMicroservice = { - uuid: AppHelper.generateRandomString(32), + uuid: AppHelper.generateUUID(), name: microserviceData.name, config: config, + annotations: annotations, catalogItemId: microserviceData.catalogItemId, iofogUuid: microserviceData.iofogUuid, - rootHostAccess: microserviceData.rootHostAccess, + hostNetworkMode: microserviceData.hostNetworkMode, + isPrivileged: microserviceData.isPrivileged, + cpuSetCpus: microserviceData.cpuSetCpus, + memoryLimit: microserviceData.memoryLimit, + pidMode: microserviceData.pidMode, + ipcMode: microserviceData.ipcMode, + cdiDevices: microserviceData.cdiDevices, + capAdd: microserviceData.capAdd, + capDrop: microserviceData.capDrop, + runAsUser: microserviceData.runAsUser, + platform: microserviceData.platform, + runtime: microserviceData.runtime, registryId: microserviceData.registryId || 1, - logSize: (microserviceData.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1, - userId: user.id + schedule: microserviceData.schedule || 50, + logSize: (microserviceData.logSize || constants.MICROSERVICE_DEFAULT_LOG_SIZE) * 1 } newMicroservice = AppHelper.deleteUndefinedFields(newMicroservice) @@ -823,10 +1883,10 @@ async function _createMicroservice (microserviceData, user, isCLI, transaction) } // validate application - const application = await _validateApplication(microserviceData.application, user, isCLI, transaction) + const application = await _validateApplication(microserviceData.application, isCLI, transaction) newMicroservice.applicationId = application.id - await _checkForDuplicateName(newMicroservice.name, {}, user.id, newMicroservice.applicationId, transaction) + await _checkForDuplicateName(newMicroservice.name, {}, newMicroservice.applicationId, transaction) // validate fog node if (newMicroservice.iofogUuid) { @@ -839,28 +1899,64 @@ async function _createMicroservice (microserviceData, user, isCLI, transaction) return MicroserviceManager.create(newMicroservice, transaction) } -async function _validateApplication (name, user, isCLI, transaction) { +async function _validateApplication (name, isCLI, transaction) { if (!name) { return null } // Force name conversion to string for PG const where = isCLI - ? { name: name.toString() } - : { name: name.toString(), userId: user.id } + ? { name: name.toString(), isSystem: false } + : { name: name.toString(), isSystem: false } const application = await ApplicationManager.findOne(where, transaction) if (!application) { - // Try with id - const where = isCLI - ? { id: name } - : { id: name, userId: user.id } + // Try with id - but only if name is actually a valid integer + if (Number.isInteger(Number(name)) && !isNaN(name)) { + const where = isCLI + ? { id: parseInt(name), isSystem: false } + : { id: parseInt(name), isSystem: false } + + const application = await ApplicationManager.findOne(where, transaction) + if (!application) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, name)) + } + return application + } else { + // If name is not a valid integer, it's not a valid ID either + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, name)) + } + } + return application +} - const application = await ApplicationManager.findOne(where, transaction) - if (!application) { +async function _validateSystemApplication (name, isCLI, transaction) { + if (!name) { + return null + } + + // Force name conversion to string for PG + const where = isCLI + ? { name: name.toString(), isSystem: true } + : { name: name.toString(), isSystem: true } + + const application = await ApplicationManager.findOne(where, transaction) + if (!application) { + // Try with id - but only if name is actually a valid integer + if (Number.isInteger(Number(name)) && !isNaN(name)) { + const where = isCLI + ? { id: parseInt(name), isSystem: true } + : { id: parseInt(name), isSystem: true } + + const application = await ApplicationManager.findOne(where, transaction) + if (!application) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, name)) + } + return application + } else { + // If name is not a valid integer, it's not a valid ID either throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, name)) } - return application } return application } @@ -871,6 +1967,12 @@ async function _createMicroserviceStatus (microservice, transaction) { }, transaction) } +async function _createMicroserviceExecStatus (microservice, transaction) { + return MicroserviceExecStatusManager.create({ + microserviceUuid: microservice.uuid + }, transaction) +} + async function _createMicroserviceImages (microservice, images, transaction) { const newImages = [] for (const img of images) { @@ -882,6 +1984,15 @@ async function _createMicroserviceImages (microservice, images, transaction) { } async function _createVolumeMappings (microservice, volumeMappings, transaction) { + // Validate volume mount references before creating mappings + if (volumeMappings && microservice.iofogUuid) { + for (const volumeMapping of volumeMappings) { + if (volumeMapping.hostDestination) { + await _validateVolumeMountReference(volumeMapping.hostDestination, microservice.iofogUuid, transaction) + } + } + } + const mappings = [] for (const volumeMapping of volumeMappings) { const mapping = Object.assign({}, volumeMapping) @@ -895,6 +2006,21 @@ async function _createVolumeMappings (microservice, volumeMappings, transaction) async function _updateVolumeMappings (volumeMappings, microserviceUuid, transaction) { _validateVolumeMappings(volumeMappings) + // Get microservice to find fogUuid for volume mount validation + const microservice = await MicroserviceManager.findOne({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + // Validate volume mount references before updating mappings + if (volumeMappings && microservice.iofogUuid) { + for (const volumeMapping of volumeMappings) { + if (volumeMapping.hostDestination) { + await _validateVolumeMountReference(volumeMapping.hostDestination, microservice.iofogUuid, transaction) + } + } + } + await VolumeMappingManager.delete({ microserviceUuid: microserviceUuid }, transaction) @@ -926,12 +2052,12 @@ async function _deleteImages (microserviceUuid, transaction) { }, transaction) } -async function _updateExtraHosts (extraHosts, microserviceUuid, user, transaction) { +async function _updateExtraHosts (extraHosts, microserviceUuid, transaction) { await MicroserviceExtraHostManager.delete({ microserviceUuid: microserviceUuid }, transaction) for (const extraHost of extraHosts) { - await _createExtraHost({ uuid: microserviceUuid }, extraHost, user, transaction) + await _createExtraHost({ uuid: microserviceUuid }, extraHost, transaction) } } @@ -946,6 +2072,49 @@ async function _updateEnv (env, microserviceUuid, transaction) { value: envData.value } + // Handle valueFromSecret + if (envData.valueFromSecret) { + const [secretName, dataKey] = envData.valueFromSecret.split('/') + if (!secretName || !dataKey) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_SECRET_REFERENCE, envData.valueFromSecret)) + } + const secret = await SecretManager.getSecret(secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, secretName)) + } + if (!secret.data[dataKey]) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SECRET_KEY_NOT_FOUND, dataKey, secretName)) + } + // If it's a TLS secret, decode the base64 value + if (secret.type === 'tls') { + try { + envObj.value = Buffer.from(secret.data[dataKey], 'base64').toString('utf-8') + } catch (error) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_BASE64_VALUE, dataKey, secretName)) + } + } else { + envObj.value = secret.data[dataKey] + } + envObj.valueFromSecret = envData.valueFromSecret + } + + // Handle valueFromConfigMap + if (envData.valueFromConfigMap) { + const [configMapName, dataKey] = envData.valueFromConfigMap.split('/') + if (!configMapName || !dataKey) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONFIGMAP_REFERENCE, envData.valueFromConfigMap)) + } + const configMap = await ConfigMapManager.getConfigMap(configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, configMapName)) + } + if (!configMap.data[dataKey]) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_KEY_NOT_FOUND, dataKey, configMapName)) + } + envObj.value = configMap.data[dataKey] + envObj.valueFromConfigMap = envData.valueFromConfigMap + } + await MicroserviceEnvManager.create(envObj, transaction) } } @@ -964,10 +2133,59 @@ async function _updateArg (arg, microserviceUuid, transaction) { } } -async function _updatePorts (newPortMappings, microservice, user, transaction) { - await MicroservicePortService.deletePortMappings(microservice, user, transaction) +async function _updateCdiDevices (cdiDevices, microserviceUuid, transaction) { + await MicroserviceCdiDevManager.delete({ + microserviceUuid: microserviceUuid + }, transaction) + for (const cdiDevicesData of cdiDevices) { + const envObj = { + microserviceUuid: microserviceUuid, + cdiDevices: cdiDevicesData + } + + await MicroserviceCdiDevManager.create(envObj, transaction) + } +} + +async function _updateCapAdd (capAdd, microserviceUuid, transaction) { + await MicroserviceCapAddManager.delete({ + microserviceUuid: microserviceUuid + }, transaction) + for (const capAddData of capAdd) { + const envObj = { + microserviceUuid: microserviceUuid, + capAdd: capAddData + } + + await MicroserviceCapAddManager.create(envObj, transaction) + } +} + +async function _updateCapDrop (capDrop, microserviceUuid, transaction) { + await MicroserviceCapDropManager.delete({ + microserviceUuid: microserviceUuid + }, transaction) + for (const capDropData of capDrop) { + const envObj = { + microserviceUuid: microserviceUuid, + capDrop: capDropData + } + + await MicroserviceCapDropManager.create(envObj, transaction) + } +} + +async function _updatePorts (newPortMappings, microservice, transaction) { + await MicroservicePortService.deletePortMappings(microservice, transaction) + for (const portMapping of newPortMappings) { + await createPortMappingEndPoint(microservice.uuid, portMapping, false, transaction) + } +} + +async function _updateSystemPorts (newPortMappings, microservice, transaction) { + await MicroservicePortService.deletePortMappings(microservice, transaction) for (const portMapping of newPortMappings) { - await createPortMappingEndPoint(microservice.uuid, portMapping, user, false, transaction) + await createSystemPortMappingEndPoint(microservice.uuid, portMapping, false, transaction) } } @@ -979,19 +2197,17 @@ async function _updateChangeTracking (configUpdated, fogNodeUuid, transaction) { } } -async function _checkForDuplicateName (name, item, userId, applicationId, transaction) { +async function _checkForDuplicateName (name, item, applicationId, transaction) { if (name) { const where = item.id ? { name: name, uuid: { [Op.ne]: item.id }, delete: false, - userId: userId, applicationId } : { name: name, - userId: userId, applicationId, delete: false } @@ -1003,10 +2219,9 @@ async function _checkForDuplicateName (name, item, userId, applicationId, transa } } -async function _validateMicroserviceOnGet (userId, microserviceUuid, transaction) { +async function _validateMicroserviceOnGet (microserviceUuid, transaction) { const where = { - '$application.user.id$': userId, - 'uuid': microserviceUuid + uuid: microserviceUuid } const microservice = await MicroserviceManager.findMicroserviceOnGet(where, transaction) if (!microservice) { @@ -1014,6 +2229,16 @@ async function _validateMicroserviceOnGet (userId, microserviceUuid, transaction } } +async function _validateSystemMicroserviceOnGet (microserviceUuid, transaction) { + const where = { + uuid: microserviceUuid + } + const microservice = await MicroserviceManager.findSystemMicroserviceOnGet(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER) + } +} + async function _getLogicalRoutesByMicroservice (microserviceUuid, transaction) { const res = [] const query = { @@ -1037,15 +2262,12 @@ async function _getLogicalRoutesByMicroservice (microserviceUuid, transaction) { } async function deleteMicroserviceWithRoutesAndPortMappings (microservice, transaction) { - const user = { - id: microservice.userId - } const routes = await _getLogicalRoutesByMicroservice(microservice.uuid, transaction) for (const route of routes) { - await RoutingService.deleteRouting(route.application.name, route.name, user, false, transaction) + await RoutingService.deleteRouting(route.application.name, route.name, false, transaction) } - await MicroservicePortService.deletePortMappings(microservice, user, transaction) + await MicroservicePortService.deletePortMappings(microservice, transaction) await MicroserviceManager.delete({ uuid: microservice.uuid @@ -1061,30 +2283,93 @@ async function _buildGetMicroserviceResponse (microservice, transaction) { const extraHosts = await MicroserviceExtraHostManager.findAll({ microserviceUuid: microserviceUuid }, transaction) const images = await CatalogItemImageManager.findAll({ microserviceUuid: microserviceUuid }, transaction) const volumeMappings = await VolumeMappingManager.findAll({ microserviceUuid: microserviceUuid }, transaction) - const routes = await RoutingManager.findAll({ sourceMicroserviceUuid: microserviceUuid }, transaction) + const routes = await getReceiverMicroservices(microservice, transaction) const env = await MicroserviceEnvManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) const cmd = await MicroserviceArgManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) const arg = cmd.map((it) => it.cmd) + const cdiDevices = await MicroserviceCdiDevManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + const cdiDevs = cdiDevices.map((it) => it.cdiDevices) + const capAdd = await MicroserviceCapAddManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + const capAdds = capAdd.map((it) => it.capAdd) + const capDrop = await MicroserviceCapDropManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + const capDrops = capDrop.map((it) => it.capDrop) + const pubTags = microservice.pubTags ? microservice.pubTags.map(t => t.value) : [] + const subTags = microservice.subTags ? microservice.subTags.map(t => t.value) : [] const status = await MicroserviceStatusManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) - + const execStatus = await MicroserviceExecStatusManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + const healthCheck = await MicroserviceHealthCheckManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) // build microservice response const res = Object.assign({}, microservice) res.ports = [] for (const pm of portMappings) { const mapping = { internal: pm.portInternal, external: pm.portExternal, protocol: pm.isUdp ? 'udp' : 'tcp' } - await MicroservicePortService.buildPublicPortMapping(pm, mapping, transaction) + // await MicroservicePortService.buildPublicPortMapping(pm, mapping, transaction) res.ports.push(mapping) } res.volumeMappings = volumeMappings.map((vm) => vm.dataValues) - res.routes = routes.map((r) => r.destMicroserviceUuid) + res.routes = routes res.env = env res.cmd = arg + res.cdiDevices = cdiDevs + res.capAdd = capAdds + res.capDrop = capDrops res.extraHosts = extraHosts.map(eH => ({ name: eH.name, address: eH.template, value: eH.value })) res.images = images.map(i => ({ containerImage: i.containerImage, fogTypeId: i.fogTypeId })) - if (status && status.length) { res.status = status[0] } + if (execStatus && execStatus.length) { + res.execStatus = execStatus[0] + } + if (healthCheck && healthCheck.length) { + const healthCheckData = healthCheck[0] + // Create a copy of the health check data to avoid modifying the Sequelize object + const healthCheckResponse = { + test: healthCheckData.test + } + + // Only add fields if they are not null + if (healthCheckData.interval !== null) { + healthCheckResponse.interval = healthCheckData.interval + } + if (healthCheckData.timeout !== null) { + healthCheckResponse.timeout = healthCheckData.timeout + } + if (healthCheckData.startPeriod !== null) { + healthCheckResponse.startPeriod = healthCheckData.startPeriod + } + if (healthCheckData.startInterval !== null) { + healthCheckResponse.startInterval = healthCheckData.startInterval + } + if (healthCheckData.retries !== null) { + healthCheckResponse.retries = healthCheckData.retries + } + + // Handle the test field - ensure it's always an array + if (healthCheckResponse.test) { + if (typeof healthCheckResponse.test === 'string') { + // It's a JSON string, try to parse it + try { + healthCheckResponse.test = JSON.parse(healthCheckResponse.test) + } catch (e) { + // If not valid JSON, treat as a single string command + healthCheckResponse.test = [healthCheckResponse.test] + } + } else if (!Array.isArray(healthCheckResponse.test)) { + // If it's not an array, convert to array + healthCheckResponse.test = [healthCheckResponse.test] + } + // If it's already an array, leave as is + } + + if (healthCheckResponse.test && healthCheckResponse.test.length > 0) { + res.healthCheck = healthCheckResponse + } else { + res.healthCheck = {} + } + } + res.pubTags = pubTags + res.subTags = subTags res.logSize *= 1 @@ -1096,29 +2381,203 @@ async function _buildGetMicroserviceResponse (microservice, transaction) { return res } -function listAllPublicPortsEndPoint (user, transaction) { - return MicroservicePortService.listAllPublicPorts(user, transaction) +async function listMicroserviceByPubTagEndPoint (pubTag, transaction) { + const where = { + delete: false, + '$pubTags.value$': pubTag + } + + const microservices = await MicroserviceManager.findAllExcludeFields(where, transaction) + + const res = await Promise.all(microservices.map(async (microservice) => { + return _buildGetMicroserviceResponse(microservice.dataValues, transaction) + })) + + return { + microservices: res + } +} + +async function listMicroserviceBySubTagEndPoint (subTag, transaction) { + const where = { + delete: false, + '$subTags.value$': subTag + } + + const microservices = await MicroserviceManager.findAllExcludeFields(where, transaction) + + const res = await Promise.all(microservices.map(async (microservice) => { + return _buildGetMicroserviceResponse(microservice.dataValues, transaction) + })) + + return { + microservices: res + } +} + +async function createExecEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { execEnabled: true }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + + const updatedMicroservice = await MicroserviceManager.findOneWithCategory({ uuid: microservice.uuid }, transaction) + + return { + uuid: microservice.uuid, + execEnabled: updatedMicroservice.execEnabled + } +} + +async function deleteExecEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { execEnabled: false }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + + const updatedMicroservice = await MicroserviceManager.findOneWithCategory({ uuid: microservice.uuid }, transaction) + + return { + uuid: microservice.uuid, + execEnabled: updatedMicroservice.execEnabled + } +} + +async function createSystemExecEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + // if (microservice.catalogItem && microservice.catalogItem.category !== 'SYSTEM') { + // throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + // } + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER) + } + await MicroserviceManager.update({ uuid: microservice.uuid }, { execEnabled: true }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + + const updatedMicroservice = await MicroserviceManager.findOneWithCategory({ uuid: microservice.uuid }, transaction) + + return { + uuid: microservice.uuid, + execEnabled: updatedMicroservice.execEnabled + } +} + +async function deleteSystemExecEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + // if (microservice.catalogItem && microservice.catalogItem.category !== 'SYSTEM') { + // throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + // } + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { execEnabled: false }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceExecSessions, transaction) + + const updatedMicroservice = await MicroserviceManager.findOneWithCategory({ uuid: microservice.uuid }, transaction) + + return { + uuid: microservice.uuid, + execEnabled: updatedMicroservice.execEnabled + } +} + +async function startMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + // Check if the parent application is activated + const application = await ApplicationManager.findOne({ id: microservice.applicationId }, transaction) + if (!application || !application.isActivated) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.APPLICATION_NOT_ACTIVATED, application ? application.name : 'Unknown')) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { isActivated: true }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) + + return { + uuid: microservice.uuid, + isActivated: true + } +} + +async function stopMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { isActivated: false }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) + + return { + uuid: microservice.uuid, + isActivated: false + } } module.exports = { createMicroserviceEndPoint: TransactionDecorator.generateTransaction(createMicroserviceEndPoint), createPortMappingEndPoint: TransactionDecorator.generateTransaction(createPortMappingEndPoint), + createSystemPortMappingEndPoint: TransactionDecorator.generateTransaction(createSystemPortMappingEndPoint), createRouteEndPoint: TransactionDecorator.generateTransaction(createRouteEndPoint), createVolumeMappingEndPoint: TransactionDecorator.generateTransaction(createVolumeMappingEndPoint), + createSystemVolumeMappingEndPoint: TransactionDecorator.generateTransaction(createSystemVolumeMappingEndPoint), deleteMicroserviceEndPoint: TransactionDecorator.generateTransaction(deleteMicroserviceEndPoint), deleteMicroserviceWithRoutesAndPortMappings: deleteMicroserviceWithRoutesAndPortMappings, deleteNotRunningMicroservices: deleteNotRunningMicroservices, deletePortMappingEndPoint: TransactionDecorator.generateTransaction(deletePortMappingEndPoint), + deleteSystemPortMappingEndPoint: TransactionDecorator.generateTransaction(deleteSystemPortMappingEndPoint), deleteRouteEndPoint: TransactionDecorator.generateTransaction(deleteRouteEndPoint), deleteVolumeMappingEndPoint: TransactionDecorator.generateTransaction(deleteVolumeMappingEndPoint), + deleteSystemVolumeMappingEndPoint: TransactionDecorator.generateTransaction(deleteSystemVolumeMappingEndPoint), getMicroserviceEndPoint: TransactionDecorator.generateTransaction(getMicroserviceEndPoint), + getSystemMicroserviceEndPoint: TransactionDecorator.generateTransaction(getSystemMicroserviceEndPoint), getReceiverMicroservices, isMicroserviceConsumer, - listAllPublicPortsEndPoint: TransactionDecorator.generateTransaction(listAllPublicPortsEndPoint), + isMicroserviceRouter, listMicroservicePortMappingsEndPoint: TransactionDecorator.generateTransaction(listPortMappingsEndPoint), listMicroservicesEndPoint: TransactionDecorator.generateTransaction(listMicroservicesEndPoint), + listSystemMicroservicesEndPoint: TransactionDecorator.generateTransaction(listSystemMicroservicesEndPoint), listVolumeMappingsEndPoint: TransactionDecorator.generateTransaction(listVolumeMappingsEndPoint), updateMicroserviceEndPoint: TransactionDecorator.generateTransaction(updateMicroserviceEndPoint), + updateSystemMicroserviceEndPoint: TransactionDecorator.generateTransaction(updateSystemMicroserviceEndPoint), + updateMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(updateMicroserviceConfigEndPoint), + getMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(getMicroserviceConfigEndPoint), + getSystemMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(getSystemMicroserviceConfigEndPoint), + deleteMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(deleteMicroserviceConfigEndPoint), + updateSystemMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(updateSystemMicroserviceConfigEndPoint), + deleteSystemMicroserviceConfigEndPoint: TransactionDecorator.generateTransaction(deleteSystemMicroserviceConfigEndPoint), + rebuildMicroserviceEndPoint: TransactionDecorator.generateTransaction(rebuildMicroserviceEndPoint), + rebuildSystemMicroserviceEndPoint: TransactionDecorator.generateTransaction(rebuildSystemMicroserviceEndPoint), buildGetMicroserviceResponse: _buildGetMicroserviceResponse, - updateChangeTracking: _updateChangeTracking + updateChangeTracking: _updateChangeTracking, + listMicroserviceByPubTagEndPoint: TransactionDecorator.generateTransaction(listMicroserviceByPubTagEndPoint), + listMicroserviceBySubTagEndPoint: TransactionDecorator.generateTransaction(listMicroserviceBySubTagEndPoint), + createExecEndPoint: TransactionDecorator.generateTransaction(createExecEndPoint), + deleteExecEndPoint: TransactionDecorator.generateTransaction(deleteExecEndPoint), + createSystemExecEndPoint: TransactionDecorator.generateTransaction(createSystemExecEndPoint), + deleteSystemExecEndPoint: TransactionDecorator.generateTransaction(deleteSystemExecEndPoint), + startMicroserviceEndPoint: TransactionDecorator.generateTransaction(startMicroserviceEndPoint), + stopMicroserviceEndPoint: TransactionDecorator.generateTransaction(stopMicroserviceEndPoint) } diff --git a/src/services/registry-service.js b/src/services/registry-service.js index 7cc0108c8..629558555 100644 --- a/src/services/registry-service.js +++ b/src/services/registry-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -18,52 +18,37 @@ const ErrorMessages = require('../helpers/error-messages') const ChangeTrackingService = require('./change-tracking-service') const TransactionDecorator = require('../decorators/transaction-decorator') const FogManager = require('../data/managers/iofog-manager') -const Sequelize = require('sequelize') -const Op = Sequelize.Op +const MicroserviceManager = require('../data/managers/microservice-manager') +// const Sequelize = require('sequelize') +// const Op = Sequelize.Op const AppHelper = require('../helpers/app-helper') -const createRegistry = async function (registry, user, transaction) { +const createRegistry = async function (registry, transaction) { await Validator.validate(registry, Validator.schemas.registryCreate) - if (registry.requiresCert && registry.certificate === undefined) { - throw new Errors.ValidationError(ErrorMessages.CERT_PROPERTY_REQUIRED) - } let registryCreate = { url: registry.url, username: registry.username, password: registry.password, isPublic: registry.isPublic, - userEmail: registry.email, - requiresCert: registry.requiresCert, - certificate: registry.certificate, - userId: user.id + userEmail: registry.email } registryCreate = AppHelper.deleteUndefinedFields(registryCreate) const createdRegistry = await RegistryManager.create(registryCreate, transaction) - await _updateChangeTracking(user, transaction) + await _updateChangeTracking(transaction) return { id: createdRegistry.id } } -const findRegistries = async function (user, isCLI, transaction) { +const findRegistries = async function (isCLI, transaction) { const queryRegistry = isCLI ? {} - : { - [Op.or]: - [ - { - userId: user.id - }, - { - isPublic: true - } - ] - } + : {} const registries = await RegistryManager.findAllWithAttributes(queryRegistry, { exclude: ['password'] }, transaction) return { @@ -71,29 +56,36 @@ const findRegistries = async function (user, isCLI, transaction) { } } -const deleteRegistry = async function (registryData, user, isCLI, transaction) { +const deleteRegistry = async function (registryData, isCLI, transaction) { await Validator.validate(registryData, Validator.schemas.registryDelete) const queryData = isCLI ? { id: registryData.id } - : { id: registryData.id, userId: user.id } + : { id: registryData.id } + // Convert registryId to number to handle string IDs from URL parameters + const id = parseInt(registryData.id, 10) + if (id === 1 || id === 2) { + throw new Errors.ValidationError(ErrorMessages.REGISTRY_IS_SYSTEM) + } const registry = await RegistryManager.findOne(queryData, transaction) if (!registry) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, registryData.id)) } - if (isCLI) { - user = { id: registry.userId } + const microservices = await MicroserviceManager.findAllWithStatuses({ registryId: registryData.id }, transaction) + if (microservices.length > 0) { + throw new Errors.ValidationError(ErrorMessages.REGISTRY_IS_IN_USE) + } else { + await RegistryManager.delete(queryData, transaction) + await _updateChangeTracking(transaction) } - await RegistryManager.delete(queryData, transaction) - await _updateChangeTracking(user, transaction) } -const updateRegistry = async function (registry, registryId, user, isCLI, transaction) { +const updateRegistry = async function (registry, registryId, isCLI, transaction) { await Validator.validate(registry, Validator.schemas.registryUpdate) - - if (registry.requiresCert && registry.certificate === undefined) { - throw new Errors.ValidationError(ErrorMessages.CERT_PROPERTY_REQUIRED) + // Convert registryId to number to handle string IDs from URL parameters + const id = parseInt(registryId, 10) + if (id === 1 || id === 2) { + throw new Errors.ValidationError(ErrorMessages.REGISTRY_IS_SYSTEM) } - const existingRegistry = await RegistryManager.findOne({ id: registryId }, transaction) @@ -107,9 +99,7 @@ const updateRegistry = async function (registry, registryId, user, isCLI, transa username: registry.username, password: registry.password, isPublic: registry.isPublic, - userEmail: registry.email, - requiresCert: registry.requiresCert, - certificate: registry.certificate + userEmail: registry.email } registryUpdate = AppHelper.deleteUndefinedFields(registryUpdate) @@ -119,21 +109,23 @@ const updateRegistry = async function (registry, registryId, user, isCLI, transa id: registryId } : { - id: registryId, - userId: user.id + id: registryId } await RegistryManager.update(where, registryUpdate, transaction) - - if (isCLI) { - user = { id: existingRegistry.userId } + const microservices = await MicroserviceManager.findAllWithStatuses({ registryId: registryId }, transaction) + if (microservices.length > 0) { + for (const ms of microservices) { + await MicroserviceManager.updateAndFind({ uuid: ms.uuid }, { rebuild: true }, transaction) + await ChangeTrackingService.update(ms.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + } } - await _updateChangeTracking(user, transaction) + await _updateChangeTracking(transaction) } -const _updateChangeTracking = async function (user, transaction) { - const fogs = await FogManager.findAll({ userId: user.id }, transaction) +const _updateChangeTracking = async function (transaction) { + const fogs = await FogManager.findAll({}, transaction) for (const fog of fogs) { await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.registries, transaction) } diff --git a/src/services/router-connection-service.js b/src/services/router-connection-service.js new file mode 100644 index 000000000..38d110c39 --- /dev/null +++ b/src/services/router-connection-service.js @@ -0,0 +1,316 @@ +const rhea = require('rhea') +const config = require('../config') +const logger = require('../logger') +const RouterManager = require('../data/managers/router-manager') +const CertificateService = require('./certificate-service') +const SecretService = require('./secret-service') +const os = require('os') + +const CONTROLLER_CERT_PREFIX = 'controller-exec-session-client' +const hostname = process.env.HOSTNAME || os.hostname() +const CONTROLLER_CERT_NAME = hostname ? `${CONTROLLER_CERT_PREFIX}-${hostname}` : CONTROLLER_CERT_PREFIX + +const DEFAULT_ROUTER_SERVICE = 'router' +const AMQP_DEFAULT_PORT = 5671 + +class RouterConnectionService { + constructor () { + this.connection = null + this.connectionPromise = null + this.certificatePromise = null + this.cachedCertificate = null + this.connectionOptions = null + this.cachedRouterRecord = null + this.fakeTransaction = { fakeTransaction: true } + this.container = rhea.create_container({ + id: 'controller-exec-session-client', + enable_sasl_external: true + }) + } + + async getConnection () { + if (this.connection && this.connection.is_open && this.connection.is_open()) { + return this.connection + } + if (this.connectionPromise) { + return this.connectionPromise + } + this.connectionPromise = this._createConnection() + return this.connectionPromise + } + + async _createConnection () { + try { + const options = await this._buildConnectionOptions() + return await new Promise((resolve, reject) => { + const connection = this.container.connect(options) + + const cleanupPromise = () => { + this.connection = null + this.connectionPromise = null + } + + connection.once('connection_open', () => { + logger.info('[AMQP] Router connection established') + this.connection = connection + this.connectionPromise = null + connection.on('connection_error', (context) => { + logger.error({ + err: context.error, + transport: 'amqp', + msg: '[AMQP] Connection error event' + }) + }) + connection.on('connection_close', () => { + logger.warn('[AMQP] Router connection closed') + cleanupPromise() + }) + connection.on('disconnected', (context) => { + logger.warn('[AMQP] Router connection disconnected', { + error: context.error ? context.error.message : 'unknown' + }) + cleanupPromise() + }) + resolve(connection) + }) + + connection.once('connection_close', (context) => { + logger.error({ + err: context.error, + transport: 'amqp', + msg: '[AMQP] Unable to open router connection (closed before open)' + }) + cleanupPromise() + reject(new Error('Router connection closed before opening')) + }) + + connection.once('disconnected', (context) => { + logger.error({ + err: context.error, + transport: 'amqp', + msg: '[AMQP] Unable to connect to router' + }) + cleanupPromise() + reject(context.error || new Error('Router disconnected during connect')) + }) + }) + } catch (error) { + this.connectionPromise = null + logger.error('[AMQP] Failed to create router connection', { + error: error.message, + stack: error.stack + }) + throw error + } + } + + async _buildConnectionOptions () { + if (this.connectionOptions && this.cachedCertificate) { + return { + ...this.connectionOptions, + cert: this.cachedCertificate.cert, + key: this.cachedCertificate.key, + ca: [this.cachedCertificate.ca] + } + } + + logger.debug({ msg: '[AMQP] Preparing router connection options' }) + + const { host, port } = await this._resolveRouterEndpoint() + logger.debug({ msg: '[AMQP] Router endpoint resolved', host, port }) + const certBundle = await this._ensureControllerCertificate() + + this.connectionOptions = { + transport: 'tls', + host, + hostname: host, + port, + rejectUnauthorized: true, + idle_time_out: 300000, + reconnect: true, + reconnect_limit: 100, + username: '', + password: '', + container_id: 'controller-exec-session-client' + } + this.cachedCertificate = certBundle + + logger.debug({ msg: '[AMQP] Router connection options built', host, port }) + + return { + ...this.connectionOptions, + cert: certBundle.cert, + key: certBundle.key, + ca: [certBundle.ca] + } + } + + async _resolveRouterEndpoint () { + logger.debug({ msg: '[AMQP] Resolving default router endpoint' }) + try { + const router = await this._getDefaultRouterRecord() + const port = router.messagingPort || AMQP_DEFAULT_PORT + let host = router.host && router.host.trim().length > 0 ? router.host.trim() : '' + + if (this._isKubernetes()) { + const namespace = process.env.CONTROLLER_NAMESPACE + if (namespace && namespace.trim().length > 0) { + host = `${DEFAULT_ROUTER_SERVICE}.${namespace}.svc.cluster.local` + } else if (!host) { + host = DEFAULT_ROUTER_SERVICE + } + } else { + if (!host) { + host = 'localhost' + } + } + logger.debug({ + msg: '[AMQP] Default router resolved', + routerHost: router.host, + computedHost: host, + port, + routerUuid: router.iofogUuid + }) + return { + host, + port, + routerUuid: router.iofogUuid + } + } catch (error) { + logger.error({ err: error, msg: '[AMQP] Failed while resolving router endpoint' }) + throw error + } + } + + async _getDefaultRouterRecord () { + if (this.cachedRouterRecord) { + return this.cachedRouterRecord + } + const router = await RouterManager.findOne({ isDefault: true }, this.fakeTransaction) + if (!router) { + throw new Error('Default router not found. Please ensure default router is provisioned.') + } + this.cachedRouterRecord = router + return router + } + + _isKubernetes () { + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + return controlPlane && controlPlane.toLowerCase() === 'kubernetes' + } + + async _ensureControllerCertificate () { + if (this.cachedCertificate) { + return this.cachedCertificate + } + if (this.certificatePromise) { + return this.certificatePromise + } + this.certificatePromise = (async () => { + try { + const bundle = await this._createControllerCertificate() + this.cachedCertificate = bundle + return bundle + } finally { + this.certificatePromise = null + } + })() + return this.certificatePromise + } + + async _createControllerCertificate () { + logger.debug('[AMQP] Ensuring controller certificate secret exists', { name: CONTROLLER_CERT_NAME }) + const existingSecret = await this._safeGetSecret(CONTROLLER_CERT_NAME) + const caName = await this._resolveCaName() + if (existingSecret) { + const caSecret = await this._safeGetSecret(caName) + const bundle = this._decodeCertificate(existingSecret, caSecret) + logger.debug({ msg: '[AMQP] Using existing controller-exec-session-client certificate', ca: caName }) + return bundle + } + + const hosts = this._buildControllerHosts() + logger.debug({ msg: '[AMQP] Generating controller-exec-session-client certificate', hosts, ca: caName }) + + try { + await CertificateService.createCertificateEndpoint({ + name: CONTROLLER_CERT_NAME, + subject: CONTROLLER_CERT_NAME, + hosts: hosts.join(','), + ca: { + type: 'direct', + secretName: caName + }, + expiration: 36 // months + }) + } catch (error) { + logger.error({ err: error, ca: caName, msg: '[AMQP] Failed to create controller certificate' }) + throw error + } + + const certSecret = await this._safeGetSecret(CONTROLLER_CERT_NAME) + const caSecret = await this._safeGetSecret(caName) + if (!certSecret || !caSecret) { + throw new Error('Controller certificate creation succeeded but secret not found') + } + logger.debug({ msg: '[AMQP] controller-exec-session-client certificate generated successfully', ca: caName }) + return this._decodeCertificate(certSecret, caSecret) + } + + async _resolveCaName () { + if (this._isKubernetes()) { + return 'default-router-local-ca' + } + const router = await this._getDefaultRouterRecord() + return `${router.iofogUuid}-local-ca` + } + + _buildControllerHosts () { + const hosts = new Set(['localhost', '127.0.0.1']) + if (hostname) hosts.add(hostname) + if (this._isKubernetes() && (process.env.CONTROLLER_NAMESPACE != null && process.env.CONTROLLER_NAMESPACE !== '')) { + hosts.add(`controller.${process.env.CONTROLLER_NAMESPACE}.svc.cluster.local`) + } + if (process.env.CONTROLLER_HOST) hosts.add(process.env.CONTROLLER_HOST) + return Array.from(hosts) + } + + _decodeCertificate (certSecret, caSecret) { + if (!certSecret || !certSecret.data) { + throw new Error(`Secret ${CONTROLLER_CERT_NAME} is empty or missing.`) + } + if (!caSecret || !caSecret.data) { + throw new Error('CA secret not found for router connection.') + } + const decode = (value, label) => { + if (!value) { + throw new Error(`Missing ${label} in certificate secret`) + } + return Buffer.from(value, 'base64') + } + return { + cert: decode(certSecret.data['tls.crt'], 'tls.crt'), + key: decode(certSecret.data['tls.key'], 'tls.key'), + ca: decode(caSecret.data['tls.crt'], 'ca.crt') + } + } + + async _safeGetSecret (name) { + try { + return await SecretService.getSecretEndpoint(name) + } catch (error) { + if (error.name === 'NotFoundError') { + logger.debug('[AMQP] Secret not found', { secret: name }) + return null + } + logger.error('[AMQP] Unexpected error while fetching secret', { + secret: name, + error: error.message, + stack: error.stack + }) + throw error + } + } +} + +module.exports = new RouterConnectionService() diff --git a/src/services/router-service.js b/src/services/router-service.js index 7614527fe..bcce7917b 100644 --- a/src/services/router-service.js +++ b/src/services/router-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -18,6 +18,10 @@ const Constants = require('../helpers/constants') const Errors = require('../helpers/errors') const ErrorMessages = require('../helpers/error-messages') const MicroserviceManager = require('../data/managers/microservice-manager') +const MicroserviceCapAddManager = require('../data/managers/microservice-cap-add-manager') +const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') +const ApplicationManager = require('../data/managers/application-manager') const MicroservicePortManager = require('../data/managers/microservice-port-manager') const RouterConnectionManager = require('../data/managers/router-connection-manager') const RouterManager = require('../data/managers/router-manager') @@ -25,6 +29,12 @@ const TransactionDecorator = require('../decorators/transaction-decorator') const Validator = require('../schemas') const ldifferenceWith = require('lodash/differenceWith') const constants = require('../helpers/constants') +const MicroserviceEnvManager = require('../data/managers/microservice-env-manager') +const SecretManager = require('../data/managers/secret-manager') +const FogManager = require('../data/managers/iofog-manager') + +const SITE_CONFIG_VERSION = 'pot' +const SITE_CONFIG_NAMESPACE = 'datasance' async function validateAndReturnUpstreamRouters (upstreamRouterIds, isSystemFog, defaultRouter, transaction) { if (!upstreamRouterIds) { @@ -33,7 +43,29 @@ async function validateAndReturnUpstreamRouters (upstreamRouterIds, isSystemFog, if (isSystemFog) { return [] } throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, Constants.DEFAULT_ROUTER_NAME)) } - return [defaultRouter] + + // Get all system fogs + const allSystemFogs = await FogManager.findAll({ isSystem: true }, transaction) + + // Get routers for each system fog + const systemFogRouters = [] + for (const systemFog of allSystemFogs) { + const systemFogRouter = await RouterManager.findOne({ iofogUuid: systemFog.uuid }, transaction) + if (systemFogRouter) { + systemFogRouters.push(systemFogRouter) + } + } + + // Combine default router with system fog routers, removing duplicates + const combinedRouters = [defaultRouter] + for (const systemFogRouter of systemFogRouters) { + // Check if this system fog router is not the same as the default router + if (systemFogRouter.id !== defaultRouter.id) { + combinedRouters.push(systemFogRouter) + } + } + + return combinedRouters } const upstreamRouters = [] @@ -51,9 +83,9 @@ async function validateAndReturnUpstreamRouters (upstreamRouterIds, isSystemFog, return upstreamRouters } -async function createRouterForFog (fogData, uuid, userId, upstreamRouters, transaction) { +async function createRouterForFog (fogData, uuid, upstreamRouters, transaction) { const isEdge = fogData.routerMode === 'edge' - const messagingPort = fogData.messagingPort || 5672 + const messagingPort = fogData.messagingPort || 5671 // Is default router if we are on a system fog and no other default router already exists const isDefault = (fogData.isSystem) ? !(await RouterManager.findOne({ isDefault: true }, transaction)) : false const routerData = { @@ -68,24 +100,25 @@ async function createRouterForFog (fogData, uuid, userId, upstreamRouters, trans const router = await RouterManager.create(routerData, transaction) - const microserviceConfig = _getRouterMicroserviceConfig(isEdge, uuid, messagingPort, router.interRouterPort, router.edgeRouterPort) + const microserviceConfig = await _getRouterMicroserviceConfig(isEdge, uuid, messagingPort, router.interRouterPort, router.edgeRouterPort, fogData.containerEngine, transaction) for (const upstreamRouter of upstreamRouters) { await RouterConnectionManager.create({ sourceRouter: router.id, destRouter: upstreamRouter.id }, transaction) - microserviceConfig.connectors = (microserviceConfig.connectors || []).concat(_getRouterConnectorConfig(isEdge, upstreamRouter)) + const connectorConfig = _getRouterConnectorConfig(isEdge, upstreamRouter, uuid) + microserviceConfig.connectors[connectorConfig.name] = connectorConfig } - const routerMicroservice = await _createRouterMicroservice(isEdge, uuid, userId, microserviceConfig, transaction) - await _createRouterPorts(routerMicroservice.uuid, messagingPort, userId, transaction) + const routerMicroservice = await _createRouterMicroservice(isEdge, uuid, microserviceConfig, transaction) + await _createRouterPorts(routerMicroservice.uuid, messagingPort, transaction) if (!isEdge) { - await _createRouterPorts(routerMicroservice.uuid, fogData.edgeRouterPort, userId, transaction) - await _createRouterPorts(routerMicroservice.uuid, fogData.interRouterPort, userId, transaction) + await _createRouterPorts(routerMicroservice.uuid, fogData.edgeRouterPort, transaction) + await _createRouterPorts(routerMicroservice.uuid, fogData.interRouterPort, transaction) } return router } -async function updateRouter (oldRouter, newRouterData, upstreamRouters, userId, transaction) { +async function updateRouter (oldRouter, newRouterData, upstreamRouters, containerEngine, transaction) { const routerCatalog = await CatalogService.getRouterCatalogItem(transaction) const routerMicroservice = await MicroserviceManager.findOne({ catalogItemId: routerCatalog.id, @@ -107,10 +140,10 @@ async function updateRouter (oldRouter, newRouterData, upstreamRouters, userId, } else if (!newRouterData.isEdge && oldRouter.isEdge) { // Moving from edge to internal // Nothing specific to update - await _createRouterPorts(routerMicroservice.uuid, newRouterData.edgeRouterPort, userId, transaction) - await _createRouterPorts(routerMicroservice.uuid, newRouterData.interRouterPort, userId, transaction) + await _createRouterPorts(routerMicroservice.uuid, newRouterData.edgeRouterPort, transaction) + await _createRouterPorts(routerMicroservice.uuid, newRouterData.interRouterPort, transaction) } - newRouterData.messagingPort = newRouterData.messagingPort || 5672 + newRouterData.messagingPort = newRouterData.messagingPort || 5671 await RouterManager.update({ id: oldRouter.id }, newRouterData, transaction) // Update upstream routers @@ -123,19 +156,19 @@ async function updateRouter (oldRouter, newRouterData, upstreamRouters, userId, await RouterConnectionManager.bulkCreate(upstreamToCreate.map(router => ({ sourceRouter: oldRouter.id, destRouter: router.id })), transaction) // Update proxy microservice (If port or host changed) - const proxyCatalog = await CatalogService.getProxyCatalogItem(transaction) - const existingProxy = await MicroserviceManager.findOne({ iofogUuid: oldRouter.iofogUuid, catalogItemId: proxyCatalog.id }, transaction) - if (existingProxy) { - const config = JSON.parse(existingProxy.config || '{}') - config.networkRouter = { - host: newRouterData.host || oldRouter.host, - port: newRouterData.messagingPort - } - await MicroserviceManager.updateIfChanged({ uuid: existingProxy.uuid }, { config: JSON.stringify(config) }, transaction) - } + // const proxyCatalog = await CatalogService.getProxyCatalogItem(transaction) + // const existingProxy = await MicroserviceManager.findOne({ iofogUuid: oldRouter.iofogUuid, catalogItemId: proxyCatalog.id }, transaction) + // if (existingProxy) { + // const config = JSON.parse(existingProxy.config || '{}') + // config.networkRouter = { + // host: newRouterData.host || oldRouter.host, + // port: newRouterData.messagingPort + // } + // await MicroserviceManager.updateIfChanged({ uuid: existingProxy.uuid }, { config: JSON.stringify(config) }, transaction) + // } // Update config if needed - await updateConfig(oldRouter.id, userId, transaction) + await updateConfig(oldRouter.id, containerEngine, transaction) await ChangeTrackingService.update(oldRouter.iofogUuid, ChangeTrackingService.events.routerChanged, transaction) await ChangeTrackingService.update(oldRouter.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) await ChangeTrackingService.update(oldRouter.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) @@ -153,52 +186,113 @@ async function _deleteRouterPorts (routerMicroserviceUuid, port, transaction) { await MicroservicePortManager.delete({ microserviceUuid: routerMicroserviceUuid, portInternal: port }, transaction) } -async function updateConfig (routerID, userId, transaction) { +async function _updateRouterPorts (routerMicroserviceUuid, router, transaction) { + await MicroservicePortManager.delete({ microserviceUuid: routerMicroserviceUuid }, transaction) + await _createRouterPorts(routerMicroserviceUuid, router.messagingPort, transaction) + if (!router.isEdge) { + await _createRouterPorts(routerMicroserviceUuid, router.edgeRouterPort, transaction) + await _createRouterPorts(routerMicroserviceUuid, router.interRouterPort, transaction) + } +} + +async function updateConfig (routerID, containerEngine, transaction) { const router = await RouterManager.findOne({ id: routerID }, transaction) if (!router) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, routerID)) } - const microserviceConfig = _getRouterMicroserviceConfig(router.isEdge, router.iofogUuid, router.messagingPort, router.interRouterPort, router.edgeRouterPort) - - const upstreamRoutersConnections = await RouterConnectionManager.findAllWithRouters({ sourceRouter: router.id }, transaction) - for (const upstreamRouterConnection of upstreamRoutersConnections) { - microserviceConfig.connectors = (microserviceConfig.connectors || []).concat(_getRouterConnectorConfig(router.isEdge, upstreamRouterConnection.dest)) - } + // Get current configuration const routerCatalog = await CatalogService.getRouterCatalogItem(transaction) const routerMicroservice = await MicroserviceManager.findOne({ catalogItemId: routerCatalog.id, iofogUuid: router.iofogUuid }, transaction) + if (!routerMicroservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, router.id)) } - if (routerMicroservice.config !== JSON.stringify(microserviceConfig)) { - await MicroserviceManager.update({ uuid: routerMicroservice.uuid }, { config: JSON.stringify(microserviceConfig) }, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + // Generate new configuration + const newConfig = await _getRouterMicroserviceConfig( + router.isEdge, + router.iofogUuid, + router.messagingPort, + router.interRouterPort, + router.edgeRouterPort, + containerEngine, + transaction + ) + + // Add connectors for upstream routers + const upstreamRoutersConnections = await RouterConnectionManager.findAllWithRouters( + { sourceRouter: router.id }, + transaction + ) - if (_listenersChanged(JSON.parse(routerMicroservice.config || '{}').listeners, microserviceConfig.listeners)) { - MicroservicePortManager.delete({ microserviceUuid: routerMicroservice.uuid }, transaction) - await _createRouterPorts(routerMicroservice.uuid, router.messagingPort, userId, transaction) - if (!router.isEdge) { - await _createRouterPorts(routerMicroservice.uuid, router.edgeRouterPort, userId, transaction) - await _createRouterPorts(routerMicroservice.uuid, router.interRouterPort, userId, transaction) - } - await MicroserviceManager.update({ uuid: routerMicroservice.uuid }, { rebuild: true }, transaction) - await ChangeTrackingService.update(router.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) + for (const upstreamRouterConnection of upstreamRoutersConnections) { + const connectorConfig = _getRouterConnectorConfig( + router.isEdge, + upstreamRouterConnection.dest, + router.iofogUuid + ) + newConfig.connectors[connectorConfig.name] = connectorConfig + } + + // Check if configuration needs update + if (JSON.stringify(currentConfig) !== JSON.stringify(newConfig)) { + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { config: JSON.stringify(newConfig) }, + transaction + ) + + // Check if listeners changed + if (_listenersChanged(currentConfig.listeners, newConfig.listeners)) { + await _updateRouterPorts(routerMicroservice.uuid, router, transaction) + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { rebuild: true }, + transaction + ) + await ChangeTrackingService.update( + router.iofogUuid, + ChangeTrackingService.events.microserviceList, + transaction + ) } else { - await ChangeTrackingService.update(router.iofogUuid, ChangeTrackingService.events.microserviceConfig, transaction) + // await MicroserviceManager.update( + // { uuid: routerMicroservice.uuid }, + // { rebuild: true }, + // transaction + // ) + await ChangeTrackingService.update( + router.iofogUuid, + ChangeTrackingService.events.microserviceConfig, + transaction + ) } } } function _listenersChanged (currentListeners, newListeners) { - if (currentListeners.length !== newListeners.length) { + if (!currentListeners || !newListeners) { + return true + } + + // Convert to arrays if they're objects + const currentArray = Object.values(currentListeners) + const newArray = Object.values(newListeners) + + if (currentArray.length !== newArray.length) { return true } - for (const listener of currentListeners) { - if (newListeners.findIndex(l => l.port === listener.port) === -1) { + // Compare only port property + for (const currentListener of currentArray) { + const matchingListener = newArray.find(l => l.port === currentListener.port) + if (!matchingListener) { return true } } @@ -206,72 +300,222 @@ function _listenersChanged (currentListeners, newListeners) { return false } -function _createRouterPorts (routerMicroserviceUuid, port, userId, transaction) { +function _createRouterPorts (routerMicroserviceUuid, port, transaction) { + // Skip port mapping for default AMQP listener (5672) + if (port === 5672) { + return Promise.resolve() + } + const mappingData = { - isPublic: false, + // isPublic: false, portInternal: port, portExternal: port, - userId: userId, microserviceUuid: routerMicroserviceUuid } return MicroservicePortManager.create(mappingData, transaction) } -async function _createRouterMicroservice (isEdge, uuid, userId, microserviceConfig, transaction) { +async function _createRouterMicroservice (isEdge, uuid, microserviceConfig, transaction) { const routerCatalog = await CatalogService.getRouterCatalogItem(transaction) + const hostNetworkMode = !isEdge + const routerApplicationData = { + name: `system-${uuid.toLowerCase()}`, + isActivated: true, + isSystem: true + } const routerMicroserviceData = { - uuid: AppHelper.generateRandomString(32), - name: `Router for Fog ${uuid}`, + uuid: AppHelper.generateUUID(), + name: `router-${uuid.toLowerCase()}`, config: JSON.stringify(microserviceConfig), catalogItemId: routerCatalog.id, iofogUuid: uuid, - rootHostAccess: false, + hostNetworkMode: hostNetworkMode, + isPrivileged: false, logSize: constants.MICROSERVICE_DEFAULT_LOG_SIZE, - userId, - configLastUpdated: Date.now() + schedule: 0, + configLastUpdated: Date.now(), + env: [ + { + key: 'QDROUTERD_CONF', + value: '/home/runner/skupper-router-certs/skrouterd.json' + }, + { + key: 'QDROUTERD_CONF_TYPE', + value: 'json' + }, + { + key: 'SKUPPER_SITE_ID', + value: uuid + } + ] + } + + const capAddValues = [ + { capAdd: 'NET_RAW' } + ] + let application = await ApplicationManager.findOne({ name: routerApplicationData.name }, transaction) + if (!application) { + application = await ApplicationManager.create(routerApplicationData, transaction) + } + routerMicroserviceData.applicationId = application.id + const routerMicroservice = await MicroserviceManager.create(routerMicroserviceData, transaction) + await MicroserviceStatusManager.create({ microserviceUuid: routerMicroserviceData.uuid }, transaction) + await MicroserviceExecStatusManager.create({ microserviceUuid: routerMicroserviceData.uuid }, transaction) + for (const capAdd of capAddValues) { + await MicroserviceCapAddManager.create({ + microserviceUuid: routerMicroserviceData.uuid, + capAdd: capAdd.capAdd + }, transaction) } - return MicroserviceManager.create(routerMicroserviceData, transaction) + + // Create environment variables + for (const env of routerMicroserviceData.env) { + await MicroserviceEnvManager.create({ + microserviceUuid: routerMicroserviceData.uuid, + key: env.key, + value: env.value + }, transaction) + } + + return routerMicroservice } -function _getRouterConnectorConfig (isEdge, dest) { - return { +function _getRouterConnectorConfig (isEdge, dest, uuid) { + const config = { name: dest.iofogUuid || Constants.DEFAULT_ROUTER_NAME, - role: (isEdge ? 'edge' : 'inter-router'), + role: isEdge ? 'edge' : 'inter-router', host: dest.host, - port: (isEdge ? dest.edgeRouterPort : dest.interRouterPort) + port: (isEdge ? dest.edgeRouterPort : dest.interRouterPort).toString(), + sslProfile: `${uuid}-site-server` } + + return config } -function _getRouterMicroserviceConfig (isEdge, uuid, messagingPort, interRouterPort, edgeRouterPort) { - const microserviceConfig = { - mode: isEdge ? 'edge' : 'interior', - id: uuid, - listeners: [ - { - role: 'normal', - host: '0.0.0.0', - port: messagingPort +async function _getRouterMicroserviceConfig (isEdge, uuid, messagingPort, interRouterPort, edgeRouterPort, containerEngine, transaction) { + let platform = 'docker' + if (containerEngine === 'podman') { + platform = 'podman' + } + + let namespace = SITE_CONFIG_NAMESPACE + if (process.env.CONTROLLER_NAMESPACE) { + namespace = process.env.CONTROLLER_NAMESPACE + } + const config = { + addresses: { + mc: { + prefix: 'mc', + distribution: 'multicast' } - ] + }, + bridges: { + tcpConnectors: {}, + tcpListeners: {} + }, + connectors: {}, + listeners: {}, + logConfig: { + ROUTER_CORE: { + enable: 'error+', + module: 'ROUTER_CORE' + } + }, + metadata: { + helloMaxAgeSeconds: '3', + id: uuid, + mode: isEdge ? 'edge' : 'interior' + }, + siteConfig: { + name: uuid, + namespace: namespace, + platform: platform, + version: SITE_CONFIG_VERSION + }, + sslProfiles: {} + } + + // Get SSL secrets for all profiles + const siteServerSecret = await SecretManager.getSecret(`${uuid}-site-server`, transaction) + const localServerSecret = await SecretManager.getSecret(`${uuid}-local-server`, transaction) + const localAgentSecret = await SecretManager.getSecret(`${uuid}-local-agent`, transaction) + + // Add SSL profiles + if (siteServerSecret) { + config.sslProfiles[`${uuid}-site-server`] = { + caCert: siteServerSecret.data['ca.crt'], + tlsCert: siteServerSecret.data['tls.crt'], + tlsKey: siteServerSecret.data['tls.key'], + name: `${uuid}-site-server` + } + } + + if (localServerSecret) { + config.sslProfiles[`${uuid}-local-server`] = { + caCert: localServerSecret.data['ca.crt'], + tlsCert: localServerSecret.data['tls.crt'], + tlsKey: localServerSecret.data['tls.key'], + name: `${uuid}-local-server` + } + } + + if (localAgentSecret) { + config.sslProfiles[`${uuid}-local-agent`] = { + caCert: localAgentSecret.data['ca.crt'], + tlsCert: localAgentSecret.data['tls.crt'], + tlsKey: localAgentSecret.data['tls.key'], + name: `${uuid}-local-agent` + } + } + + // Add default AMQP listener (internal) + config.listeners[`${uuid}-amqp`] = { + host: '0.0.0.0', + name: `${uuid}-amqp`, + port: 5672, + role: 'normal' } + // Add AMQPS listener + const amqpsListener = { + host: '0.0.0.0', + name: `${uuid}-amqps`, + port: messagingPort, + role: 'normal', + authenticatePeer: true, + saslMechanisms: 'EXTERNAL', + sslProfile: `${uuid}-local-server` + } + config.listeners[`${uuid}-amqps`] = amqpsListener + if (!isEdge) { - microserviceConfig.listeners.push( - { - role: 'inter-router', - host: '0.0.0.0', - port: interRouterPort - }, - { - role: 'edge', - host: '0.0.0.0', - port: edgeRouterPort - } - ) + // Add inter-router listener + const interRouterListener = { + host: '0.0.0.0', + name: `${uuid}-inter-router`, + port: interRouterPort, + role: 'inter-router', + authenticatePeer: true, + saslMechanisms: 'EXTERNAL', + sslProfile: `${uuid}-site-server` + } + config.listeners[`${uuid}-inter-router`] = interRouterListener + + // Add edge listener + const edgeListener = { + host: '0.0.0.0', + name: `${uuid}-edge`, + port: edgeRouterPort, + role: 'edge', + authenticatePeer: true, + saslMechanisms: 'EXTERNAL', + sslProfile: `${uuid}-site-server` + } + config.listeners[`${uuid}-edge`] = edgeListener } - return microserviceConfig + return config } async function getNetworkRouter (networkRouterId, transaction) { @@ -303,10 +547,10 @@ async function upsertDefaultRouter (routerData, transaction) { const createRouterData = { isEdge: false, - messagingPort: routerData.messagingPort || 5672, + messagingPort: routerData.messagingPort || 5671, host: routerData.host, - edgeRouterPort: routerData.edgeRouterPort || 56722, - interRouterPort: routerData.interRouterPort || 56721, + edgeRouterPort: routerData.edgeRouterPort || 45671, + interRouterPort: routerData.interRouterPort || 55671, isDefault: true } diff --git a/src/services/routing-service.js b/src/services/routing-service.js index f2dde0610..f6cb1fdc4 100644 --- a/src/services/routing-service.js +++ b/src/services/routing-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -21,7 +21,7 @@ const RoutingManager = require('../data/managers/routing-manager') const TransactionDecorator = require('../decorators/transaction-decorator') const Validator = require('../schemas') -async function getRoutings (user, isCLI, transaction) { +async function getRoutings (isCLI, transaction) { const routes = await RoutingManager.findAllPopulated({}, transaction) return { routes: routes.map(r => ({ application: r.application.name, @@ -34,7 +34,7 @@ async function getRoutings (user, isCLI, transaction) { })) } } -async function getRouting (appName, name, user, isCLI, transaction) { +async function getRouting (appName, name, isCLI, transaction) { const application = await ApplicationManager.findOne({ name: appName }, transaction) if (!application) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_NAME, appName)) @@ -54,7 +54,7 @@ async function getRouting (appName, name, user, isCLI, transaction) { } } -async function _validateRouteMsvc (routingData, user, isCLI, transaction) { +async function _validateRouteMsvc (routingData, isCLI, transaction) { // Retro compatibility logic if (routingData.sourceMicroserviceUuid) { const sourceWhere = { uuid: routingData.sourceMicroserviceUuid } @@ -70,7 +70,7 @@ async function _validateRouteMsvc (routingData, user, isCLI, transaction) { } return { sourceMicroservice, destMicroservice } } else { - const applicationWhere = isCLI ? { name: routingData.application } : { name: routingData.application, userId: user.id } + const applicationWhere = isCLI ? { name: routingData.application } : { name: routingData.application } const application = await ApplicationManager.findOne(applicationWhere, transaction) if (!application) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, routingData.application)) @@ -91,15 +91,15 @@ async function _validateRouteMsvc (routingData, user, isCLI, transaction) { } } -async function createRouting (routingData, user, isCLI, transaction) { +async function createRouting (routingData, isCLI, transaction) { await Validator.validate(routingData, Validator.schemas.routingCreate) - const { sourceMicroservice, destMicroservice } = await _validateRouteMsvc(routingData, user, isCLI, transaction) + const { sourceMicroservice, destMicroservice } = await _validateRouteMsvc(routingData, isCLI, transaction) - return _createRoute(sourceMicroservice, destMicroservice, routingData, user, transaction) + return _createRoute(sourceMicroservice, destMicroservice, routingData, transaction) } -async function updateRouting (appName, routeName, routeData, user, isCLI, transaction) { +async function updateRouting (appName, routeName, routeData, isCLI, transaction) { await Validator.validate(routeData, Validator.schemas.routingUpdate) const application = await ApplicationManager.findOne({ name: appName }, transaction) if (!application) { @@ -111,7 +111,7 @@ async function updateRouting (appName, routeName, routeData, user, isCLI, transa throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTING_NAME, routeName)) } - const { sourceMicroservice, destMicroservice } = await _validateRouteMsvc({ ...routeData, application: oldRoute.application.name }, user, isCLI, transaction) + const { sourceMicroservice, destMicroservice } = await _validateRouteMsvc({ ...routeData, application: oldRoute.application.name }, isCLI, transaction) const updateRebuildMs = { rebuild: true @@ -143,7 +143,7 @@ async function updateRouting (appName, routeName, routeData, user, isCLI, transa await RoutingManager.update({ id: oldRoute.id }, updateRouteData, transaction) } -async function _createRoute (sourceMicroservice, destMicroservice, routeData, user, transaction) { +async function _createRoute (sourceMicroservice, destMicroservice, routeData, transaction) { if (!sourceMicroservice.iofogUuid || !destMicroservice.iofogUuid) { throw new Errors.ValidationError('fog not set') } @@ -162,7 +162,7 @@ async function _createRoute (sourceMicroservice, destMicroservice, routeData, us return _createSimpleRoute(sourceMicroservice, destMicroservice, routeData, transaction) } -async function deleteRouting (appName, name, user, isCLI, transaction) { +async function deleteRouting (appName, name, isCLI, transaction) { const application = await ApplicationManager.findOne({ name: appName }, transaction) if (!application) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_NAME, appName)) diff --git a/src/services/scheduler-access-token-service.js b/src/services/scheduler-access-token-service.js deleted file mode 100644 index 0dec87c4d..000000000 --- a/src/services/scheduler-access-token-service.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const AppHelper = require('../helpers/app-helper') -const SchedulerAccessTokenManager = require('../data/managers/scheduler-access-token-manager') - -const Config = require('../config') - -const generateAccessToken = async function (transaction) { - while (true) { - const newAccessToken = AppHelper.generateAccessToken() - const exists = await SchedulerAccessTokenManager.findOne({ - token: newAccessToken - }, transaction) - if (!exists) { - const accessTokenExpiryTime = Date.now() + Config.get('Settings:SchedulerTokenExpirationIntervalSeconds') * 99999 - return { - token: newAccessToken, - expirationTime: accessTokenExpiryTime - } - } - } -} - -async function updateAccessToken (userId, newAccessToken, transaction) { - return SchedulerAccessTokenManager.updateOrCreate({ - userId: userId - }, { - userId: userId, - token: newAccessToken.token, - expirationTime: newAccessToken.expirationTime - }, transaction) -} - -module.exports = { - generateAccessToken, - updateAccessToken -} diff --git a/src/services/secret-service.js b/src/services/secret-service.js new file mode 100644 index 000000000..c788d2552 --- /dev/null +++ b/src/services/secret-service.js @@ -0,0 +1,248 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const TransactionDecorator = require('../decorators/transaction-decorator') +const SecretManager = require('../data/managers/secret-manager') +const MicroserviceManager = require('../data/managers/microservice-manager') +const MicroserviceEnvManager = require('../data/managers/microservice-env-manager') +const ChangeTrackingService = require('./change-tracking-service') +const AppHelper = require('../helpers/app-helper') +const Errors = require('../helpers/errors') +const ErrorMessages = require('../helpers/error-messages') +const Validator = require('../schemas/index') +const VolumeMountService = require('./volume-mount-service') +const VolumeMountingManager = require('../data/managers/volume-mounting-manager') +const CertificateManager = require('../data/managers/certificate-manager') + +function validateBase64 (value) { + try { + const decoded = Buffer.from(value, 'base64').toString('utf-8') + const reencoded = Buffer.from(decoded).toString('base64') + return reencoded === value + } catch (error) { + return false + } +} + +function validateSecretData (type, data) { + if (type === 'tls') { + const invalidKeys = Object.entries(data) + .filter(([_, value]) => !validateBase64(value)) + .map(([key]) => key) + + if (invalidKeys.length > 0) { + throw new Errors.ValidationError( + `Invalid base64 encoding for keys: ${invalidKeys.join(', ')}` + ) + } + } +} + +async function createSecretEndpoint (secretData, transaction) { + const validation = await Validator.validate(secretData, Validator.schemas.secretCreate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + + validateSecretData(secretData.type, secretData.data) + + const existingSecret = await SecretManager.findOne({ name: secretData.name }, transaction) + if (existingSecret) { + throw new Errors.ConflictError(AppHelper.formatMessage(ErrorMessages.SECRET_ALREADY_EXISTS, secretData.name)) + } + + const secret = await SecretManager.createSecret(secretData.name, secretData.type, secretData.data, transaction) + return { + id: secret.id, + name: secret.name, + type: secret.type, + created_at: secret.created_at, + updated_at: secret.updated_at + } +} + +async function updateSecretEndpoint (secretName, secretData, transaction) { + const validation = await Validator.validate(secretData, Validator.schemas.secretUpdate) + if (!validation.valid) { + throw new Errors.ValidationError(validation.error) + } + + const existingSecret = await SecretManager.findOne({ name: secretName }, transaction) + if (!existingSecret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, secretName)) + } + + validateSecretData(existingSecret.type, secretData.data) + + const secret = await SecretManager.updateSecret(secretName, secretData.data, transaction) + await _updateChangeTrackingForFogs(secretName, transaction) + await _updateMicroservicesUsingSecret(secretName, transaction) + return { + id: secret.id, + name: secret.name, + type: secret.type, + created_at: secret.created_at, + updated_at: secret.updated_at + } +} + +async function getSecretEndpoint (secretName, transaction) { + const secret = await SecretManager.getSecret(secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, secretName)) + } + + return { + id: secret.id, + name: secret.name, + type: secret.type, + data: secret.data, + created_at: secret.created_at, + updated_at: secret.updated_at + } +} + +async function listSecretsEndpoint (transaction) { + const secrets = await SecretManager.listSecrets(transaction) + return { + secrets: secrets.map(secret => ({ + id: secret.id, + name: secret.name, + type: secret.type, + created_at: secret.created_at, + updated_at: secret.updated_at + })) + } +} + +async function deleteSecretEndpoint (secretName, transaction) { + const existingSecret = await SecretManager.findOne({ name: secretName }, transaction) + if (!existingSecret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, secretName)) + } + + if (existingSecret.type === 'tls') { + const certificate = await CertificateManager.findCertificateByName(secretName, transaction) + if (certificate) { + if (certificate.isCA) { + // Check if this CA has signed certificates + const signedCerts = await CertificateManager.findCertificatesByCA(certificate.id, transaction) + if (signedCerts.length > 0) { + throw new Errors.ValidationError(`Cannot delete CA that has signed certificates. Please delete the following certificates first: ${signedCerts.map(cert => cert.name).join(', ')}`) + } + await CertificateManager.deleteCertificate(certificate.name, transaction) + await _deleteVolumeMountsUsingSecret(secretName, transaction) + } else { + await CertificateManager.deleteCertificate(certificate.name, transaction) + await _deleteVolumeMountsUsingSecret(secretName, transaction) + } + } + } else { + await SecretManager.deleteSecret(secretName, transaction) + await _deleteVolumeMountsUsingSecret(secretName, transaction) + } + return {} +} + +async function _deleteVolumeMountsUsingSecret (secretName, transaction) { + const volumeMounts = await VolumeMountingManager.findAll({ secretName: secretName }, transaction) + if (volumeMounts.length > 0) { + for (const volumeMount of volumeMounts) { + await VolumeMountService.deleteVolumeMountEndpoint(volumeMount.name, transaction) + } + } +} + +async function _updateChangeTrackingForFogs (secretName, transaction) { + const secretVolumeMounts = await VolumeMountingManager.findAll({ secretName: secretName }, transaction) + if (secretVolumeMounts.length > 0) { + for (const secretVolumeMount of secretVolumeMounts) { + const volumeMountObj = { + name: secretVolumeMount.name, + secretName: secretName + } + await VolumeMountService.updateVolumeMountEndpoint(secretVolumeMount.name, volumeMountObj, transaction) + } + } +} + +async function _updateMicroservicesUsingSecret (secretName, transaction) { + // Find all microservice environment variables that use this secret + const envVars = await MicroserviceEnvManager.findAll({ + valueFromSecret: { [require('sequelize').Op.like]: `${secretName}/%` } + }, transaction) + + if (envVars.length === 0) { + return + } + + // Get the updated secret data + const secret = await SecretManager.getSecret(secretName, transaction) + if (!secret) { + return + } + + // Group environment variables by microservice UUID + const microserviceEnvMap = new Map() + for (const envVar of envVars) { + if (!microserviceEnvMap.has(envVar.microserviceUuid)) { + microserviceEnvMap.set(envVar.microserviceUuid, []) + } + microserviceEnvMap.get(envVar.microserviceUuid).push(envVar) + } + + // Update each microservice's environment variables and change tracking + for (const [microserviceUuid, envVars] of microserviceEnvMap) { + // Get the microservice to access its iofogUuid + const microservice = await MicroserviceManager.findOne({ uuid: microserviceUuid }, transaction) + if (!microservice) { + continue + } + + // Update each environment variable with the new secret data + for (const envVar of envVars) { + const [secretNameFromRef, dataKey] = envVar.valueFromSecret.split('/') + if (secretNameFromRef === secretName && secret.data[dataKey]) { + let newValue = secret.data[dataKey] + + // If it's a TLS secret, decode the base64 value + if (secret.type === 'tls') { + try { + newValue = Buffer.from(secret.data[dataKey], 'base64').toString('utf-8') + } catch (error) { + // Skip this environment variable if base64 decoding fails + continue + } + } + + // Update the environment variable value with the new secret data + await MicroserviceEnvManager.update( + { id: envVar.id }, + { value: newValue }, + transaction + ) + } + } + + // Update change tracking for the microservice's fog node + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction) + } +} + +module.exports = { + createSecretEndpoint: TransactionDecorator.generateTransaction(createSecretEndpoint), + updateSecretEndpoint: TransactionDecorator.generateTransaction(updateSecretEndpoint), + getSecretEndpoint: TransactionDecorator.generateTransaction(getSecretEndpoint), + listSecretsEndpoint: TransactionDecorator.generateTransaction(listSecretsEndpoint), + deleteSecretEndpoint: TransactionDecorator.generateTransaction(deleteSecretEndpoint) +} diff --git a/src/services/services-service.js b/src/services/services-service.js new file mode 100644 index 000000000..2b2c0b591 --- /dev/null +++ b/src/services/services-service.js @@ -0,0 +1,1280 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const TransactionDecorator = require('../decorators/transaction-decorator') +const ServiceManager = require('../data/managers/service-manager') +const MicroserviceManager = require('../data/managers/microservice-manager') +const RouterManager = require('../data/managers/router-manager') +const RouterConnectionManager = require('../data/managers/router-connection-manager') +const K8sClient = require('../utils/k8s-client') +const AppHelper = require('../helpers/app-helper') +const config = require('../config') +const Errors = require('../helpers/errors') +const ErrorMessages = require('../helpers/error-messages') +const Validator = require('../schemas') +const logger = require('../logger') +const FogManager = require('../data/managers/iofog-manager') +const TagsManager = require('../data/managers/tags-manager') +const ChangeTrackingService = require('./change-tracking-service') +const ApplicationManager = require('../data/managers/application-manager') +// const { Op } = require('sequelize') + +const K8S_ROUTER_CONFIG_MAP = 'pot-router' +const SERVICE_ANNOTATION_TAG = 'service.datasance.com/tag' + +// Map service tags to string array +// Return plain JS object +function _mapTags (service) { + return service.tags ? service.tags.map(t => t.value) : [] +} + +async function _setTags (serviceModel, tagsArray, transaction) { + if (tagsArray) { + let tags = [] + for (const tag of tagsArray) { + let tagModel = await TagsManager.findOne({ value: tag }, transaction) + if (!tagModel) { + tagModel = await TagsManager.create({ value: tag }, transaction) + } + tags.push(tagModel) + } + await serviceModel.setTags(tags) + } +} + +async function handleServiceDistribution (serviceTags, transaction) { + // Always find fog nodes with 'all' tag + const allTaggedFogNodes = await FogManager.findAllWithTags({ + '$tags.value$': `${SERVICE_ANNOTATION_TAG}: all` + }, transaction) + + // If serviceTags is null or empty, return only fog nodes with 'all' tag + if (!serviceTags || serviceTags.length === 0) { + const uuids = allTaggedFogNodes.map(fog => fog.uuid) + return uuids + } + + // Filter tags that don't contain ':' or '=' + const filteredServiceTags = serviceTags + .filter(tag => tag != null) + .map(tag => String(tag)) + .filter(tag => !tag.includes(':') && !tag.includes('=')) + .filter(tag => tag.length > 0) + + if (filteredServiceTags.length === 0) { + const uuids = allTaggedFogNodes.map(fog => fog.uuid) + return uuids + } + + // Find fog nodes for each filtered tag + const specificTaggedFogNodes = new Set() + for (const tag of filteredServiceTags) { + const fogNodes = await FogManager.findAllWithTags({ + '$tags.value$': `${SERVICE_ANNOTATION_TAG}: ${tag}` + }, transaction) + fogNodes.forEach(fog => specificTaggedFogNodes.add(fog.uuid)) + } + + // Get all tag fog node UUIDs + const allTagUuids = allTaggedFogNodes.map(fog => fog.uuid) + + // Combine both sets of fog nodes and remove duplicates + const allFogUuids = new Set([...allTagUuids, ...Array.from(specificTaggedFogNodes)]) + + return Array.from(allFogUuids) +} + +async function checkKubernetesEnvironment () { + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + return controlPlane && controlPlane.toLowerCase() === 'kubernetes' +} + +async function validateNonK8sType (serviceConfig) { + const isK8s = await checkKubernetesEnvironment() + if (serviceConfig.type.toLowerCase() !== 'k8s' && isK8s) { + if (!serviceConfig.k8sType || !serviceConfig.servicePort) { + throw new Errors.ValidationError('Kubernetes environment is required for k8s service type(LoadBalancer or ClusterIP or NodePort) and service port') + } + } +} + +async function _validateServiceName (serviceConfig) { + if (serviceConfig.name.toLowerCase() === 'controller' || serviceConfig.name.toLowerCase() === 'router' || serviceConfig.name.toLowerCase() === 'router-internal' || serviceConfig.name.toLowerCase() === 'docker' || serviceConfig.name.toLowerCase() === 'podman' || serviceConfig.name.toLowerCase() === 'kubernetes') { + throw new Errors.ValidationError('Service name cannot be "controller" or "router" or "router-internal" or "docker"') + } +} + +async function validateMicroserviceType (serviceConfig, transaction) { + if (serviceConfig.type.toLowerCase() !== 'microservice') { + return + } + + let microserviceUuid = serviceConfig.resource + + // If resource contains "/", it means user provided "/" + if (serviceConfig.resource.includes('/')) { + const [appName, microserviceName] = serviceConfig.resource.split('/') + const app = await ApplicationManager.findOne({ name: appName }, transaction) + if (!app) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_APPLICATION_NAME, appName)) + } + const microservice = await MicroserviceManager.findOne({ + name: microserviceName, + applicationId: app.id + }, transaction) + + if (!microservice) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_NAME, serviceConfig.resource)) + } + + microserviceUuid = microservice.uuid + } else { + // User provided UUID directly, validate if microservice exists + const microservice = await MicroserviceManager.findOne({ uuid: serviceConfig.resource }, transaction) + if (!microservice) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, serviceConfig.resource)) + } + } + + // Update resource to be the microservice UUID + serviceConfig.resource = microserviceUuid +} + +async function validateFogServiceType (serviceConfig, transaction) { + if (serviceConfig.type.toLowerCase() !== 'agent') { + return + } + + // First try to find fog node by name + let fogNode = await FogManager.findOne({ name: serviceConfig.resource }, transaction) + + // If not found by name, try to find by UUID + if (!fogNode) { + fogNode = await FogManager.findOne({ uuid: serviceConfig.resource }, transaction) + } + + // If still not found, throw error + if (!fogNode) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, serviceConfig.resource)) + } + + // Always set resource to be the fog node UUID + serviceConfig.resource = fogNode.uuid +} + +async function validateDefaultBridge (serviceConfig, transaction) { + // If defaultBridge is empty, set it to 'default-router' + if (!serviceConfig.defaultBridge) { + logger.debug('Setting default bridge to default-router') + serviceConfig.defaultBridge = 'default-router' + return + } + + // If service type is not microservice or agent, defaultBridge must be 'default-router' + if (serviceConfig.type.toLowerCase() !== 'microservice' && serviceConfig.type.toLowerCase() !== 'agent') { + if (serviceConfig.defaultBridge !== 'default-router') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_DEFAULT_BRIDGE, serviceConfig.defaultBridge)) + } + return + } + + // For microservice or agent type, if user provided a UUID instead of 'default-router' + if (serviceConfig.defaultBridge !== 'default-router') { + let iofogUuid + + if (serviceConfig.type.toLowerCase() === 'microservice') { + // Get the microservice to find its iofog node + const microservice = await MicroserviceManager.findOne({ uuid: serviceConfig.resource }, transaction) + if (!microservice) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, serviceConfig.resource)) + } + iofogUuid = microservice.iofogUuid + } else if (serviceConfig.type.toLowerCase() === 'agent') { + // For agent type, the resource is the agent UUID + iofogUuid = serviceConfig.resource + } + + // Get the router for the iofog node + const router = await RouterManager.findOne({ iofogUuid: iofogUuid }, transaction) + if (!router) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, iofogUuid)) + } + + // Check if the router has a connection to the specified upstream router + const upstreamRouter = await RouterManager.findOne({ iofogUuid: serviceConfig.defaultBridge }, transaction) + if (!upstreamRouter) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER, serviceConfig.defaultBridge)) + } + + const routerConnection = await RouterConnectionManager.findOne({ + sourceRouter: router.id, + destRouter: upstreamRouter.id + }, transaction) + + if (!routerConnection) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_CONNECTION, serviceConfig.defaultBridge, router.id)) + } + } +} + +async function defineBridgePort (serviceConfig, transaction) { + // Get bridge port range from environment or config + const bridgePortRangeStr = process.env.BRIDGE_PORTS_RANGE || config.get('bridgePorts.range') || '10024-65535' + const [startStr, endStr] = bridgePortRangeStr.split('-') + const start = parseInt(startStr) + const end = parseInt(endStr) + + // Get all existing services to check used ports + const existingServices = await ServiceManager.findAll({}, transaction) + const usedPorts = new Set(existingServices.map(service => service.bridgePort)) + + // Find the first available port in the range + let bridgePort = start + while (bridgePort <= end) { + if (!usedPorts.has(bridgePort)) { + serviceConfig.bridgePort = bridgePort + return + } + bridgePort++ + } + + // If we get here, no ports are available + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.NO_AVAILABLE_BRIDGE_PORT, bridgePortRangeStr)) +} + +// Helper function to determine host based on service type +async function _determineConnectorHost (serviceConfig, transaction) { + switch (serviceConfig.type.toLowerCase()) { + case 'microservice': + const microservice = await MicroserviceManager.findOne({ uuid: serviceConfig.resource }, transaction) + if (microservice.hostNetworkMode) { + return 'iofog' + } else { + return `iofog_${serviceConfig.resource}` + } + case 'agent': + return 'iofog' + case 'k8s': + case 'external': + return serviceConfig.resource + default: + throw new Errors.ValidationError(`Invalid service type: ${serviceConfig.type}`) + } +} + +// Helper function to determine siteId for connector +async function _determineConnectorSiteId (serviceConfig, transaction) { + switch (serviceConfig.type.toLowerCase()) { + case 'microservice': { + const microservice = await MicroserviceManager.findOne({ uuid: serviceConfig.resource }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(`Microservice not found: ${serviceConfig.resource}`) + } + return microservice.iofogUuid + } + case 'agent': + return serviceConfig.resource + case 'k8s': + case 'external': + return 'default-router' + default: + throw new Errors.ValidationError(`Invalid service type: ${serviceConfig.type}`) + } +} + +// Helper function to determine processId for connector +async function _determineConnectorProcessId (serviceConfig) { + switch (serviceConfig.type.toLowerCase()) { + case 'microservice': + return serviceConfig.resource + case 'agent': + return `${serviceConfig.resource}-local-${serviceConfig.targetPort}` + case 'k8s': + return `${serviceConfig.resource}-k8s-${serviceConfig.targetPort}` + case 'external': + return `${serviceConfig.resource}-external-${serviceConfig.targetPort}` + default: + throw new Errors.ValidationError(`Invalid service type: ${serviceConfig.type}`) + } +} + +// Helper function to build tcpConnector configuration +async function _buildTcpConnector (serviceConfig, transaction) { + const host = await _determineConnectorHost(serviceConfig, transaction) + const siteId = await _determineConnectorSiteId(serviceConfig, transaction) + const processId = await _determineConnectorProcessId(serviceConfig) + + return { + name: `${serviceConfig.name}-connector`, + host, + port: serviceConfig.targetPort.toString(), + address: serviceConfig.name, + siteId, + processId + } +} + +// Helper function to build tcpListener configuration +async function _buildTcpListener (serviceConfig, fogNodeUuid = null) { + const listener = { + name: `${serviceConfig.name}-listener`, + port: serviceConfig.bridgePort.toString(), + address: serviceConfig.name, + siteId: fogNodeUuid || serviceConfig.defaultBridge + } + return listener +} + +// Helper function to get router microservice by fog node UUID +async function _getRouterMicroservice (fogNodeUuid, transaction) { + const routerName = `router-${fogNodeUuid.toLowerCase()}` + const routerMicroservice = await MicroserviceManager.findOne({ name: routerName }, transaction) + if (!routerMicroservice) { + throw new Errors.NotFoundError(`Router microservice not found: ${routerName}`) + } + return routerMicroservice +} + +// Helper function to update router config in Kubernetes environment +async function _updateK8sRouterConfig (config) { + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const patchData = { + data: { + 'skrouterd.json': JSON.stringify(config) + } + } + + await K8sClient.patchConfigMap(K8S_ROUTER_CONFIG_MAP, patchData) +} + +// Helper function to update router microservice config +async function _updateRouterMicroserviceConfig (fogNodeUuid, config, transaction) { + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + + // Update microservice with the provided config + await MicroserviceManager.update( + { uuid: routerMicroservice.uuid }, + { config: JSON.stringify(config) }, + transaction + ) + + // Update change tracking + await ChangeTrackingService.update(fogNodeUuid, ChangeTrackingService.events.microserviceConfig, transaction) +} + +// Helper function to add tcpConnector to router config +async function _addTcpConnector (serviceConfig, transaction) { + const isK8s = await checkKubernetesEnvironment() + const connector = await _buildTcpConnector(serviceConfig, transaction) + const siteId = connector.siteId + + if (siteId === 'default-router') { + if (isK8s) { + // Update K8s router config + logger.debug('Updating K8s router config') + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + logger.error('ConfigMap not found:' + K8S_ROUTER_CONFIG_MAP) + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const routerConfig = JSON.parse(configMap.data['skrouterd.json']) + // Add new connector to the array + routerConfig.push(['tcpConnector', connector]) + + await _updateK8sRouterConfig(routerConfig) + } else { + // Update default router microservice config + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + logger.error('Default router not found') + throw new Errors.NotFoundError('Default router not found') + } + const fogNodeUuid = defaultRouter.iofogUuid + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (!currentConfig.bridges) { + currentConfig.bridges = {} + } + if (!currentConfig.bridges.tcpConnectors) { + currentConfig.bridges.tcpConnectors = {} + } + currentConfig.bridges.tcpConnectors[connector.name] = connector + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } + } else { + // Update specific router microservice config + const fogNodeUuid = siteId + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (!currentConfig.bridges) { + currentConfig.bridges = {} + } + if (!currentConfig.bridges.tcpConnectors) { + currentConfig.bridges.tcpConnectors = {} + } + currentConfig.bridges.tcpConnectors[connector.name] = connector + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } +} + +// Helper function to add tcpListener to router config +async function _addTcpListener (serviceConfig, transaction) { + const isK8s = await checkKubernetesEnvironment() + + // First handle K8s case if we're in K8s environment + if (isK8s) { + const k8sListener = await _buildTcpListener(serviceConfig, null) // null for K8s case + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + logger.error('ConfigMap not found:' + K8S_ROUTER_CONFIG_MAP) + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const routerConfig = JSON.parse(configMap.data['skrouterd.json']) + // Add new listener to the array + routerConfig.push(['tcpListener', k8sListener]) + + await _updateK8sRouterConfig(routerConfig) + } + + // Handle distributed router microservice cases + // Get list of fog nodes that need this listener + const fogNodeUuids = await handleServiceDistribution(serviceConfig.tags, transaction) + + // If not in K8s environment, always include default router + if (!isK8s) { + if (serviceConfig.defaultBridge === 'default-router') { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + logger.error('Default router not found') + throw new Errors.NotFoundError('Default router not found') + } + // Add default router if not already in the list + if (!fogNodeUuids.includes(defaultRouter.iofogUuid)) { + fogNodeUuids.push(defaultRouter.iofogUuid) + } + } else { + if (!fogNodeUuids.includes(serviceConfig.defaultBridge)) { + fogNodeUuids.push(serviceConfig.defaultBridge) + } + } + } + // else if (!fogNodeUuids || fogNodeUuids.length === 0) { + // // If in K8s and no fog nodes found, add default router + // const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + // if (!defaultRouter) { + // logger.error('Default router not found') + // throw new Errors.NotFoundError('Default router not found') + // } + // fogNodeUuids.push(defaultRouter.iofogUuid) + // } + + // Add listener to each router microservice + for (const fogNodeUuid of fogNodeUuids) { + try { + const listener = await _buildTcpListener(serviceConfig, fogNodeUuid) + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + if (!currentConfig.bridges) currentConfig.bridges = {} + if (!currentConfig.bridges.tcpListeners) currentConfig.bridges.tcpListeners = {} + currentConfig.bridges.tcpListeners[listener.name] = listener + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } catch (err) { + if (err instanceof Errors.NotFoundError) { + logger.info(`Router microservice not found for fogNodeUuid ${fogNodeUuid}, skipping.`) + continue + } + throw err + } + } +} + +// Helper function to update tcpConnector in router config +async function _updateTcpConnector (serviceConfig, transaction) { + const isK8s = await checkKubernetesEnvironment() + const connector = await _buildTcpConnector(serviceConfig, transaction) + const siteId = connector.siteId + + if (siteId === 'default-router') { + if (isK8s) { + // Update K8s router config + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const routerConfig = JSON.parse(configMap.data['skrouterd.json']) + // Find and update the existing connector + const connectorIndex = routerConfig.findIndex(item => + item[0] === 'tcpConnector' && item[1].name === connector.name + ) + if (connectorIndex !== -1) { + routerConfig[connectorIndex] = ['tcpConnector', connector] + } + + await _updateK8sRouterConfig(routerConfig) + } else { + // Update default router microservice config + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + throw new Errors.NotFoundError('Default router not found') + } + const fogNodeUuid = defaultRouter.iofogUuid + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (!currentConfig.bridges) { + currentConfig.bridges = {} + } + if (!currentConfig.bridges.tcpConnectors) { + currentConfig.bridges.tcpConnectors = {} + } + // Update the connector with the same name + currentConfig.bridges.tcpConnectors[connector.name] = connector + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } + } else { + // Update specific router microservice config + const fogNodeUuid = siteId + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (!currentConfig.bridges) { + currentConfig.bridges = {} + } + if (!currentConfig.bridges.tcpConnectors) { + currentConfig.bridges.tcpConnectors = {} + } + // Update the connector with the same name + currentConfig.bridges.tcpConnectors[connector.name] = connector + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } +} + +// // Helper function to update tcpListener in router config +// async function _updateTcpListener (serviceConfig, transaction) { +// const isK8s = await checkKubernetesEnvironment() + +// // First handle K8s case if we're in K8s environment +// if (isK8s) { +// const k8sListener = await _buildTcpListener(serviceConfig, null) // null for K8s case +// const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) +// if (!configMap) { +// throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) +// } + +// const routerConfig = JSON.parse(configMap.data['skrouterd.json']) +// // Update the listener in the array +// const listenerIndex = routerConfig.findIndex(item => +// item[0] === 'tcpListener' && item[1].name === k8sListener.name +// ) +// if (listenerIndex !== -1) { +// routerConfig[listenerIndex] = ['tcpListener', k8sListener] +// } else { +// routerConfig.push(['tcpListener', k8sListener]) +// } + +// await _updateK8sRouterConfig(routerConfig) +// } + +// // Handle distributed router microservice cases +// // Get list of fog nodes that need this listener +// const fogNodeUuids = await handleServiceDistribution(serviceConfig.tags, transaction) +// // If not in K8s environment, always include default router +// if (!isK8s) { +// const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) +// if (!defaultRouter) { +// throw new Errors.NotFoundError('Default router not found') +// } +// // Add default router if not already in the list +// if (!fogNodeUuids.includes(defaultRouter.iofogUuid)) { +// fogNodeUuids.push(defaultRouter.iofogUuid) +// } +// } +// // else if (!fogNodeUuids || fogNodeUuids.length === 0) { +// // // If in K8s and no fog nodes found, add default router +// // const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) +// // if (!defaultRouter) { +// // throw new Errors.NotFoundError('Default router not found') +// // } +// // fogNodeUuids.push(defaultRouter.iofogUuid) +// // } + +// // Update listener in each router microservice +// for (const fogNodeUuid of fogNodeUuids) { +// try { +// const listener = await _buildTcpListener(serviceConfig, fogNodeUuid) +// const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) +// const currentConfig = JSON.parse(routerMicroservice.config || '{}') + +// if (!currentConfig.bridges) { +// currentConfig.bridges = {} +// } +// if (!currentConfig.bridges.tcpListeners) { +// currentConfig.bridges.tcpListeners = {} +// } +// // Update listener with its name as key +// currentConfig.bridges.tcpListeners[listener.name] = listener + +// await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) +// } catch (err) { +// if (err instanceof Errors.NotFoundError) { +// logger.info(`Router microservice not found for fogNodeUuid ${fogNodeUuid}, skipping.`) +// continue +// } +// throw err +// } +// } +// } + +// Helper function to delete tcpConnector from router config +async function _deleteTcpConnector (serviceName, transaction) { + const isK8s = await checkKubernetesEnvironment() + const connectorName = `${serviceName}-connector` + + // Get service to determine if it's using default router + const service = await ServiceManager.findOne({ name: serviceName }, transaction) + if (!service) { + throw new Errors.NotFoundError(`Service not found: ${serviceName}`) + } + + const isDefaultRouter = service.defaultBridge === 'default-router' + let microserviceSource = null + if (service.type === 'microservice') { + microserviceSource = await MicroserviceManager.findOne({ uuid: service.resource }, transaction) + } + let fogSource = null + if (service.type === 'agent') { + fogSource = await FogManager.findOne({ uuid: service.resource }, transaction) + if (!fogSource) { + fogSource = await FogManager.findOne({ name: service.resource }, transaction) + } + } + + if (isDefaultRouter && (!microserviceSource || !fogSource)) { + if (isK8s) { + // Update K8s router config + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const routerConfig = JSON.parse(configMap.data['skrouterd.json']) + // Remove the connector from the array + const updatedConfig = routerConfig.filter(item => + !(item[0] === 'tcpConnector' && item[1].name === connectorName) + ) + + await _updateK8sRouterConfig(updatedConfig) + } else { + // Update default router microservice config + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + throw new Errors.NotFoundError('Default router not found') + } + const fogNodeUuid = defaultRouter.iofogUuid + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (currentConfig.bridges && currentConfig.bridges.tcpConnectors) { + delete currentConfig.bridges.tcpConnectors[connectorName] + } + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } + } + + let fogNodeUuid = null + if (!isDefaultRouter && (!microserviceSource || !fogSource)) { + fogNodeUuid = service.defaultBridge + } + if (microserviceSource) { + fogNodeUuid = microserviceSource.iofogUuid + } + if (fogSource) { + fogNodeUuid = fogSource.uuid + } + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + + if (currentConfig.bridges && currentConfig.bridges.tcpConnectors) { + delete currentConfig.bridges.tcpConnectors[connectorName] + } + + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) +} + +// Helper function to delete tcpListener from router config +async function _deleteTcpListener (serviceName, transaction) { + const isK8s = await checkKubernetesEnvironment() + const listenerName = `${serviceName}-listener` + + // First handle K8s case if we're in K8s environment + if (isK8s) { + const configMap = await K8sClient.getConfigMap(K8S_ROUTER_CONFIG_MAP) + if (!configMap) { + throw new Errors.NotFoundError(`ConfigMap not found: ${K8S_ROUTER_CONFIG_MAP}`) + } + + const routerConfig = JSON.parse(configMap.data['skrouterd.json']) + // Remove the listener from the array + const updatedConfig = routerConfig.filter(item => + !(item[0] === 'tcpListener' && item[1].name === listenerName) + ) + + await _updateK8sRouterConfig(updatedConfig) + } + + // Get service to determine its tags for distribution + const service = await ServiceManager.findOneWithTags({ name: serviceName }, transaction) + if (!service) { + throw new Errors.NotFoundError(`Service not found: ${serviceName}`) + } + + let microserviceSource = null + if (service.type === 'microservice') { + microserviceSource = await MicroserviceManager.findOne({ uuid: service.resource }, transaction) + } + // Handle distributed router microservice cases + // Get list of fog nodes that need this listener removed + const serviceTags = service.tags.map(tag => tag.value) + const fogNodeUuids = await handleServiceDistribution(serviceTags, transaction) + + if (microserviceSource) { + if (!fogNodeUuids.includes(microserviceSource.iofogUuid)) { + fogNodeUuids.push(microserviceSource.iofogUuid) + } + } + // If not in K8s environment, always include default router + if (!isK8s) { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + throw new Errors.NotFoundError('Default router not found') + } + // Add default router if not already in the list + if (!fogNodeUuids.includes(defaultRouter.iofogUuid)) { + fogNodeUuids.push(defaultRouter.iofogUuid) + } + } + // else if (!fogNodeUuids || fogNodeUuids.length === 0) { + // // If in K8s and no fog nodes found, add default router + // const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + // if (!defaultRouter) { + // throw new Errors.NotFoundError('Default router not found') + // } + // fogNodeUuids.push(defaultRouter.iofogUuid) + // } + + // Remove listener from each router microservice + for (const fogNodeUuid of fogNodeUuids) { + try { + const routerMicroservice = await _getRouterMicroservice(fogNodeUuid, transaction) + const currentConfig = JSON.parse(routerMicroservice.config || '{}') + if (currentConfig.bridges && currentConfig.bridges.tcpListeners) { + delete currentConfig.bridges.tcpListeners[listenerName] + } + await _updateRouterMicroserviceConfig(fogNodeUuid, currentConfig, transaction) + } catch (err) { + if (err instanceof Errors.NotFoundError) { + logger.info(`Router microservice not found for fogNodeUuid ${fogNodeUuid}, skipping.`) + continue + } + throw err + } + } +} + +// Helper function to create Kubernetes service +async function _createK8sService (serviceConfig, transaction) { + const normalizedTags = serviceConfig.tags.map(tag => tag.includes(':') ? tag : `${tag}:`) + const serviceSpec = { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: serviceConfig.name, + annotations: normalizedTags.reduce((acc, tag) => { + const [key, value] = tag.split(':') + acc[key] = (value || '').trim() + return acc + }, {}) + }, + spec: { + type: serviceConfig.k8sType, + selector: { + 'datasance.com/component': 'router' + }, + ports: [{ + name: 'pot-service', + targetPort: parseInt(serviceConfig.bridgePort), + port: parseInt(serviceConfig.servicePort), + protocol: 'TCP' + }] + } + } + + const service = await K8sClient.createService(serviceSpec) + + // If LoadBalancer type, wait for and set the external IP + if (serviceConfig.k8sType === 'LoadBalancer') { + const loadBalancerIP = await K8sClient.watchLoadBalancerIP(serviceConfig.name) + if (loadBalancerIP) { + await ServiceManager.update( + { name: serviceConfig.name }, + { serviceEndpoint: loadBalancerIP }, + transaction + ) + } + } + + return service +} + +// Helper function to update Kubernetes service +async function _updateK8sService (serviceConfig, transaction) { + const existingService = await K8sClient.getService(serviceConfig.name) + if (!existingService) { + logger.debug(`Service not found: ${serviceConfig.name}, creating new service`) + const service = await _createK8sService(serviceConfig, transaction) + return service + } else { + const normalizedTags = serviceConfig.tags.map(tag => tag.includes(':') ? tag : `${tag}:`) + const patchData = { + metadata: { + annotations: normalizedTags.reduce((acc, tag) => { + const [key, value] = tag.split(':') + acc[key] = (value || '').trim() + return acc + }, {}) + }, + spec: { + type: serviceConfig.k8sType, + selector: { + 'datasance.com/component': 'router' + }, + ports: [{ + name: 'pot-service', + port: parseInt(serviceConfig.servicePort), + targetPort: parseInt(serviceConfig.bridgePort), + protocol: 'TCP' + }] + } + } + + logger.debug(`Updating service: ${serviceConfig.name}`) + const updatedService = await K8sClient.updateService(serviceConfig.name, patchData) + + // If LoadBalancer type, wait for and set the external IP + if (serviceConfig.k8sType === 'LoadBalancer') { + const loadBalancerIP = await K8sClient.watchLoadBalancerIP(serviceConfig.name) + if (loadBalancerIP) { + await ServiceManager.update( + { name: serviceConfig.name }, + { serviceEndpoint: loadBalancerIP }, + transaction + ) + } + } + return updatedService + } +} + +// Helper function to delete Kubernetes service +async function _deleteK8sService (serviceName) { + try { + await K8sClient.deleteService(serviceName) + } catch (error) { + // If it's a 404 (Not Found), log a warning and continue + if (error.response && error.response.status === 404) { + logger.warn(`K8s service ${serviceName} not found during delete. It may have already been deleted.`) + } else { + // For other errors, you may want to log and rethrow, or just log as warning + logger.warn(`Failed to delete K8s service ${serviceName}: ${error.message}`) + } + // Do not throw, so the flow continues + } +} + +// Create service endpoint +async function createServiceEndpoint (serviceData, transaction) { + logger.debug('Creating service with data:' + JSON.stringify(serviceData)) + + // 1. Validate from schemas validator + await Validator.validate(serviceData, Validator.schemas.serviceCreate) + await _validateServiceName(serviceData) + + // 2. Check K8s environment if type is k8s + const isK8s = await checkKubernetesEnvironment() + if (serviceData.type === 'k8s' && !isK8s) { + throw new Errors.ValidationError('Kubernetes environment is required for k8s service type') + } + + if (serviceData.type !== 'k8s' && isK8s) { + logger.debug('Validating non k8s service type') + await validateNonK8sType(serviceData) + } + + // 3. Validate microservice type + if (serviceData.type === 'microservice') { + await validateMicroserviceType(serviceData, transaction) + } + + // 4. Validate agent type + if (serviceData.type === 'agent') { + logger.debug('Validating agent service type') + await validateFogServiceType(serviceData, transaction) + } + + // 5. Validate default bridge + logger.debug('Validating default bridge') + await validateDefaultBridge(serviceData, transaction) + + logger.debug('Defining bridge port') + // 6. Define bridge port + await defineBridgePort(serviceData, transaction) + + // Set provisioning fields + serviceData.provisioningStatus = 'pending' + serviceData.provisioningError = null + if (!isK8s) { + if (serviceData.defaultBridge === 'default-router') { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + throw new Errors.NotFoundError('Default router not found') + } + serviceData.serviceEndpoint = defaultRouter.host + serviceData.servicePort = serviceData.bridgePort + } else { + const router = await RouterManager.findOne({ iofogUuid: serviceData.defaultBridge }, transaction) + if (!router) { + throw new Errors.NotFoundError('Router not found') + } + serviceData.serviceEndpoint = router.host + serviceData.servicePort = serviceData.bridgePort + } + } + + // 7. Create service in database first + logger.debug('Creating service in database') + const service = await ServiceManager.create(serviceData, transaction) + + // 8. Start background orchestration + setImmediate(async () => { + try { + // Set tags if provided + logger.debug('Setting tags (background)') + if (serviceData.tags && serviceData.tags.length > 0) { + await _setTags(service, serviceData.tags, transaction) + } + + // Add TCP connector + logger.debug('Adding TCP connector (background)') + await _addTcpConnector(serviceData, transaction) + + // Add TCP listener + logger.debug('Adding TCP listener (background)') + await _addTcpListener(serviceData, transaction) + + // Create K8s service if needed + if ((serviceData.type === 'microservice' || serviceData.type === 'agent' || serviceData.type === 'external') && isK8s) { + logger.debug('Creating K8s service (background)') + await _createK8sService(serviceData, transaction) + } + + // Update provisioning status to ready + await ServiceManager.update({ id: service.id }, { provisioningStatus: 'ready', provisioningError: null }, transaction) + } catch (err) { + logger.error('Background provisioning failed:', err) + // Update provisioning status to failed and set error message + await ServiceManager.update({ id: service.id }, { provisioningStatus: 'failed', provisioningError: err.message }, transaction) + } + }) + + // 9. Return service immediately + return service +} + +// Update service endpoint +async function updateServiceEndpoint (serviceName, serviceData, transaction) { + // 1. Validate from schemas validator + await Validator.validate(serviceData, Validator.schemas.serviceUpdate) + await _validateServiceName(serviceData) + + // 2. Get existing service + const existingService = await ServiceManager.findOneWithTags({ name: serviceName }, transaction) + if (!existingService) { + throw new Errors.NotFoundError(`Service with name ${serviceName} not found`) + } + + // 3. Check if service type is being changed + if (serviceData.type && serviceData.type !== existingService.type) { + throw new Errors.ValidationError('Changing service type is not allowed. Please delete the service and create a new one with the desired type.') + } + + if (serviceData.defaultBridge && serviceData.defaultBridge !== existingService.defaultBridge) { + throw new Errors.ValidationError('Changing default bridge is not allowed. Please delete the service and create a new one with the desired default bridge.') + } + + // 4. Check K8s environment if type is k8s + const isK8s = await checkKubernetesEnvironment() + if (existingService.type === 'k8s' && !isK8s) { + throw new Errors.ValidationError('Kubernetes environment is required for k8s service type') + } + + if (serviceData.type !== 'k8s' && isK8s) { + logger.debug('Validating non k8s service type') + await validateNonK8sType(serviceData) + } + + // 5. Validate microservice type if needed + if (existingService.type === 'microservice') { + await validateMicroserviceType(serviceData, transaction) + } + + // 6. Validate agent type if needed + if (existingService.type === 'agent') { + await validateFogServiceType(serviceData, transaction) + } + + // 7. Validate default bridge if needed + if (serviceData.defaultBridge) { + await validateDefaultBridge(serviceData, transaction) + } + + serviceData.bridgePort = existingService.bridgePort + + // Set provisioning fields + serviceData.provisioningStatus = 'pending' + serviceData.provisioningError = null + + if (!isK8s) { + if (serviceData.defaultBridge === 'default-router') { + const defaultRouter = await RouterManager.findOne({ isDefault: true }, transaction) + if (!defaultRouter) { + throw new Errors.NotFoundError('Default router not found') + } + serviceData.serviceEndpoint = defaultRouter.host + serviceData.servicePort = serviceData.bridgePort + } else { + const router = await RouterManager.findOne({ iofogUuid: serviceData.defaultBridge }, transaction) + if (!router) { + throw new Errors.NotFoundError('Router not found') + } + serviceData.serviceEndpoint = router.host + serviceData.servicePort = serviceData.bridgePort + } + } + + // 8. Update service in database + const updatedService = await ServiceManager.update( + { name: serviceName }, + serviceData, + transaction + ) + + // 9. Start background orchestration + setImmediate(async () => { + try { + // Update tags if provided + if (serviceData.tags) { + await _setTags(existingService, serviceData.tags, transaction) + } + + // Handle resource changes + if (serviceData.resource && + JSON.stringify(serviceData.resource) !== JSON.stringify(existingService.resource)) { + await _deleteTcpConnector(serviceName, transaction) + await _addTcpConnector(serviceData, transaction) + } else { + await _updateTcpConnector(serviceData, transaction) + // await _updateTcpListener(serviceData, transaction) + } + + // Update K8s service if needed + if ((existingService.type === 'microservice' || existingService.type === 'agent' || existingService.type === 'external') && isK8s) { + await _updateK8sService(serviceData, transaction) + } + + // Update provisioning status to ready + await ServiceManager.update( + { name: serviceName }, + { provisioningStatus: 'ready', provisioningError: null }, + transaction + ) + } catch (err) { + logger.error('Background provisioning failed (update):', err) + // Update provisioning status to failed and set error message + await ServiceManager.update( + { name: serviceName }, + { provisioningStatus: 'failed', provisioningError: err.message }, + transaction + ) + } + }) + + // 10. Return updated service immediately + return updatedService +} + +// Delete service endpoint +async function deleteServiceEndpoint (serviceName, transaction) { + // Get existing service + const existingService = await ServiceManager.findOne({ name: serviceName }, transaction) + if (!existingService) { + throw new Errors.NotFoundError(`Service with name ${serviceName} not found`) + } + + const isK8s = await checkKubernetesEnvironment() + + try { + // Delete TCP connector + await _deleteTcpConnector(serviceName, transaction) + + // Delete TCP listener + await _deleteTcpListener(serviceName, transaction) + + // Delete K8s service if needed + if (isK8s && existingService.type !== 'k8s') { + await _deleteK8sService(serviceName) + } + + // Finally delete the service from database + await ServiceManager.delete({ name: serviceName }, transaction) + + return { message: `Service ${serviceName} deleted successfully` } + } catch (error) { + logger.error('Error deleting service:', { + error: error.message, + stack: error.stack, + serviceName: serviceName, + serviceType: existingService.type + }) + + // Wrap the error in a proper error type if it's not already + if (!(error instanceof Errors.ValidationError) && + !(error instanceof Errors.NotFoundError) && + !(error instanceof Errors.TransactionError) && + !(error instanceof Errors.DuplicatePropertyError)) { + throw new Errors.ValidationError(`Failed to delete service: ${error.message}`) + } + throw error + } +} + +// List services endpoint +async function getServicesListEndpoint (transaction) { + const queryFogData = {} + const services = await ServiceManager.findAllWithTags(queryFogData, transaction) + return services.map(service => ({ + name: service.name, + type: service.type, + resource: service.resource, + defaultBridge: service.defaultBridge, + bridgePort: service.bridgePort, + targetPort: service.targetPort, + servicePort: service.servicePort, + k8sType: service.k8sType, + serviceEndpoint: service.serviceEndpoint, + tags: _mapTags(service), + provisioningStatus: service.provisioningStatus, + provisioningError: service.provisioningError + })) +} + +// Get service endpoint +async function getServiceEndpoint (serviceName, transaction) { + const queryFogData = { name: serviceName } + const service = await ServiceManager.findOneWithTags(queryFogData, transaction) + if (!service) { + throw new Errors.NotFoundError(`Service with name ${serviceName} not found`) + } + return { + name: service.name, + type: service.type, + resource: service.resource, + defaultBridge: service.defaultBridge, + bridgePort: service.bridgePort, + targetPort: service.targetPort, + servicePort: service.servicePort, + k8sType: service.k8sType, + serviceEndpoint: service.serviceEndpoint, + tags: _mapTags(service), + provisioningStatus: service.provisioningStatus, + provisioningError: service.provisioningError + } +} + +async function moveMicroserviceTcpBridgeToNewFog (service, newFogUuid, oldFogUuid, transaction) { + const listenerName = `${service.name}-listener` + const connectorName = `${service.name}-connector` + + const oldRouterMicroservice = await _getRouterMicroservice(oldFogUuid, transaction) + const oldRouterConfig = JSON.parse(oldRouterMicroservice.config || '{}') + const newRouterMicroservice = await _getRouterMicroservice(newFogUuid, transaction) + const newRouterConfig = JSON.parse(newRouterMicroservice.config || '{}') + + const connector = oldRouterConfig.bridges.tcpConnectors[connectorName] + const listener = oldRouterConfig.bridges.tcpListeners[listenerName] + + if (oldRouterConfig.bridges.tcpConnectors[connectorName]) { + delete oldRouterConfig.bridges.tcpConnectors[connectorName] + } + if (oldRouterConfig.bridges.tcpListeners[listenerName]) { + delete oldRouterConfig.bridges.tcpListeners[listenerName] + } + + if (!newRouterConfig.bridges) { + newRouterConfig.bridges = {} + } + if (!newRouterConfig.bridges.tcpConnectors) { + newRouterConfig.bridges.tcpConnectors = {} + } + + newRouterConfig.bridges.tcpConnectors[connectorName] = connector + newRouterConfig.bridges.tcpListeners[listenerName] = listener + + await _updateRouterMicroserviceConfig(oldFogUuid, oldRouterConfig, transaction) + await _updateRouterMicroserviceConfig(newFogUuid, newRouterConfig, transaction) +} + +module.exports = { + checkKubernetesEnvironment, + validateMicroserviceType: TransactionDecorator.generateTransaction(validateMicroserviceType), + validateNonK8sType, + _validateServiceName, + validateFogServiceType: TransactionDecorator.generateTransaction(validateFogServiceType), + validateDefaultBridge: TransactionDecorator.generateTransaction(validateDefaultBridge), + defineBridgePort: TransactionDecorator.generateTransaction(defineBridgePort), + handleServiceDistribution: TransactionDecorator.generateTransaction(handleServiceDistribution), + _mapTags, + _setTags: TransactionDecorator.generateTransaction(_setTags), + _createK8sService, + _updateK8sService, + _deleteK8sService, + createServiceEndpoint: TransactionDecorator.generateTransaction(createServiceEndpoint), + updateServiceEndpoint: TransactionDecorator.generateTransaction(updateServiceEndpoint), + deleteServiceEndpoint: TransactionDecorator.generateTransaction(deleteServiceEndpoint), + getServicesListEndpoint: TransactionDecorator.generateTransaction(getServicesListEndpoint), + getServiceEndpoint: TransactionDecorator.generateTransaction(getServiceEndpoint), + moveMicroserviceTcpBridgeToNewFog: TransactionDecorator.generateTransaction(moveMicroserviceTcpBridgeToNewFog) +} diff --git a/src/services/tunnel-service.js b/src/services/tunnel-service.js index 29ce0d6a2..7c013b26f 100644 --- a/src/services/tunnel-service.js +++ b/src/services/tunnel-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -21,7 +21,7 @@ const ErrorMessages = require('../helpers/error-messages') const TransactionDecorator = require('../decorators/transaction-decorator') const ChangeTrackingService = require('./change-tracking-service') -const openTunnel = async function (tunnelData, user, isCli, transaction) { +const openTunnel = async function (tunnelData, isCli, transaction) { const iofog = await FogManager.findOne({ uuid: tunnelData.iofogUuid }, transaction) if (!iofog) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, tunnelData.iofogUuid)) @@ -30,13 +30,13 @@ const openTunnel = async function (tunnelData, user, isCli, transaction) { if (isCli) { tunnel.rport = await AppHelper.findAvailablePort(tunnelData.host) } else { - const host = Config.get('Tunnel:Host') + const host = Config.get('tunnel.host') tunnel = { - username: Config.get('Tunnel:Username'), - password: Config.get('Tunnel:Password'), + username: Config.get('tunnel.username'), + password: Config.get('tunnel.password'), host: host, - rsakey: Config.get('Tunnel:RsaKey'), - lport: Config.get('Tunnel:Lport'), + rsakey: Config.get('tunnel.rsaKey'), + lport: Config.get('tunnel.lport'), iofogUuid: iofog.uuid, closed: false, rport: await AppHelper.findAvailablePort(host) @@ -47,7 +47,7 @@ const openTunnel = async function (tunnelData, user, isCli, transaction) { await ChangeTrackingService.update(tunnelData.iofogUuid, ChangeTrackingService.events.tunnel, transaction) } -const findTunnel = async function (tunnelData, user, transaction) { +const findTunnel = async function (tunnelData, transaction) { const tunnel = await TunnelManager.findOne(tunnelData, transaction) if (!tunnel) { throw new Errors.NotFoundError('Invalid Tunnel Id') @@ -68,8 +68,8 @@ const findAll = async function (transaction) { } } -const closeTunnel = async function (tunnelData, user, transaction) { - await module.exports.findTunnel(tunnelData, user, transaction) +const closeTunnel = async function (tunnelData, transaction) { + await module.exports.findTunnel(tunnelData, transaction) await TunnelManager.update(tunnelData, { closed: true }, transaction) await ChangeTrackingService.update(tunnelData.iofogUuid, ChangeTrackingService.events.tunnel, transaction) } diff --git a/src/services/user-service.js b/src/services/user-service.js index 12bb45b80..639b1feff 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. + * * Copyright (c) 2023 Datasance Teknoloji A.S. * * * * This program and the accompanying materials are made available under the * * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,383 +11,227 @@ * */ -const nodemailer = require('nodemailer') -const smtpTransport = require('nodemailer-smtp-transport') -const UserManager = require('../data/managers/user-manager') -const AppHelper = require('../helpers/app-helper') const Errors = require('../helpers/errors') -const ErrorMessages = require('../helpers/error-messages') -const Config = require('../config') -const ioFogManager = require('../data/managers/iofog-manager') -const FogStates = require('../enums/fog-state') -const emailActivationTemplate = require('../views/email-activation-temp') -const emailRecoveryTemplate = require('../views/email-temp') -const emailResetTemplate = require('../views/reset-password-temp') -const EmailActivationCodeService = require('./email-activation-code-service') -const AccessTokenService = require('./access-token-service') - const TransactionDecorator = require('../decorators/transaction-decorator') -const Validator = require('../schemas') - -const signUp = async function (user, isCLI, transaction) { - const isEmailActivationEnabled = Config.get('Email:ActivationEnabled') - - if (isEmailActivationEnabled) { - const newUser = await _handleCreateUser(user, isEmailActivationEnabled, transaction) +const axios = require('axios') +const qs = require('qs') +const https = require('https') +const config = require('../config') - const activationCodeData = await EmailActivationCodeService.generateActivationCode(transaction) - await EmailActivationCodeService.saveActivationCode(newUser.id, activationCodeData, transaction) +const kcClient = process.env.KC_CLIENT || config.get('auth.client.id') +const kcClientSecret = process.env.KC_CLIENT_SECRET || config.get('auth.client.secret') +const kcUrl = process.env.KC_URL || config.get('auth.url') +const kcRealm = process.env.KC_REALM || config.get('auth.realm') +const isDevMode = config.get('server.devMode', true) - const emailData = await _getEmailData() - const transporter = await _userEmailSender(emailData) - await _notifyUserAboutActivationCode(user.email, Config.get('Email:HomeUrl'), emailData, activationCodeData, transporter) - return newUser - } else { - return _handleCreateUser(user, isEmailActivationEnabled, transaction) +const mockUser = { + preferred_username: 'dev-user', + email: 'dev@example.com', + realm_access: { + roles: ['SRE', 'Developer', 'Viewer'] } } -const login = async function (credentials, isCLI, transaction) { - const user = await UserManager.findOne({ - email: credentials.email - }, transaction) - if (!user) { - throw new Errors.InvalidCredentialsError() - } - - const pass = AppHelper.decryptText(user.password, user.email) - if (isCLI) { - credentials.password = AppHelper.decryptText(credentials.password, credentials.email) - } - - const validPassword = credentials.password === pass || credentials.password === user.tempPassword - if (!validPassword) { - throw new Errors.InvalidCredentialsError() - } - - _verifyEmailActivation(user.emailActivated) - - const accessToken = await _generateAccessToken(transaction) - accessToken.userId = user.id - - await AccessTokenService.createAccessToken(accessToken, transaction) - - return { - accessToken: accessToken.token - } +const mockToken = { + access_token: 'mock-access-token', + refresh_token: 'mock-refresh-token' } -const resendActivation = async function (emailObj, isCLI, transaction) { - await Validator.validate(emailObj, Validator.schemas.resendActivation) - - const user = await UserManager.findOne({ - email: emailObj.email - }, transaction) - if (!user) { - throw new Errors.ValidationError(ErrorMessages.INVALID_USER_EMAIL) - } - - const activationCodeData = await EmailActivationCodeService.generateActivationCode(transaction) - await EmailActivationCodeService.saveActivationCode(user.id, activationCodeData, transaction) - - const emailData = await _getEmailData() - const transporter = await _userEmailSender(emailData) - await _notifyUserAboutActivationCode(user.email, Config.get('Email:HomeUrl'), emailData, activationCodeData, transporter) +const isAuthConfigured = () => { + return kcUrl && kcRealm && kcClient && kcClientSecret } -const activateUser = async function (codeData, isCLI, transaction) { - const updatedObj = { - emailActivated: true - } - - if (isCLI) { - const user = await UserManager.findOne({ - id: codeData.userId - }, transaction) - - if (user.emailActivated === true) { - throw new Error(ErrorMessages.USER_ALREADY_ACTIVATED) - } - - await _updateUser(codeData.userId, updatedObj, transaction) - } else { - await Validator.validate(codeData, Validator.schemas.activateUser) - - const activationCode = await EmailActivationCodeService.verifyActivationCode(codeData.activationCode, transaction) - if (!activationCode) { - throw new Errors.NotFoundError(ErrorMessages.ACTIVATION_CODE_NOT_FOUND) +const login = async function (credentials, isCLI, transaction) { + // If in dev mode and auth is not configured, always return mock token + if (!isAuthConfigured() && isDevMode) { + return { + accessToken: mockToken.access_token, + refreshToken: mockToken.refresh_token } - - await _updateUser(activationCode.userId, updatedObj, transaction) - - await EmailActivationCodeService.deleteActivationCode(codeData.activationCode, transaction) } -} -const logout = async function (user, isCLI, transaction) { - return AccessTokenService.removeAccessTokenByUserId(user.id, transaction) -} - -const updateUserDetails = async function (user, profileData, isCLI, transaction) { - if (isCLI) { - await Validator.validate(profileData, Validator.schemas.updateUserProfileCLI) - } else { - await Validator.validate(profileData, Validator.schemas.updateUserProfile) + // If auth is not configured and not in dev mode, throw error + if (!isAuthConfigured() && !isDevMode) { + throw new Error(`Auth is not configured for this cluster. Please contact your administrator.`) } - const password = (profileData.password) ? AppHelper.encryptText(profileData.password, user.email) : undefined + // Only proceed with axios request if auth is configured + const data = qs.stringify({ + grant_type: 'password', + username: credentials.email, + password: credentials.password, + totp: credentials.totp, + client_id: kcClient, + client_secret: kcClientSecret + }) - let updateObject = isCLI - ? { - firstName: profileData.firstName, - lastName: profileData.lastName, - password: password - } - : { - firstName: profileData.firstName, - lastName: profileData.lastName - } - - updateObject = AppHelper.deleteUndefinedFields(updateObject) - - await UserManager.updateDetails(user, updateObject, transaction) + const agent = new https.Agent({ + rejectUnauthorized: false + }) - return { - firstName: updateObject.firstName, - lastName: updateObject.lastName, - email: user.email + const requestConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${kcUrl}realms/${kcRealm}/protocol/openid-connect/token`, + headers: { + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data, + httpsAgent: agent } -} - -const deleteUser = async function (force, user, isCLI, transaction) { - if (!force) { - const ioFogArray = await ioFogManager.findAll({ - userId: user.id - }, transaction) - if (ioFogArray) { - for (const ioFog of ioFogArray) { - if (ioFog.daemonStatus === FogStates.RUNNING) { - throw new Errors.ValidationError(ErrorMessages.NEEDED_FORCE_DELETE_USER) - } - } + try { + const response = await axios.request(requestConfig) + const accessToken = response.data.access_token + const refreshToken = response.data.refresh_token + return { + accessToken, + refreshToken + } + } catch (error) { + if (error.response && error.response.data) { + throw new Errors.InvalidCredentialsError(error.response.data.error_description || 'Invalid credentials') } + throw new Errors.InvalidCredentialsError(error.message || 'Invalid credentials') } - - await UserManager.delete({ - id: user.id - }, transaction) } -const updateUserPassword = async function (passwordUpdates, user, isCLI, transaction) { - const pass = AppHelper.decryptText(user.password, user.email) - - if (pass !== passwordUpdates.oldPassword && user.tempPassword !== passwordUpdates.oldPassword) { - throw new Errors.ValidationError(ErrorMessages.INVALID_OLD_PASSWORD) - } - const newPass = AppHelper.encryptText(passwordUpdates.newPassword, user.email) - - await UserManager.updatePassword(user.id, newPass, transaction) - await AccessTokenService.removeAccessTokenByUserId(user.id, transaction) - - try { - const emailData = await _getEmailData() - const transporter = await _userEmailSender(emailData) - - await _notifyUserAboutPasswordChange(user, emailData, transporter) - } catch (e) { - console.error(e) +const refresh = async function (credentials, isCLI, transaction) { + // If in dev mode and auth is not configured, always return mock token + if (!isAuthConfigured() && isDevMode) { + return { + accessToken: mockToken.access_token, + refreshToken: mockToken.refresh_token + } } -} - -const resetUserPassword = async function (emailObj, isCLI, transaction) { - await Validator.validate(emailObj, Validator.schemas.resetUserPassword) - const user = await UserManager.findOne({ - email: emailObj.email - }, transaction) - if (!user) { - throw new Errors.NotFoundError(ErrorMessages.ACCOUNT_NOT_FOUND) + // If auth is not configured and not in dev mode, throw error + if (!isAuthConfigured() && !isDevMode) { + throw new Error(`Auth is not configured for this cluster. Please contact your administrator.`) } - const tempPass = AppHelper.generateRandomString(2) + 'uL7' - const tempDbPass = AppHelper.encryptText(tempPass, user.email) - await UserManager.updateTempPassword(user.id, tempDbPass, transaction) + // Only proceed with axios request if auth is configured + const data = qs.stringify({ + grant_type: 'refresh_token', + refresh_token: credentials.refreshToken, + client_id: kcClient, + client_secret: kcClientSecret + }) - const emailData = await _getEmailData() - const transporter = await _userEmailSender(emailData) - await _notifyUserAboutPasswordReset(user, Config.get('Email:HomeUrl'), emailData, tempPass, transporter) -} + const agent = new https.Agent({ + rejectUnauthorized: false + }) -const list = async function (isCLI, transaction) { - return UserManager.findAllWithAttributes({}, { exclude: ['password'] }, transaction) -} - -const suspendUser = async function (user, isCLI, transaction) { - if (user.emailActivated === false) { - throw new Error(ErrorMessages.USER_NOT_ACTIVATED_YET) - } - - const updatedObj = { - emailActivated: false + const requestConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${kcUrl}realms/${kcRealm}/protocol/openid-connect/token`, + headers: { + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data, + httpsAgent: agent } - await AccessTokenService.removeAccessTokenByUserId(user.id, transaction) - - return _updateUser(user.id, updatedObj, transaction) -} - -async function _updateUser (userId, updatedUser, transaction) { try { - return UserManager.update({ - id: userId - }, updatedUser, transaction) - } catch (errMsg) { - throw new Error(ErrorMessages.USER_NOT_UPDATED) - } -} - -async function _generateAccessToken (transaction) { - while (true) { - const newAccessToken = AppHelper.generateAccessToken() - const exists = await UserManager.findByAccessToken(newAccessToken, transaction) - if (!exists) { - const tokenExpiryTime = new Date().getTime() + (Config.get('Settings:UserTokenExpirationIntervalSeconds') * 1000) - - return { - token: newAccessToken, - expirationTime: tokenExpiryTime - } + const response = await axios.request(requestConfig) + const accessToken = response.data.access_token + const refreshToken = response.data.refresh_token + return { + accessToken, + refreshToken + } + } catch (error) { + if (error.response && error.response.data) { + throw new Errors.InvalidCredentialsError(error.response.data.error_description || 'Invalid credentials') } + throw new Errors.InvalidCredentialsError(error.message || 'Invalid credentials') } } -function _verifyEmailActivation (emailActivated) { - const isEmailActivationEnabled = Config.get('Email:ActivationEnabled') - if (isEmailActivationEnabled && !emailActivated) { - throw new Error(ErrorMessages.EMAIL_NOT_ACTIVATED) +const profile = async function (req, isCLI, transaction) { + // If in dev mode and auth is not configured, always return mock user + if (!isAuthConfigured() && isDevMode) { + return mockUser } -} -async function _userEmailSender (emailData) { - let transporter - if (emailData.service) { - transporter = nodemailer.createTransport(smtpTransport({ - service: emailData.service, - auth: { - user: emailData.email, - pass: emailData.password - } - })) - } else { - transporter = nodemailer.createTransport(smtpTransport({ - host: emailData.host, - port: emailData.port, - auth: { - user: emailData.email, - pass: emailData.password - } - })) + // If auth is not configured and not in dev mode, throw error + if (!isAuthConfigured() && !isDevMode) { + throw new Error(`Auth is not configured for this cluster. Please contact your administrator.`) } - return transporter -} - -async function _handleCreateUser (user, isEmailActivationEnabled, transaction) { - const existingUser = await UserManager.findOne({ - email: user.email - }, transaction) + // Only proceed with axios request if auth is configured + const accessToken = req.headers.authorization.replace('Bearer ', '') + const agent = new https.Agent({ + rejectUnauthorized: false + }) - if (existingUser) { - throw new Errors.ValidationError('Registration failed: There is already an account associated with your email address. ' + - 'Please try logging in instead.') + const requestConfig = { + method: 'get', + maxBodyLength: Infinity, + url: `${kcUrl}realms/${kcRealm}/protocol/openid-connect/userinfo`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Bearer ${accessToken}` + }, + httpsAgent: agent } - const newUser = await _createNewUser(user, isEmailActivationEnabled, transaction) - return { - userId: newUser.id, - firstName: newUser.firstName, - lastName: newUser.lastName, - email: newUser.email, - emailActivated: user.emailActivated - } -} - -async function _createNewUser (user, isEmailActivationEnabled, transaction) { - user.emailActivated = !isEmailActivationEnabled - return UserManager.create(user, transaction) -} - -async function _notifyUserAboutActivationCode (email, url, emailSenderData, activationCodeData, transporter) { - const mailOptions = { - from: '"IOFOG" <' + emailSenderData.email + '>', - to: email, - subject: 'Activate Your Account', - html: emailActivationTemplate.p1 + url + emailActivationTemplate.p2 + activationCodeData.activationCode + - emailActivationTemplate.p3 + url + emailActivationTemplate.p4 + activationCodeData.activationCode + - emailActivationTemplate.p5 + url + emailActivationTemplate.p6 + activationCodeData.activationCode + emailActivationTemplate.p7 + try { + const response = await axios.request(requestConfig) + return response.data + } catch (error) { + if (error.response && error.response.data) { + throw new Errors.InvalidCredentialsError(error.response.data.error_description || 'Invalid credentials') + } + throw new Errors.InvalidCredentialsError(error.message || 'Invalid credentials') } - - await _sendEmail(transporter, mailOptions) } -async function _notifyUserAboutPasswordChange (user, emailSenderData, transporter) { - const mailOptions = { - from: '"IOFOG" <' + emailSenderData.email + '>', - to: user.email, - subject: 'Password Change Notification', - html: emailRecoveryTemplate.p1 + user.firstName + ' ' + user.lastName + emailRecoveryTemplate.p2 +const logout = async function (req, isCLI, transaction) { + // If in dev mode and auth is not configured, always return success + if (!isAuthConfigured() && isDevMode) { + return { status: 'success' } } - await _sendEmail(transporter, mailOptions) -} - -async function _notifyUserAboutPasswordReset (user, url, emailSenderData, tempPass, transporter) { - const mailOptions = { - from: '"IOFOG" <' + emailSenderData.email + '>', - to: user.email, - subject: 'Password Reset Request', - html: emailResetTemplate.p1 + user.firstName + ' ' + user.lastName + emailResetTemplate.p2 + tempPass + emailResetTemplate.p3 + - url + emailResetTemplate.p4 + // If auth is not configured and not in dev mode, throw error + if (!isAuthConfigured() && !isDevMode) { + throw new Error(`Auth is not configured for this cluster. Please contact your administrator.`) } - await _sendEmail(transporter, mailOptions) -} + // Only proceed with axios request if auth is configured + const accessToken = req.headers.authorization.replace('Bearer ', '') + const agent = new https.Agent({ + rejectUnauthorized: false + }) -async function _sendEmail (transporter, mailOptions) { - try { - await transporter.sendMail(mailOptions) - } catch (errMsg) { - throw new Error(ErrorMessages.EMAIL_SENDER_NOT_CONFIGURED) + const requestConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${kcUrl}realms/${kcRealm}/protocol/openid-connect/logout`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Bearer ${accessToken}` + }, + httpsAgent: agent } -} -async function _getEmailData () { try { - const email = Config.get('Email:Address') - const password = AppHelper.decryptText(Config.get('Email:Password'), Config.get('Email:Address')) - const service = Config.get('Email:Service') - - return { - email: email, - password: password, - service: service + const response = await axios.request(requestConfig) + return response.data + } catch (error) { + if (error.response && error.response.data) { + throw new Errors.InvalidCredentialsError(error.response.data.error_description || 'Invalid credentials') } - } catch (errMsg) { - throw new Errors.EmailActivationSetupError() + throw new Errors.InvalidCredentialsError(error.message || 'Invalid credentials') } } module.exports = { - signUp: TransactionDecorator.generateTransaction(signUp), login: TransactionDecorator.generateTransaction(login), - resendActivation: TransactionDecorator.generateTransaction(resendActivation), - activateUser: TransactionDecorator.generateTransaction(activateUser), - logout: TransactionDecorator.generateTransaction(logout), - updateUserDetails: TransactionDecorator.generateTransaction(updateUserDetails), - deleteUser: TransactionDecorator.generateTransaction(deleteUser), - updateUserPassword: TransactionDecorator.generateTransaction(updateUserPassword), - resetUserPassword: TransactionDecorator.generateTransaction(resetUserPassword), - list: TransactionDecorator.generateTransaction(list), - suspendUser: TransactionDecorator.generateTransaction(suspendUser) + refresh: TransactionDecorator.generateTransaction(refresh), + profile: TransactionDecorator.generateTransaction(profile), + logout: TransactionDecorator.generateTransaction(logout) } diff --git a/src/services/volume-mount-service.js b/src/services/volume-mount-service.js new file mode 100644 index 000000000..21193e231 --- /dev/null +++ b/src/services/volume-mount-service.js @@ -0,0 +1,204 @@ +const Errors = require('../helpers/errors') +const ErrorMessages = require('../helpers/error-messages') +const AppHelper = require('../helpers/app-helper') +const VolumeMountingManager = require('../data/managers/volume-mounting-manager') +const SecretManager = require('../data/managers/secret-manager') +const ConfigMapManager = require('../data/managers/config-map-manager') +const ChangeTrackingService = require('./change-tracking-service') +const FogManager = require('../data/managers/iofog-manager') +const TransactionDecorator = require('../decorators/transaction-decorator') +const Validator = require('../schemas') + +async function findVolumeMountedFogNodes (volumeMountName, transaction) { + const volumeMount = await VolumeMountingManager.findOne({ + name: volumeMountName + }, transaction) + + if (!volumeMount) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.VOLUME_MOUNT_NOT_FOUND, volumeMountName)) + } + + const fogs = await volumeMount.getFogs({}, transaction) + return fogs.map(fog => fog.uuid) +} + +async function _updateChangeTrackingForFogs (fogUuids, transaction) { + for (const fogUuid of fogUuids) { + await ChangeTrackingService.update(fogUuid, ChangeTrackingService.events.volumeMounts, transaction) + } +} + +async function listVolumeMountsEndpoint (transaction) { + return VolumeMountingManager.findAll({}, transaction) +} + +async function getVolumeMountEndpoint (name, transaction) { + const volumeMount = await VolumeMountingManager.findOne({ + name: name + }, transaction) + + if (!volumeMount) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.VOLUME_MOUNT_NOT_FOUND, name)) + } + + return volumeMount +} + +async function createVolumeMountEndpoint (data, transaction) { + await Validator.validate(data, Validator.schemas.volumeMountCreate) + // Validate that either secretName or configMapName is provided + if (!data.secretName && !data.configMapName) { + throw new Errors.ValidationError('Must specify either secretName or configMapName') + } + + // Validate that both are not provided + if (data.secretName && data.configMapName) { + throw new Errors.ValidationError('Cannot specify both secretName and configMapName') + } + + const existingVolumeMount = await VolumeMountingManager.findOne({ name: data.name }, transaction) + if (existingVolumeMount) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, data.name)) + } + + // Check if secret/configMap exists + if (data.secretName) { + const secret = await SecretManager.getSecret(data.secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, data.secretName)) + } + } + + if (data.configMapName) { + const configMap = await ConfigMapManager.getConfigMap(data.configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, data.configMapName)) + } + } + const volumeMountObj = { + uuid: AppHelper.generateUUID(), + version: 1, + name: data.name, + configMapName: data.configMapName, + secretName: data.secretName + } + return VolumeMountingManager.create(volumeMountObj, transaction) +} + +async function updateVolumeMountEndpoint (name, data, transaction) { + await Validator.validate(data, Validator.schemas.volumeMountUpdate) + const volumeMount = await getVolumeMountEndpoint(name, transaction) + const existingVersion = volumeMount.version + + // Validate that either secretName or configMapName is provided + if (!data.secretName && !data.configMapName) { + throw new Errors.ValidationError('Must specify either secretName or configMapName') + } + + // Validate that both are not provided + if (data.secretName && data.configMapName) { + throw new Errors.ValidationError('Cannot specify both secretName and configMapName') + } + // Check if secret/configMap exists + if (data.secretName) { + const secret = await SecretManager.getSecret(data.secretName, transaction) + if (!secret) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.SECRET_NOT_FOUND, data.secretName)) + } + } + + if (data.configMapName) { + const configMap = await ConfigMapManager.getConfigMap(data.configMapName, transaction) + if (!configMap) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.CONFIGMAP_NOT_FOUND, data.configMapName)) + } + } + + // Get linked fog nodes before update + const linkedFogUuids = await findVolumeMountedFogNodes(name, transaction) + + // Update volume mount + const updatedVolumeMountObj = { + uuid: volumeMount.uuid, + version: existingVersion + 1, + name: volumeMount.name, + configMapName: data.configMapName, + secretName: data.secretName + } + await VolumeMountingManager.update({ name: name }, updatedVolumeMountObj, transaction) + + // Update change tracking for all linked fog nodes + await _updateChangeTrackingForFogs(linkedFogUuids, transaction) + + return getVolumeMountEndpoint(name, transaction) +} + +async function deleteVolumeMountEndpoint (name, transaction) { + // Get linked fog nodes before deletion + const linkedFogUuids = await findVolumeMountedFogNodes(name, transaction) + + // Delete volume mount + await VolumeMountingManager.delete({ name: name }, transaction) + + // Update change tracking for all linked fog nodes + await _updateChangeTrackingForFogs(linkedFogUuids, transaction) + + return {} +} + +async function linkVolumeMountEndpoint (name, fogUuids, transaction) { + await Validator.validate({ fogUuids }, Validator.schemas.volumeMountLink) + + const volumeMount = await getVolumeMountEndpoint(name, transaction) + + for (const fogUuid of fogUuids) { + const agent = await FogManager.findOne({ uuid: fogUuid }, transaction) + if (!agent) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_AGENT_NAME, fogUuid)) + } + await agent.addVolumeMount(volumeMount.uuid, transaction) + } + + // Update change tracking for all linked fog nodes + await _updateChangeTrackingForFogs(fogUuids, transaction) + + return getVolumeMountEndpoint(name, transaction) +} + +async function unlinkVolumeMountEndpoint (name, fogUuids, transaction) { + await Validator.validate({ fogUuids }, Validator.schemas.volumeMountUnlink) + + const volumeMount = await getVolumeMountEndpoint(name, transaction) + + for (const fogUuid of fogUuids) { + const agent = await FogManager.findOne({ uuid: fogUuid }, transaction) + if (!agent) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.NOT_FOUND_AGENT_NAME, fogUuid)) + } + await agent.removeVolumeMount(volumeMount.uuid, transaction) + } + + // Update change tracking for all unlinked fog nodes + await _updateChangeTrackingForFogs(fogUuids, transaction) + + return {} +} + +async function getVolumeMountLinkEndpoint (name, transaction) { + const linkedFogUuids = await findVolumeMountedFogNodes(name, transaction) + return { + fogUuids: linkedFogUuids + } +} + +module.exports = { + listVolumeMountsEndpoint: TransactionDecorator.generateTransaction(listVolumeMountsEndpoint), + getVolumeMountEndpoint: TransactionDecorator.generateTransaction(getVolumeMountEndpoint), + createVolumeMountEndpoint: TransactionDecorator.generateTransaction(createVolumeMountEndpoint), + updateVolumeMountEndpoint: TransactionDecorator.generateTransaction(updateVolumeMountEndpoint), + deleteVolumeMountEndpoint: TransactionDecorator.generateTransaction(deleteVolumeMountEndpoint), + linkVolumeMountEndpoint: TransactionDecorator.generateTransaction(linkVolumeMountEndpoint), + unlinkVolumeMountEndpoint: TransactionDecorator.generateTransaction(unlinkVolumeMountEndpoint), + findVolumeMountedFogNodes: TransactionDecorator.generateTransaction(findVolumeMountedFogNodes), + getVolumeMountLinkEndpoint: TransactionDecorator.generateTransaction(getVolumeMountLinkEndpoint) +} diff --git a/src/services/websocket-queue-service.js b/src/services/websocket-queue-service.js new file mode 100644 index 000000000..8c962a144 --- /dev/null +++ b/src/services/websocket-queue-service.js @@ -0,0 +1,391 @@ +const WebSocket = require('ws') +const logger = require('../logger') +const RouterConnectionService = require('./router-connection-service') + +const MESSAGE_TYPES = { + STDIN: 0, + STDOUT: 1, + STDERR: 2, + CONTROL: 3, + CLOSE: 4, + ACTIVATION: 5 +} + +const MESSAGE_QUEUE_PREFIX = { + agent: 'agent', + user: 'user' +} + +function buildQueueName (prefix, execId) { + return `${prefix}-${execId}` +} + +function getBufferFromBody (body) { + if (!body) return Buffer.alloc(0) + if (Buffer.isBuffer(body)) return body + if (body.type === 'Buffer' && Array.isArray(body.data)) { + return Buffer.from(body.data) + } + if (typeof body === 'string') { + return Buffer.from(body, 'utf8') + } + return Buffer.from(body) +} + +class WebSocketQueueService { + constructor () { + this.execBridges = new Map() + } + + async enableForSession (session, cleanupCallback) { + const execId = session.execId + if (!execId) { + logger.warn('[AMQP][QUEUE] Missing execId for session, skipping queue bridge enablement') + return false + } + + const bridge = this.execBridges.get(execId) || { + execId, + senders: {}, + receivers: {}, + cleanupCallback: null + } + + // Store cleanup callback for CLOSE message handling + if (cleanupCallback) { + bridge.cleanupCallback = cleanupCallback + } + + if (session.user) { + await this._ensureReceiver(bridge, 'user', session.user, session) + } + if (session.agent) { + await this._ensureReceiver(bridge, 'agent', session.agent, session) + } + this.execBridges.set(execId, bridge) + return true + } + + shouldUseQueue (execId) { + return this.execBridges.has(execId) + } + + async publishToAgent (execId, buffer, options = {}) { + await this._send(execId, 'agent', buffer, options) + } + + async publishToUser (execId, buffer, options = {}) { + await this._send(execId, 'user', buffer, options) + } + + async cleanup (execId) { + const bridge = this.execBridges.get(execId) + if (!bridge) return + + const closeLink = (link) => { + if (!link) return + try { + if (link.receiver) { + link.receiver.close() + } else if (link.sender) { + link.sender.close() + } + } catch (error) { + logger.debug('[AMQP][QUEUE] Failed to close link during cleanup', { execId, error: error.message }) + } + } + + closeLink(bridge.receivers.agent) + closeLink(bridge.receivers.user) + closeLink(bridge.senders.agent) + closeLink(bridge.senders.user) + this.execBridges.delete(execId) + } + + detachSocket (execId, side) { + const bridge = this.execBridges.get(execId) + if (!bridge || !bridge.receivers[side]) return + bridge.receivers[side].socket = null + } + + async _send (execId, side, buffer, options = {}) { + const bridge = await this._ensureSender(execId, side) + if (!bridge) { + throw new Error('Queue bridge missing for execId=' + execId) + } + try { + const message = { + body: buffer, + content_type: 'application/octet-stream' + } + + const applicationProperties = { ...((options && options.applicationProperties) || {}) } + const hasMessageType = options && Object.prototype.hasOwnProperty.call(options, 'messageType') + const messageType = hasMessageType ? options.messageType : null + if (messageType !== null) { + applicationProperties.messageType = messageType + } + if (Object.keys(applicationProperties).length > 0) { + message.application_properties = applicationProperties + } + + bridge.sender.send(message) + logger.debug('[AMQP][QUEUE] Published message to queue', { + execId, + side, + messageSize: buffer.length, + messageType: messageType !== null ? messageType : 'normal' + }) + } catch (error) { + logger.error('[AMQP][QUEUE] Failed to publish message', { execId, side, error: error.message }) + throw error + } + } + + async _ensureSender (execId, side) { + const bridge = this.execBridges.get(execId) + if (!bridge) return null + if (bridge.senders[side]) { + return bridge.senders[side] + } + + const queueName = buildQueueName( + side === 'agent' ? MESSAGE_QUEUE_PREFIX.agent : MESSAGE_QUEUE_PREFIX.user, + execId + ) + const connection = await RouterConnectionService.getConnection() + const sender = await new Promise((resolve, reject) => { + const link = connection.open_sender({ + target: { + address: queueName, + durable: 0, + expiry_policy: 'link-detach' + }, + autosettle: true + }) + + link.once('sender_open', () => resolve(link)) + link.once('sender_close', (context) => reject(context.error || new Error('Sender closed before open'))) + link.once('error', reject) + }) + + sender.on('sender_close', () => { + bridge.senders[side] = null + }) + + bridge.senders[side] = { sender } + return bridge.senders[side] + } + + async _ensureReceiver (bridge, side, socket, session) { + if (!socket) return + if (bridge.receivers[side]) { + // Update socket reference if receiver already exists + bridge.receivers[side].socket = socket + logger.debug('[AMQP][QUEUE] Updated socket reference for existing receiver', { + execId: session.execId, + side, + socketState: socket.readyState + }) + return + } + + const queueName = buildQueueName( + side === 'agent' ? MESSAGE_QUEUE_PREFIX.agent : MESSAGE_QUEUE_PREFIX.user, + session.execId + ) + logger.info('[AMQP][QUEUE] Setting up receiver for queue', { + execId: session.execId, + side, + queueName + }) + const connection = await RouterConnectionService.getConnection() + + const receiver = await new Promise((resolve, reject) => { + const link = connection.open_receiver({ + source: { + address: queueName, + durable: 0, + expiry_policy: 'link-detach' + }, + credit_window: 50 + }) + link.once('receiver_open', () => { + logger.info('[AMQP][QUEUE] Receiver opened successfully', { + execId: session.execId, + side, + queueName + }) + resolve(link) + }) + link.once('receiver_close', (context) => reject(context.error || new Error('Receiver closed before open'))) + link.once('error', reject) + }) + + receiver.on('message', async (context) => { + try { + // Always get the latest socket reference from the bridge + const currentBridge = this.execBridges.get(session.execId) + const ws = currentBridge && currentBridge.receivers[side] ? currentBridge.receivers[side].socket : null + const body = getBufferFromBody(context.message.body) + const msgType = context.message.application_properties + ? context.message.application_properties.messageType + : null + + // Handle CLOSE messages (works for both user and agent sides) + if (msgType === MESSAGE_TYPES.CLOSE) { + await this._handleCloseMessage({ + bridge: currentBridge, + session, + side, + ws, + context, + body + }) + return + } + + // Forward message to socket (normal message or non-CLOSE message) + if (ws && ws.readyState === WebSocket.OPEN) { + try { + ws.send(body, { + binary: true, + compress: false, + mask: false, + fin: true + }) + context.delivery.accept() + logger.debug('[AMQP][QUEUE] Delivered message to socket', { + execId: session.execId, + side, + messageSize: body.length + }) + } catch (error) { + logger.error('[AMQP][QUEUE] Failed to deliver message to socket', { + execId: session.execId, + side, + error: error.message + }) + context.delivery.release() + } + } else { + logger.debug('[AMQP][QUEUE] No socket available for message delivery', { + execId: session.execId, + side, + hasSocket: !!ws, + socketState: ws ? ws.readyState : 'N/A', + hasBridge: !!currentBridge, + hasReceiver: currentBridge && !!currentBridge.receivers[side] + }) + context.delivery.release() + } + } catch (error) { + logger.error('[AMQP][QUEUE] Error handling queued message', { + execId: session.execId, + side, + error: error.message + }) + try { + context.delivery.release() + } catch (releaseError) { + logger.warn('[AMQP][QUEUE] Failed to release delivery after error', { + execId: session.execId, + error: releaseError.message + }) + } + } + }) + + receiver.on('receiver_close', () => { + logger.info('[AMQP][QUEUE] Receiver closed', { + execId: session.execId, + side + }) + bridge.receivers[side] = null + }) + + bridge.receivers[side] = { receiver, socket } + logger.info('[AMQP][QUEUE] Receiver setup complete', { + execId: session.execId, + side, + queueName, + socketState: socket.readyState + }) + } + + async _handleCloseMessage ({ bridge, session, side, ws, context, body }) { + const execId = session.execId + const closeInitiator = side === 'user' ? 'agent' : 'user' + const closeAck = Boolean( + context.message.application_properties && + context.message.application_properties.closeAck + ) + logger.info('[AMQP][QUEUE] Received CLOSE message via queue', { + execId, + side, + closeInitiator, + closeAck + }) + + // Attempt to close the socket gracefully (if present) + if (ws && ws.readyState === WebSocket.OPEN) { + try { + const reason = closeInitiator === 'agent' ? 'Agent closed connection' : 'User closed connection' + ws.close(1000, reason) + logger.debug('[AMQP][QUEUE] Closed WebSocket with code 1000 after CLOSE message', { + execId, + side + }) + } catch (error) { + logger.warn('[AMQP][QUEUE] Failed to close WebSocket after CLOSE message', { + execId, + side, + error: error.message + }) + } + } else { + logger.debug('[AMQP][QUEUE] No active socket while handling CLOSE message', { + execId, + side, + hasSocket: !!ws, + socketState: ws ? ws.readyState : 'N/A' + }) + } + + context.delivery.accept() + + if (!closeAck && this.execBridges.has(execId)) { + const ackSide = side === 'user' ? 'agent' : 'user' + try { + await this._send(execId, ackSide, body, { + messageType: MESSAGE_TYPES.CLOSE, + applicationProperties: { closeAck: true } + }) + logger.debug('[AMQP][QUEUE] Sent CLOSE acknowledgement', { + execId, + ackSide + }) + } catch (error) { + logger.warn('[AMQP][QUEUE] Failed to send CLOSE acknowledgement', { + execId, + ackSide, + error: error.message + }) + } + } + + // Invoke cleanup callback to remove session/queue resources + if (bridge && bridge.cleanupCallback) { + try { + await bridge.cleanupCallback(execId) + } catch (error) { + logger.error('[AMQP][QUEUE] Error in cleanup callback during CLOSE handling', { + execId, + error: error.message + }) + } + } + } +} + +module.exports = new WebSocketQueueService() diff --git a/src/services/yaml-parser-service.js b/src/services/yaml-parser-service.js index 5e1dfbf48..084f2f3ed 100644 --- a/src/services/yaml-parser-service.js +++ b/src/services/yaml-parser-service.js @@ -44,6 +44,183 @@ async function parseAppTemplateFile (fileContent) { return appTemplate } +async function parseSecretFile (fileContent, options = {}) { + try { + const doc = yaml.load(fileContent) + if (!doc || !doc.kind) { + throw new Errors.ValidationError(`Invalid YAML format: missing kind field`) + } + if (doc.kind !== 'Secret') { + throw new Errors.ValidationError(`Invalid kind ${doc.kind}`) + } + if (doc.metadata == null || doc.type == null || doc.data == null) { + throw new Errors.ValidationError('Invalid YAML format: missing metadata or spec') + } + + // If this is an update, validate that the name matches + if (options.isUpdate && options.secretName) { + if (doc.metadata.name !== options.secretName) { + throw new Errors.ValidationError(`Secret name in YAML (${doc.metadata.name}) doesn't match endpoint path (${options.secretName})`) + } + + // For updates, we only need the data + return { + data: doc.data + } + } + + // For creates, return full object + return { + name: lget(doc, 'metadata.name', undefined), + type: doc.spec.type, + data: doc.data + } + } catch (error) { + if (error instanceof Errors.ValidationError) { + throw error + } + throw new Errors.ValidationError(`Error parsing YAML: ${error.message}`) + } +} + +async function parseVolumeMountFile (fileContent, options = {}) { + try { + const doc = yaml.load(fileContent) + if (!doc || !doc.kind) { + throw new Errors.ValidationError(`Invalid YAML format: missing kind field`) + } + if (doc.kind !== 'VolumeMount') { + throw new Errors.ValidationError(`Invalid kind ${doc.kind}`) + } + if (doc.metadata == null || doc.spec == null) { + throw new Errors.ValidationError('Invalid YAML format: missing metadata or spec') + } + + // Validate that either secretName or configMapName is provided, but not both + if (doc.spec.secretName && doc.spec.configMapName) { + throw new Errors.ValidationError('Cannot specify both secretName and configMapName') + } + if (!doc.spec.secretName && !doc.spec.configMapName) { + throw new Errors.ValidationError('Must specify either secretName or configMapName') + } + + // If this is an update, validate that the name matches + if (options.isUpdate && options.volumeMountName) { + if (doc.metadata.name !== options.volumeMountName) { + throw new Errors.ValidationError(`VolumeMount name in YAML (${doc.metadata.name}) doesn't match endpoint path (${options.volumeMountName})`) + } + + return { + name: lget(doc, 'metadata.name', undefined), + secretName: doc.spec.secretName, + configMapName: doc.spec.configMapName + } + } + + // For creates, return full object + return { + name: lget(doc, 'metadata.name', undefined), + secretName: doc.spec.secretName, + configMapName: doc.spec.configMapName + } + } catch (error) { + if (error instanceof Errors.ValidationError) { + throw error + } + throw new Errors.ValidationError(`Error parsing YAML: ${error.message}`) + } +} + +async function parseConfigMapFile (fileContent, options = {}) { + try { + const doc = yaml.load(fileContent) + if (!doc || !doc.kind) { + throw new Errors.ValidationError(`Invalid YAML format: missing kind field`) + } + if (doc.kind !== 'ConfigMap') { + throw new Errors.ValidationError(`Invalid kind ${doc.kind}`) + } + if (doc.metadata == null || doc.data == null) { + throw new Errors.ValidationError('Invalid YAML format: missing metadata or spec') + } + + // If this is an update, validate that the name matches + if (options.isUpdate && options.configMapName) { + if (doc.metadata.name !== options.configMapName) { + throw new Errors.ValidationError(`ConfigMap name in YAML (${doc.metadata.name}) doesn't match endpoint path (${options.configMapName})`) + } + + // For updates, we only need the data + return { + data: doc.data + } + } + + // For creates, return full object + return { + name: lget(doc, 'metadata.name', undefined), + data: doc.data, + immutable: doc.spec.immutable + } + } catch (error) { + if (error instanceof Errors.ValidationError) { + throw error + } + throw new Errors.ValidationError(`Error parsing YAML: ${error.message}`) + } +} + +async function parseServiceFile (fileContent, options = {}) { + try { + const doc = yaml.load(fileContent) + if (!doc || !doc.kind) { + throw new Errors.ValidationError(`Invalid YAML format: missing kind field`) + } + if (doc.kind !== 'Service') { + throw new Errors.ValidationError(`Invalid kind ${doc.kind}`) + } + if (doc.metadata == null || doc.spec == null) { + throw new Errors.ValidationError('Invalid YAML format: missing metadata or spec') + } + + // If this is an update, validate that the name matches + if (options.isUpdate && options.serviceName) { + if (doc.metadata.name !== options.serviceName) { + throw new Errors.ValidationError(`Service name in YAML (${doc.metadata.name}) doesn't match endpoint path (${options.serviceName})`) + } + + // For updates, we only need the spec and tags fields + return { + name: lget(doc, 'metadata.name', undefined), + tags: lget(doc, 'metadata.tags', []), + type: doc.spec.type, + resource: doc.spec.resource, + targetPort: doc.spec.targetPort, + defaultBridge: doc.spec.defaultBridge, + servicePort: doc.spec.servicePort, + k8sType: doc.spec.k8sType + } + } + + // For creates, return full object + return { + name: lget(doc, 'metadata.name', undefined), + tags: lget(doc, 'metadata.tags', []), + type: doc.spec.type, + resource: doc.spec.resource, + targetPort: doc.spec.targetPort, + defaultBridge: doc.spec.defaultBridge, + servicePort: doc.spec.servicePort, + k8sType: doc.spec.k8sType + } + } catch (error) { + if (error instanceof Errors.ValidationError) { + throw error + } + throw new Errors.ValidationError(`Error parsing YAML: ${error.message}`) + } +} + const mapImages = (images) => { const imgs = [] if (images.x86 != null) { @@ -80,20 +257,103 @@ const parseMicroserviceImages = async (fileImages) => { const parseMicroserviceYAML = async (microservice) => { const { registryId, catalogItemId, images } = await parseMicroserviceImages(microservice.images) + const container = microservice.container || {} + + // Parse environment variables with support for value, valueFromSecret, and valueFromConfigMap + const parseEnvVariables = (envArray) => { + if (!envArray || !Array.isArray(envArray)) { + return [] + } + + return envArray.map(env => { + if (!env || typeof env !== 'object') { + throw new Errors.ValidationError('Invalid environment variable format') + } + + if (!env.key) { + throw new Errors.ValidationError('Environment variable must have a key') + } + + const envVar = { + key: env.key.toString() + } + + // Check that exactly one of value, valueFromSecret, or valueFromConfigMap is provided + const hasValue = env.hasOwnProperty('value') + const hasValueFromSecret = env.hasOwnProperty('valueFromSecret') + const hasValueFromConfigMap = env.hasOwnProperty('valueFromConfigMap') + + const valueCount = [hasValue, hasValueFromSecret, hasValueFromConfigMap].filter(Boolean).length + + if (valueCount === 0) { + throw new Errors.ValidationError(`Environment variable '${env.key}' must have either value, valueFromSecret, or valueFromConfigMap`) + } + + if (valueCount > 1) { + throw new Errors.ValidationError(`Environment variable '${env.key}' can only have one of: value, valueFromSecret, or valueFromConfigMap`) + } + + // Handle simple value + if (hasValue) { + envVar.value = env.value.toString() + } + + // Handle valueFromSecret + if (hasValueFromSecret) { + if (typeof env.valueFromSecret !== 'string') { + throw new Errors.ValidationError(`valueFromSecret for environment variable '${env.key}' must be a string`) + } + const parts = env.valueFromSecret.split('/') + if (parts.length !== 2 || !parts[0] || !parts[1]) { + throw new Errors.ValidationError(`valueFromSecret for environment variable '${env.key}' must be in format 'secret-name/key'`) + } + envVar.valueFromSecret = env.valueFromSecret + } + + // Handle valueFromConfigMap + if (hasValueFromConfigMap) { + if (typeof env.valueFromConfigMap !== 'string') { + throw new Errors.ValidationError(`valueFromConfigMap for environment variable '${env.key}' must be a string`) + } + const parts = env.valueFromConfigMap.split('/') + if (parts.length !== 2 || !parts[0] || !parts[1]) { + throw new Errors.ValidationError(`valueFromConfigMap for environment variable '${env.key}' must be in format 'configmap-name/key'`) + } + envVar.valueFromConfigMap = env.valueFromConfigMap + } + + return envVar + }) + } + const microserviceData = { config: microservice.config != null ? JSON.stringify(microservice.config) : undefined, name: microservice.name, catalogItemId, agentName: lget(microservice, 'agent.name'), registryId, - ...microservice.container, + ...container, + hostNetworkMode: lget(microservice, 'container.hostNetworkMode', false), + isPrivileged: lget(microservice, 'container.isPrivileged', false), + pidMode: lget(microservice, 'container.pidMode', ''), + ipcMode: lget(microservice, 'container.ipcMode', ''), + cpuSetCpus: lget(microservice, 'container.cpuSetCpus', ''), + memoryLimit: lget(microservice, 'container.memoryLimit', undefined), + healthCheck: lget(microservice, 'container.healthCheck', {}), + annotations: container.annotations != null ? JSON.stringify(container.annotations) : undefined, + capAdd: lget(microservice, 'container.capAdd', []), + capDrop: lget(microservice, 'container.capDrop', []), ports: (lget(microservice, 'container.ports', [])), volumeMappings: lget(microservice, 'container.volumes', []), cmd: lget(microservice, 'container.commands', []), - env: (lget(microservice, 'container.env', [])).map(e => ({ key: e.key.toString(), value: e.value.toString() })), + env: parseEnvVariables(lget(microservice, 'container.env', [])), images, extraHosts: lget(microservice, 'container.extraHosts', []), - application: microservice.application + ...microservice.msRoutes, + pubTags: lget(microservice, 'msRoutes.pubTags', []), + subTags: lget(microservice, 'msRoutes.subTags', []), + application: microservice.application, + schedule: lget(microservice, 'schedule', 50) } _deleteUndefinedFields(microserviceData) return microserviceData @@ -134,8 +394,44 @@ async function parseMicroserviceFile (fileContent) { const _deleteUndefinedFields = (obj) => Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]) +async function parseCertificateFile (fileContent) { + try { + const doc = yaml.load(fileContent) + if (!doc || !doc.kind) { + throw new Errors.ValidationError(`Invalid YAML format: missing kind field`) + } + if (doc.kind !== 'Certificate' && doc.kind !== 'CertificateAuthority') { + throw new Errors.ValidationError(`Invalid kind ${doc.kind}`) + } + if (doc.metadata == null || doc.spec == null) { + throw new Errors.ValidationError('Invalid YAML format: missing metadata or spec') + } + + const result = { + name: lget(doc, 'metadata.name', undefined), + ...doc.spec + } + + if (doc.kind === 'CertificateAuthority') { + result.isCA = true + } + + return result + } catch (error) { + if (error instanceof Errors.ValidationError) { + throw error + } + throw new Errors.ValidationError(`Error parsing YAML: ${error.message}`) + } +} + module.exports = { parseAppTemplateFile: parseAppTemplateFile, parseAppFile: parseAppFile, - parseMicroserviceFile: parseMicroserviceFile + parseMicroserviceFile: parseMicroserviceFile, + parseSecretFile: parseSecretFile, + parseVolumeMountFile: parseVolumeMountFile, + parseConfigMapFile: parseConfigMapFile, + parseCertificateFile: parseCertificateFile, + parseServiceFile: parseServiceFile } diff --git a/src/utils/cert.js b/src/utils/cert.js new file mode 100644 index 000000000..c2dccf4f7 --- /dev/null +++ b/src/utils/cert.js @@ -0,0 +1,541 @@ +const forge = require('node-forge') +const k8sClient = require('./k8s-client') +const BigNumber = require('bignumber.js') + +// Types for CA input +const CA_TYPES = { + K8S_SECRET: 'k8s-secret', + DIRECT: 'direct', + SELF_SIGNED: 'self-signed' +} + +/** + * Certificate Authority class + * Holds certificate, private key, and certificate data + */ +class CertificateAuthority { + constructor (certificate, key, crtData) { + this.certificate = certificate + this.key = key + this.crtData = crtData + } + + // Get certificate in PEM format + get certPem () { + return this.certificate + } +} + +/** + * CA Storage Format + * @typedef {Object} CAStorage + * @property {string} cert - PEM encoded certificate + * @property {string} key - PEM encoded private key + */ + +/** + * Validates a CA certificate and key pair + * @param {string} cert - PEM encoded certificate + * @param {string} key - PEM encoded private key + * @returns {boolean} - True if valid + * @throws {Error} - If validation fails + */ +async function validateCA (cert, key) { + try { + // Convert PEM to forge objects + const forgeCert = forge.pki.certificateFromPem(cert) + const forgeKey = forge.pki.privateKeyFromPem(key) + + // Extract public key from the certificate + const certPublicKey = forgeCert.publicKey + + // Create a message to test the keys + const md = forge.md.sha256.create() + md.update('test', 'utf8') + + // Sign with private key + const signature = forge.util.encode64( + forgeKey.sign(md) + ) + + // Verify with the certificate's public key + const verified = certPublicKey.verify( + md.digest().getBytes(), + forge.util.decode64(signature) + ) + + if (!verified) { + throw new Error('Private key does not match certificate') + } + + return true + } catch (error) { + throw new Error(`CA validation failed: ${error.message}`) + } +} + +/** + * Stores CA certificate and key to internal secret storage + * @param {CAStorage} ca - CA data to store + * @param {string} name - Name of the secret + * @returns {Promise} + */ +async function storeCA (ca, name) { + try { + // Ensure data is in base64 format for TLS secrets + const secretData = { + 'tls.crt': Buffer.from(ca.cert).toString('base64'), + 'tls.key': Buffer.from(ca.key).toString('base64'), + 'ca.crt': Buffer.from(ca.cert).toString('base64') + } + + const secret = { + name: name, + type: 'tls', + data: secretData + } + + // Use the secret service to store the CA + const SecretService = require('../services/secret-service') + await SecretService.createSecretEndpoint(secret) + } catch (error) { + throw new Error(`Failed to store CA: ${error.message}`) + } +} + +/** + * Loads CA certificate and key from internal secret storage + * @param {string} name - Name of the secret + * @returns {Promise} + */ +async function loadCA (name) { + try { + // Use SecretManager to get the secret with decryption handling + const SecretManager = require('../data/managers/secret-manager') + const fakeTransaction = { fakeTransaction: true } + + const secret = await SecretManager.getSecret(name, fakeTransaction) + if (!secret) { + throw new Error(`TLS secret with name ${name} not found`) + } + + if (secret.type !== 'tls') { + throw new Error(`Secret ${name} is not a TLS secret`) + } + + if (!secret.data || !secret.data['tls.crt'] || !secret.data['tls.key']) { + throw new Error(`Invalid TLS secret data for ${name}`) + } + + // Convert base64 data back to PEM format + return { + cert: Buffer.from(secret.data['tls.crt'], 'base64').toString(), + key: Buffer.from(secret.data['tls.key'], 'base64').toString() + } + } catch (error) { + throw new Error(`Failed to load CA: ${error.message}`) + } +} + +/** + * Generates a random serial number between 0 and 2^128-1 + * Ensures the serial number is always positive by making sure the first byte < 0x80 + * @returns {string} - Serial number as a decimal string + */ +function generateSerialNumber () { + // Create a random 16-byte buffer + let randomBytes = forge.random.getBytesSync(16) + // Ensure first byte is < 0x80 to prevent negative serial numbers in ASN.1 encoding + // In ASN.1, INTEGER is signed, so if MSB of first byte is set (>= 0x80), it's negative + let firstByte = randomBytes.charCodeAt(0) + // Regenerate first byte if it's >= 0x80 to ensure positive serial number + while (firstByte >= 0x80) { + firstByte = forge.random.getBytesSync(1).charCodeAt(0) + } + randomBytes = String.fromCharCode(firstByte) + randomBytes.substring(1) + // Convert to BigNumber + const serialNumber = new BigNumber('0x' + forge.util.bytesToHex(randomBytes)) + return serialNumber.toString() +} + +/** + * Generates a self-signed CA certificate + * @param {string} subject - CA subject name + * @param {number} expiration - Expiration time in milliseconds + * @returns {Promise} + */ +async function generateSelfSignedCA (subject, expiration = 5 * 365 * 24 * 60 * 60 * 1000) { + try { + // Generate RSA key pair + const keys = forge.pki.rsa.generateKeyPair(2048) + + // Create a certificate + const cert = forge.pki.createCertificate() + + // Set certificate fields + cert.publicKey = keys.publicKey + cert.serialNumber = generateSerialNumber() + + // Set validity period + const now = new Date() + cert.validity.notBefore = now + cert.validity.notAfter = new Date(now.getTime() + expiration) + + // Parse the subject string (format: /CN=Subject Name) + const subjectAttrs = [] + const issuerAttrs = [] + + // Extract CN from subject string + let commonName = subject + if (subject.startsWith('/CN=')) { + commonName = subject.substring(4) + } + + subjectAttrs.push({ name: 'commonName', value: commonName }) + issuerAttrs.push({ name: 'commonName', value: commonName }) + + cert.setSubject(subjectAttrs) + cert.setIssuer(issuerAttrs) // Self-signed, so issuer = subject + + // Add extensions for a CA certificate + cert.setExtensions([ + { + name: 'basicConstraints', + cA: true, + critical: true + }, + { + name: 'keyUsage', + keyCertSign: true, + cRLSign: true, + critical: true + }, + { + name: 'subjectKeyIdentifier' + } + ]) + + // Self-sign the certificate with SHA-256 + cert.sign(keys.privateKey, forge.md.sha256.create()) + + // Convert to PEM + const certPem = forge.pki.certificateToPem(cert) + const keyPem = forge.pki.privateKeyToPem(keys.privateKey) + + return { + cert: certPem, + key: keyPem + } + } catch (error) { + throw new Error(`Failed to generate certificate: ${error.message}`) + } +} + +// CA handling functions +async function getCAFromK8sSecret (secretName) { + try { + // Check that k8sClient is properly required and available + if (!k8sClient) { + throw new Error('Kubernetes client not available') + } + const secret = await k8sClient.getSecret(secretName) + if (!secret) { + return null + } + if (!secret.data) { + return null + } + if (!secret.data['tls.crt'] || !secret.data['tls.key']) { + return null + } + + const cert = Buffer.from(secret.data['tls.crt'], 'base64').toString() + const key = Buffer.from(secret.data['tls.key'], 'base64').toString() + + // Check if we need to register this CA in our local database + try { + // Use SecretManager to check if there's a local secret + const SecretManager = require('../data/managers/secret-manager') + const localSecret = await SecretManager.findOne({ name: secretName }, { fakeTransaction: true }) + + // If no local secret, we need to create one + if (!localSecret) { + // Store the CA in local secret storage + await storeCA({ cert, key }, secretName) + // Also create a certificate record + const CertificateManager = require('../data/managers/certificate-manager') + const forge = require('node-forge') + const forgeCert = forge.pki.certificateFromPem(cert) + // Extract subject + const subject = forgeCert.subject.getField('CN') ? forgeCert.subject.getField('CN').value : secretName + + // Create CA record + await CertificateManager.createCertificateRecord({ + name: secretName, + subject: subject, + isCA: true, + validFrom: forgeCert.validity.notBefore, + validTo: forgeCert.validity.notAfter, + serialNumber: forgeCert.serialNumber + }, { fakeTransaction: true }) + } + } catch (dbError) { + // Continue anyway - we at least have the cert/key + } + + return new CertificateAuthority( + cert, + key, + cert + ) + } catch (error) { + throw new Error(`Failed to get CA from Kubernetes secret: ${error.message}`) + } +} + +async function getCAFromDirect (ca) { + if (!ca.cert || !ca.key) { + throw new Error('CA must provide both certificate and private key in PEM format') + } + + try { + // Validate the CA + await validateCA(ca.cert, ca.key) + + return new CertificateAuthority(ca.cert, ca.key, ca.cert) + } catch (error) { + throw new Error(`failed to get CA from direct input: ${error.message}`) + } +} + +async function getCAFromInput (ca) { + if (!ca) { + return null + } + + // Normalize CA type to lowercase for case-insensitive matching + const caType = ca.type ? ca.type.toLowerCase() : '' + + switch (caType) { + case CA_TYPES.K8S_SECRET.toLowerCase(): + return getCAFromK8sSecret(ca.secretName) + case CA_TYPES.DIRECT.toLowerCase(): + if (ca.secretName) { + // If secretName is provided, load from internal secret storage + const caData = await loadCA(ca.secretName) + return getCAFromDirect(caData) + } + return getCAFromDirect(ca) + case CA_TYPES.SELF_SIGNED.toLowerCase(): + return null + default: + throw new Error(`unknown CA type: ${caType}. Expected one of: ${Object.values(CA_TYPES).join(', ')}`) + } +} + +/** + * Main certificate generation function + * @param {Object} params - Certificate parameters + * @returns {Promise} - Certificate data + */ +async function generateCertificate ({ + name, + subject, + hosts, + expiration = 5 * 365 * 24 * 60 * 60 * 1000, + ca, + isRenewal = false +}) { + try { + const caCert = await getCAFromInput(ca) + + // Generate RSA key pair + const keys = forge.pki.rsa.generateKeyPair(2048) + + // Create a certificate + const cert = forge.pki.createCertificate() + + // Set certificate fields + cert.publicKey = keys.publicKey + cert.serialNumber = generateSerialNumber() + + // Set validity period + const now = new Date() + cert.validity.notBefore = now + cert.validity.notAfter = new Date(now.getTime() + expiration) + + // Parse the subject string (format: /CN=Subject Name) + const subjectAttrs = [] + + // Extract CN from subject string + let commonName = subject + if (subject.startsWith('/CN=')) { + commonName = subject.substring(4) + } + + subjectAttrs.push({ name: 'commonName', value: commonName }) + cert.setSubject(subjectAttrs) + + // Process hosts for Subject Alternative Names + const hostsList = hosts ? hosts.split(',').map(h => h.trim()) : [] + const altNames = [] + + for (const host of hostsList) { + if (host.match(/^(\d{1,3}\.){3}\d{1,3}$/)) { + // IP address + altNames.push({ type: 7, ip: host }) + altNames.push({ type: 2, value: host }) + } else { + // DNS name + altNames.push({ type: 2, value: host }) + } + } + + // Set up the certificate based on whether we have a CA or not + if (caCert) { + // If we have a CA, use it to sign the certificate + const caForgeCert = forge.pki.certificateFromPem(caCert.certPem || caCert.crtData) + const caForgeKey = forge.pki.privateKeyFromPem(caCert.key) + + // Set the issuer from the CA + cert.setIssuer(caForgeCert.subject.attributes) + + // Add extensions for a server certificate + cert.setExtensions([ + { + name: 'basicConstraints', + cA: false, + critical: true + }, + { + name: 'keyUsage', + digitalSignature: true, + keyEncipherment: true, + critical: true + }, + { + name: 'extKeyUsage', + serverAuth: true, + clientAuth: true + }, + { + name: 'subjectAltName', + altNames: altNames + }, + { + name: 'authorityKeyIdentifier', + authorityCertIssuer: true, + serialNumber: caForgeCert.serialNumber + } + ]) + + // Sign the certificate with the CA's private key + cert.sign(caForgeKey, forge.md.sha256.create()) + } else { + // Self-signed certificate + cert.setIssuer(subjectAttrs) + + // Add extensions for a self-signed server certificate + cert.setExtensions([ + { + name: 'basicConstraints', + cA: false, + critical: true + }, + { + name: 'keyUsage', + digitalSignature: true, + keyEncipherment: true, + critical: true + }, + { + name: 'extKeyUsage', + serverAuth: true, + clientAuth: true + }, + { + name: 'subjectAltName', + altNames: altNames + }, + { + name: 'subjectKeyIdentifier' + } + ]) + + // Self-sign the certificate + cert.sign(keys.privateKey, forge.md.sha256.create()) + } + + // Convert to PEM + const certPem = forge.pki.certificateToPem(cert) + const keyPem = forge.pki.privateKeyToPem(keys.privateKey) + + // Store the certificate as a TLS secret + const secretData = { + 'tls.crt': Buffer.from(certPem).toString('base64'), + 'tls.key': Buffer.from(keyPem).toString('base64'), + 'ca.crt': Buffer.from(caCert ? caCert.certPem || caCert.crtData : certPem).toString('base64') + } + + const secret = { + name: name, + type: 'tls', + data: secretData + } + + // Use the secret service to store the certificate + const SecretService = require('../services/secret-service') + + if (isRenewal) { + // For renewals, delete the existing secret first + try { + await SecretService.deleteSecretEndpoint(name) + } catch (error) { + // If the secret doesn't exist, that's okay, just continue + if (error.name !== 'NotFoundError') { + throw error + } + } + } + + // Create new secret with certificate data + await SecretService.createSecretEndpoint(secret) + + return { + cert: certPem, + key: keyPem, + ca: caCert ? caCert.crtData : certPem + } + } catch (error) { + throw error + } +} + +function decodeCertificate (data) { + try { + const cert = forge.pki.certificateFromPem(data) + return { + subject: cert.subject.getField('CN').value, + issuer: cert.issuer.getField('CN').value, + validFrom: cert.validity.notBefore, + validTo: cert.validity.notAfter, + serialNumber: cert.serialNumber, + extensions: cert.extensions + } + } catch (error) { + throw new Error(`Failed to decode certificate: ${error.message}`) + } +} + +module.exports = { + CA_TYPES, + CertificateAuthority, + generateCertificate, + decodeCertificate, + generateSelfSignedCA, + storeCA, + loadCA, + validateCA, + getCAFromDirect, + getCAFromK8sSecret +} diff --git a/src/utils/k8s-client.js b/src/utils/k8s-client.js new file mode 100644 index 000000000..7cbdabe34 --- /dev/null +++ b/src/utils/k8s-client.js @@ -0,0 +1,276 @@ +const logger = require('../logger') +const config = require('../config') + +// Only set CONTROLLER_NAMESPACE if running in Kubernetes mode +let CONTROLLER_NAMESPACE = null + +function checkKubernetesEnvironment () { + const controlPlane = process.env.CONTROL_PLANE || config.get('app.ControlPlane') + return controlPlane && controlPlane.toLowerCase() === 'kubernetes' +} + +if (checkKubernetesEnvironment()) { + CONTROLLER_NAMESPACE = process.env.CONTROLLER_NAMESPACE + + // Validate that CONTROLLER_NAMESPACE is set when in Kubernetes mode + if (!CONTROLLER_NAMESPACE) { + logger.error('CONTROLLER_NAMESPACE environment variable is not set') + throw new Error('CONTROLLER_NAMESPACE environment variable is not set') + } +} + +let k8sApi = null + +async function initializeK8sClient () { + if (!k8sApi) { + logger.debug('Initializing Kubernetes client') + const k8s = require('@kubernetes/client-node') + const kubeConfig = new k8s.KubeConfig() + + // Use the in-cluster configuration + kubeConfig.loadFromCluster() + // kubeConfig.loadFromDefault() + k8sApi = kubeConfig.makeApiClient(k8s.CoreV1Api) + logger.info('Kubernetes client initialized successfully') + } + return k8sApi +} + +async function getSecret (secretName) { + logger.debug(`Getting secret: ${secretName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.readNamespacedSecret(secretName, CONTROLLER_NAMESPACE) + logger.info(`Successfully retrieved secret: ${secretName}`) + return response.body + } catch (error) { + logger.error(`Failed to get secret ${secretName}: ${error.message}`) + throw error + } +} + +async function getService (serviceName) { + logger.debug(`Getting service: ${serviceName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.readNamespacedService(serviceName, CONTROLLER_NAMESPACE) + logger.info(`Successfully retrieved service: ${serviceName}`) + return response.body + } catch (error) { + logger.error(`Failed to get service ${serviceName}: ${error.message}`) + throw error + } +} + +// ConfigMap methods +async function getConfigMap (configMapName) { + logger.debug(`Getting ConfigMap: ${configMapName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.readNamespacedConfigMap(configMapName, CONTROLLER_NAMESPACE) + logger.info(`Successfully retrieved ConfigMap: ${configMapName}`) + return response.body + } catch (error) { + logger.error(`Failed to get ConfigMap ${configMapName}: ${error.message}`) + throw error + } +} + +async function patchConfigMap (configMapName, patchData) { + logger.debug(`Patching ConfigMap: ${configMapName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + + // Create JSON Patch operation with formatted JSON + const patch = [ + { + op: 'replace', + path: '/data/skrouterd.json', + value: typeof patchData.data['skrouterd.json'] === 'string' + ? JSON.stringify(JSON.parse(patchData.data['skrouterd.json']), null, 2) + : JSON.stringify(patchData.data['skrouterd.json'], null, 2) + } + ] + + const { body: configMap } = await api.patchNamespacedConfigMap( + configMapName, + CONTROLLER_NAMESPACE, + patch, + undefined, + undefined, + undefined, + undefined, + undefined, + { headers: { 'content-type': 'application/json-patch+json' } } + ) + logger.info(`Successfully patched ConfigMap: ${configMapName}`) + return configMap + } catch (error) { + logger.error(`Failed to patch ConfigMap ${configMapName}: ${error.message}`) + if (error.response) { + logger.error(`Response status: ${error.response.status}`) + logger.error(`Response body: ${JSON.stringify(error.response.body)}`) + } + throw error + } +} + +// Service methods +async function getNamespacedServices () { + logger.debug(`Listing services in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.listNamespacedService(CONTROLLER_NAMESPACE) + logger.info(`Successfully retrieved ${response.body.items.length} services in namespace: ${CONTROLLER_NAMESPACE}`) + return response.body + } catch (error) { + logger.error(`Failed to list services in namespace ${CONTROLLER_NAMESPACE}: ${error.message}`) + throw error + } +} + +async function createService (serviceSpec) { + logger.debug(`Creating service in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.createNamespacedService(CONTROLLER_NAMESPACE, serviceSpec) + logger.info(`Successfully created service: ${response.body.metadata.name} in namespace: ${CONTROLLER_NAMESPACE}`) + return response.body + } catch (error) { + logger.error(`Failed to create service in namespace ${CONTROLLER_NAMESPACE}: ${error.message}`) + if (error.response) { + logger.error(`Response status: ${error.response.status}`) + logger.error(`Response body: ${JSON.stringify(error.response.body)}`) + } + throw error + } +} + +async function deleteService (serviceName) { + logger.debug(`Deleting service: ${serviceName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + const response = await api.deleteNamespacedService(serviceName, CONTROLLER_NAMESPACE) + logger.info(`Successfully deleted service: ${serviceName} from namespace: ${CONTROLLER_NAMESPACE}`) + return response.body + } catch (error) { + logger.error(`Failed to delete service ${serviceName}: ${error.message}`) + throw error + } +} + +/** + * Updates a service using strategic merge patch + * @param {string} serviceName - The name of the service to update + * @param {Object} patchData - The patch data to apply to the service + * @returns {Promise} The updated service object + */ +async function updateService (serviceName, patchData) { + logger.debug(`Updating service: ${serviceName} in namespace: ${CONTROLLER_NAMESPACE}`) + try { + const api = await initializeK8sClient() + + const patch = [] + + // Update spec fields + if (patchData.spec.type) { + patch.push({ op: 'replace', path: '/spec/type', value: patchData.spec.type }) + } + if (patchData.spec.selector) { + patch.push({ op: 'replace', path: '/spec/selector', value: patchData.spec.selector }) + } + if (patchData.spec.ports) { + patch.push({ op: 'replace', path: '/spec/ports', value: patchData.spec.ports }) + } + + // Update annotations + if (patchData.metadata && patchData.metadata.annotations) { + patch.push({ op: 'replace', path: '/metadata/annotations', value: patchData.metadata.annotations }) + } + const response = await api.patchNamespacedService( + serviceName, + CONTROLLER_NAMESPACE, + patch, + undefined, + undefined, + undefined, + undefined, + undefined, + { headers: { 'Content-Type': 'application/json-patch+json' } } + ) + logger.info(`Successfully updated service: ${serviceName} in namespace: ${CONTROLLER_NAMESPACE}`) + return response.body + } catch (error) { + logger.error(`Failed to update service ${serviceName}: ${error.message}`) + if (error.response) { + logger.error(`Response status: ${error.response.status}`) + logger.error(`Response body: ${JSON.stringify(error.response.body)}`) + } + throw error + } +} + +/** + * Gets the LoadBalancer IP for a service if it exists + * @param {string} serviceName - The name of the service + * @param {number} maxRetries - Maximum number of retries (default: 30) + * @param {number} retryInterval - Interval between retries in milliseconds (default: 2000) + * @returns {Promise} The LoadBalancer IP or null if not available after timeout + */ +async function watchLoadBalancerIP (serviceName, maxRetries = 10, retryInterval = 2000) { + logger.debug(`Checking LoadBalancer IP for service: ${serviceName} in namespace: ${CONTROLLER_NAMESPACE}`) + const api = await initializeK8sClient() + + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const response = await api.readNamespacedService(serviceName, CONTROLLER_NAMESPACE) + const service = response.body + + // Check if the service type is LoadBalancer + if (service.spec && service.spec.type === 'LoadBalancer') { + // Check if the LoadBalancer IP exists + if (service.status && + service.status.loadBalancer && + service.status.loadBalancer.ingress && + service.status.loadBalancer.ingress.length > 0) { + const ingress = service.status.loadBalancer.ingress[0] + if (ingress.ip) { + logger.info(`Found LoadBalancer IP: ${ingress.ip} for service: ${serviceName}`) + return ingress.ip + } else if (ingress.hostname) { + logger.info(`Found LoadBalancer hostname: ${ingress.hostname} for service: ${serviceName}`) + return ingress.hostname + } + } + logger.info(`Service ${serviceName} is LoadBalancer type but IP not yet assigned (attempt ${attempt + 1}/${maxRetries})`) + } else { + const serviceType = service.spec && service.spec.type ? service.spec.type : 'unknown' + logger.info(`Service ${serviceName} is not of type LoadBalancer (type: ${serviceType})`) + return null + } + + // Wait before next retry + await new Promise(resolve => setTimeout(resolve, retryInterval)) + } catch (error) { + logger.error(`Error getting LoadBalancer IP for service ${serviceName}: ${error.message}`) + // Wait before next retry even if there's an error + await new Promise(resolve => setTimeout(resolve, retryInterval)) + } + } + + logger.warn(`LoadBalancer IP not assigned for service ${serviceName} after ${maxRetries} attempts`) + return null +} + +module.exports = { + getSecret, + getService, + getConfigMap, + patchConfigMap, + getNamespacedServices, + createService, + deleteService, + updateService, + watchLoadBalancerIP, + checkKubernetesEnvironment +} diff --git a/src/utils/ssl-utils.js b/src/utils/ssl-utils.js new file mode 100644 index 000000000..6a0a12005 --- /dev/null +++ b/src/utils/ssl-utils.js @@ -0,0 +1,81 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2023 Datasance Teknoloji A.S. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const fs = require('fs') +const logger = require('../logger') + +/** + * Loads a certificate from either a file path or base64 string + * @param {string} source - The source of the certificate (file path or base64 string) + * @param {boolean} isBase64 - Whether the source is a base64 string + * @returns {Buffer} The loaded certificate + * @throws {Error} If there's an error loading the certificate + */ +function loadCertificate (source, isBase64 = false) { + try { + if (!source) { + throw new Error('Certificate source is empty') + } + + if (isBase64) { + return Buffer.from(source, 'base64') + } + return fs.readFileSync(source) + } catch (e) { + logger.error(`Error loading certificate: ${e.message}`) + throw e + } +} + +/** + * Creates SSL options from either file paths or base64 strings + * @param {Object} options - SSL configuration options + * @param {string} options.key - SSL key file path or base64 string + * @param {string} options.cert - SSL certificate file path or base64 string + * @param {string} [options.intermedKey] - Intermediate certificate file path or base64 string + * @param {boolean} [options.isBase64=false] - Whether the inputs are base64 strings + * @returns {Object} SSL options for HTTPS server + */ +function createSSLOptions ({ key, cert, intermedKey, isBase64 = false }) { + if (!key || !cert) { + throw new Error('SSL key and certificate are required') + } + + const sslOptions = { + key: loadCertificate(key, isBase64), + cert: loadCertificate(cert, isBase64), + requestCert: true, + rejectUnauthorized: false + } + + // Only add CA if intermediate certificate is provided and exists + if (intermedKey) { + try { + // Check if file exists when not using base64 + if (!isBase64 && !fs.existsSync(intermedKey)) { + logger.warn(`Intermediate certificate file not found at path: ${intermedKey}, continuing without it`) + return sslOptions + } + sslOptions.ca = loadCertificate(intermedKey, isBase64) + } catch (e) { + logger.warn('Intermediate certificate could not be loaded, continuing without it') + } + } + + return sslOptions +} + +module.exports = { + loadCertificate, + createSSLOptions +} diff --git a/src/views/email-activation-temp.js b/src/views/email-activation-temp.js deleted file mode 100644 index 6f85c3fb4..000000000 --- a/src/views/email-activation-temp.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const emailActivationTemplate = { - p1: ` -
- - - - - - - - -
-
- - - - - - - - - -
- Welcome to Eclipse ioFog! -
- - - - - - - - - - - - - - - - -
- Before we can get started, you need to click this button to activate your account. - If clicking the button doesn't work, copy and paste copy and paste this link into your web browser - address bar.
-
- ', - - p4: '/account/activate/code/', - - p5: ' -
- - Confirm Email Address - -
- — the IOFOG team -
-
-
- - - - - - -
- Follow - - @EclipseioFog - - on Twitter. -
-
-
-
-
-
` -} - -module.exports = emailActivationTemplate diff --git a/src/views/email-temp.js b/src/views/email-temp.js deleted file mode 100644 index 155cb39e9..000000000 --- a/src/views/email-temp.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const emailRecoveryTemplate = { - p1: `
- - - - - - -
-
- - - - - - - - - -
- Hi, ', - - p2: '
- - - - - - - - - - - - -
- We sent you this email just to tell you that your password was changed. - Did you do that? If so, then we are all good. If not, then please contact us - so we can help you avoid any potential problems. -
- You can just reply directly to this email if you need to contact us. -
- — the IOFOG team -
-
-
- - - - - - -
- Follow @EclipseioFog on Twitter. -
-
-
-
` -} - -module.exports = emailRecoveryTemplate diff --git a/src/views/reset-password-temp.js b/src/views/reset-password-temp.js deleted file mode 100644 index a6f893dd9..000000000 --- a/src/views/reset-password-temp.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2020 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const emailResetTemplate = { - p1: `
- - - - - - - -
-
- - - - - - - - - -
- Hi ', - - p2: '
- - - - - - - - - - - - - - - - -
- It took like you were having some trouble with your password? -
- You can use the temporary password ', - - p3: ' to log in. -
- - Go To Login - -
- — the IOFOG team -
-
-
- - - - - - -
- Follow - - @EclipseioFog - - on Twitter. -
-
-
-
-
-
` -} - -module.exports = emailResetTemplate diff --git a/src/websocket/error-handler.js b/src/websocket/error-handler.js new file mode 100644 index 000000000..f8a372e87 --- /dev/null +++ b/src/websocket/error-handler.js @@ -0,0 +1,54 @@ +const logger = require('../logger') + +class WebSocketError extends Error { + constructor (code, message) { + super(message) + this.code = code + this.name = 'WebSocketError' + } +} + +class WebSocketErrorHandler { + static handleError (ws, error) { + logger.error('WebSocket error:' + JSON.stringify({ error })) + + if (error instanceof WebSocketError) { + ws.send(JSON.stringify({ + type: 'error', + code: error.code, + message: error.message + })) + ws.close(error.code, error.message) + } else { + ws.send(JSON.stringify({ + type: 'error', + code: 1011, + message: 'Internal server error' + })) + ws.close(1011, 'Internal server error') + } + } + + static createError (code, message) { + return new WebSocketError(code, message) + } + + static getErrorCode (error) { + if (error instanceof WebSocketError) { + return error.code + } + return 1011 // Internal server error + } + + static getErrorMessage (error) { + if (error instanceof WebSocketError) { + return error.message + } + return 'Internal server error' + } +} + +module.exports = { + WebSocketError, + WebSocketErrorHandler +} diff --git a/src/websocket/server.js b/src/websocket/server.js new file mode 100644 index 000000000..3ef2e88d2 --- /dev/null +++ b/src/websocket/server.js @@ -0,0 +1,1964 @@ +const WebSocket = require('ws') +const config = require('../config') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const SessionManager = require('./session-manager') +const { WebSocketError } = require('./error-handler') +const MicroserviceManager = require('../data/managers/microservice-manager') +const ApplicationManager = require('../data/managers/application-manager') +const MicroserviceStatusManager = require('../data/managers/microservice-status-manager') +const { microserviceState, microserviceExecState } = require('../enums/microservice-state') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') +const keycloak = require('../config/keycloak.js').initKeycloak() +const AuthDecorator = require('../decorators/authorization-decorator') +const TransactionDecorator = require('../decorators/transaction-decorator') +const msgpack = require('@msgpack/msgpack') +const WebSocketQueueService = require('../services/websocket-queue-service') + +const MESSAGE_TYPES = { + STDIN: 0, + STDOUT: 1, + STDERR: 2, + CONTROL: 3, + CLOSE: 4, + ACTIVATION: 5 +} + +const EventService = require('../services/event-service') + +class WebSocketServer { + constructor () { + this.wss = null + this.agentSessions = new Map() + this.userSessions = new Map() + this.connectionLimits = new Map() + this.rateLimits = new Map() + this.sessionManager = new SessionManager(config.get('server.webSocket')) + this.queueService = WebSocketQueueService + this.pendingCloseTimeouts = new Map() // Track pending CLOSE messages in cross-replica scenarios + this.config = { + pingInterval: process.env.WS_PING_INTERVAL || config.get('server.webSocket.pingInterval'), + pongTimeout: process.env.WS_PONG_TIMEOUT || config.get('server.webSocket.pongTimeout'), + maxPayload: process.env.WS_MAX_PAYLOAD || config.get('server.webSocket.maxPayload'), + sessionTimeout: process.env.WS_SESSION_TIMEOUT || config.get('server.webSocket.session.timeout'), + cleanupInterval: process.env.WS_CLEANUP_INTERVAL || config.get('server.webSocket.session.cleanupInterval'), + sessionMaxConnections: process.env.WS_SESSION_MAX_CONNECTIONS || config.get('server.webSocket.session.maxConnections'), + closeResponseTimeout: process.env.WS_CLOSE_RESPONSE_TIMEOUT || 5000 // 5 seconds timeout for agent CLOSE response + } + + this.ensureSocketPongHandler = (ws) => { + if (!ws || ws._hasPingListener) { + return + } + ws._hasPingListener = true + ws.on('ping', () => { + if (ws.readyState === WebSocket.OPEN) { + try { + ws.pong() + } catch (error) { + logger.debug('[RELAY] Failed to respond to ping frame', { error: error.message }) + } + } + }) + } + } + + // MessagePack encoding/decoding helpers with improved error handling + encodeMessage (message) { + try { + // Ensure we're only encoding the actual message content + const encoded = msgpack.encode(message) + logger.debug('Encoded MessagePack message:' + JSON.stringify({ + type: typeof message, + isMap: message instanceof Map, + keys: message instanceof Map ? Array.from(message.keys()) : Object.keys(message), + hasExecId: message instanceof Map ? message.has('execId') : 'execId' in message, + hasMicroserviceUuid: message instanceof Map ? message.has('microserviceUuid') : 'microserviceUuid' in message, + encodedLength: encoded.length, + firstBytes: encoded.subarray(0, 16).toString('hex') + })) + return encoded + } catch (error) { + logger.error('Failed to encode message:' + JSON.stringify({ + error: error.message, + message: message + })) + throw new WebSocketError(1008, 'Message encoding failed') + } + } + + decodeMessage (buffer) { + try { + const decoded = msgpack.decode(buffer) + logger.debug('Decoded MessagePack message:' + JSON.stringify({ + type: typeof decoded, + isMap: decoded instanceof Map, + keys: decoded instanceof Map ? Array.from(decoded.keys()) : Object.keys(decoded), + hasExecId: decoded instanceof Map ? decoded.has('execId') : 'execId' in decoded, + hasMicroserviceUuid: decoded instanceof Map ? decoded.has('microserviceUuid') : 'microserviceUuid' in decoded, + bufferLength: buffer.length, + firstBytes: buffer.subarray(0, 16).toString('hex') + })) + return decoded + } catch (error) { + logger.error('Failed to decode MessagePack message:' + JSON.stringify({ + error: error.message, + bufferLength: buffer.length, + firstBytes: buffer.subarray(0, 16).toString('hex') + })) + throw error + } + } + + initialize (server) { + // Strict WebSocket configuration with no extensions and RSV control + const options = { + server, + maxPayload: process.env.WS_SECURITY_MAX_PAYLOAD || config.get('server.webSocket.security.maxPayload'), + perMessageDeflate: false, // Explicitly disable compression + clientTracking: true, + verifyClient: this.verifyClient.bind(this), + // Strict protocol handling + handleProtocols: (protocols) => { + // Accept any protocol but ensure strict mode + return protocols[0] + } + } + + logger.info('Initializing WebSocket server with strict options:' + JSON.stringify(options)) + this.wss = new WebSocket.Server(options) + + // Handle WebSocket server errors + this.wss.on('error', (error) => { + logger.error('WebSocket server error:' + JSON.stringify({ + error: error.message, + stack: error.stack + })) + }) + + // Handle individual connection errors + this.wss.on('connection', (ws, req) => { + logger.info('New WebSocket connection established:' + JSON.stringify({ + url: req.url, + headers: req.headers, + remoteAddress: req.socket.remoteAddress + })) + + // Set strict WebSocket options for this connection + ws.binaryType = 'arraybuffer' // Force binary type to be arraybuffer + + if (ws._socket) { + ws._socket.setNoDelay(true) + ws._socket.setKeepAlive(true, 30000) // Enable keep-alive instead of disabling + } + + // Add detailed frame-level logging + ws.on('message', (data, isBinary) => { + const buffer = Buffer.from(data) + logger.debug('WebSocket frame received:' + JSON.stringify({ + isBinary, + length: buffer.length, + firstBytes: buffer.subarray(0, 16).toString('hex'), + lastBytes: buffer.subarray(-16).toString('hex'), + url: req.url + })) + }) + + // Add error handler for each connection + ws.on('error', (error) => { + logger.error('WebSocket connection error:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url + })) + if (ws.readyState === WebSocket.OPEN) { + try { + ws.close(1002, 'Protocol error') + } catch (closeError) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } + }) + + // Wrap handleConnection in try-catch to prevent unhandled errors + try { + this.handleConnection(ws, req) + } catch (error) { + logger.error('Unhandled error in handleConnection:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url + })) + if (ws.readyState === WebSocket.OPEN) { + try { + ws.close(1002, 'Internal server error') + } catch (closeError) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } + } + }) + + // Add global error handler for the server + process.on('uncaughtException', (error) => { + logger.error('Uncaught exception in WebSocket server:' + JSON.stringify({ + error: error.message, + stack: error.stack + })) + // Don't let the error crash the process + }) + + process.on('unhandledRejection', (reason, promise) => { + logger.error('Unhandled rejection in WebSocket server:' + JSON.stringify({ + reason: reason, + promise: promise + })) + // Don't let the error crash the process + }) + + this.sessionManager.startCleanup() + } + + async verifyClient (info, callback) { + try { + // Check connection limits + const clientIp = info.req.socket.remoteAddress + const currentConnections = this.connectionLimits.get(clientIp) || 0 + if (currentConnections >= (process.env.WS_SECURITY_MAX_CONNECTIONS_PER_IP || config.get('server.webSocket.security.maxConnectionsPerIp'))) { + callback(new Error('Too many connections'), false) + return + } + + // Check rate limits + const now = Date.now() + const rateLimit = this.rateLimits.get(clientIp) || { count: 0, resetTime: now + 60000 } + if (now > rateLimit.resetTime) { + rateLimit.count = 0 + rateLimit.resetTime = now + 60000 + } + + if (rateLimit.count >= (process.env.WS_SECURITY_MAX_REQUESTS_PER_MINUTE || config.get('server.webSocket.security.maxRequestsPerMinute'))) { + callback(new Error('Rate limit exceeded'), false) + return + } + + rateLimit.count++ + this.rateLimits.set(clientIp, rateLimit) + + callback(null, true) + } catch (error) { + callback(new Error('Internal server error'), false) + } + } + + extractMicroserviceUuid (url) { + // Match UUID pattern in the URL + const uuidPattern = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i + const match = url.match(uuidPattern) + return match ? match[0] : null + } + + handleConnection (ws, req) { + // Add error handler for this connection + ws.on('error', (error) => { + logger.error('WebSocket connection error:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url, + headers: req.headers + })) + // Don't let the error crash the process + if (ws.readyState === WebSocket.OPEN) { + try { + ws.close(1002, 'Protocol error') + } catch (closeError) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } + }) + + // Wrap the entire connection handling in a transaction + TransactionDecorator.generateTransaction(async (transaction) => { + try { + // Check for token in Authorization header first (for agent and CLI connections) + let token = req.headers.authorization + + // If no token in header, check query parameters (for React UI connections) + if (!token) { + logger.debug('Missing authentication token in header, checking query parameters') + const url = new URL(req.url, `http://${req.headers.host}`) + token = url.searchParams.get('token') + + // If token is found in query params, format it as Bearer token + if (token) { + token = `Bearer ${token}` + // Store in headers for event creation code + req.headers.authorization = token + } + } + + if (!token) { + logger.error('WebSocket connection failed: Missing authentication token neither in header nor query parameters') + try { + ws.close(1008, 'Missing authentication token') + } catch (error) { + logger.error('Error closing WebSocket:' + error.message) + } + return + } + + const microserviceUuid = this.extractMicroserviceUuid(req.url) + if (!microserviceUuid) { + logger.error('WebSocket connection failed: Invalid endpoint - no UUID found') + try { + ws.close(1008, 'Invalid endpoint') + } catch (error) { + logger.error('Error closing WebSocket:' + error.message) + } + return + } + + // Determine connection type and handle accordingly + if (req.url.startsWith('/api/v3/agent/exec/')) { + await this.handleAgentConnection(ws, req, token, microserviceUuid, transaction) + } else if (req.url.startsWith('/api/v3/microservices/exec/')) { + await this.handleUserConnection(ws, req, token, microserviceUuid, transaction) + } else { + logger.error('WebSocket connection failed: Invalid endpoint') + try { + ws.close(1008, 'Invalid endpoint') + } catch (error) { + logger.error('Error closing WebSocket:' + error.message) + } + return + } + } catch (error) { + logger.error('WebSocket connection error:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url, + headers: req.headers + })) + + // Handle WebSocket errors gracefully + try { + if (ws.readyState === ws.OPEN) { + ws.close(1008, error.message || 'Internal server error') + await MicroserviceExecStatusManager.update( + { microserviceUuid: this.extractMicroserviceUuid(req.url) }, + { execSessionId: '', status: microserviceExecState.INACTIVE }, + transaction + ) + await MicroserviceManager.update({ uuid: this.extractMicroserviceUuid(req.url) }, { execEnabled: false }, transaction) + } + } catch (closeError) { + logger.error('Error closing WebSocket connection:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } + })().catch(error => { + logger.error('Unhandled WebSocket transaction error:' + JSON.stringify({ + error: error.message, + stack: error.stack + })) + }) + } + + async handleAgentConnection (ws, req, token, microserviceUuid, transaction) { + try { + this.ensureSocketPongHandler(ws) + logger.debug('[WS-CONN] Processing agent connection:' + JSON.stringify({ + url: req.url, + microserviceUuid, + remoteAddress: req.socket.remoteAddress + })) + + // Set up message handler for initial message only + const initialMessageHandler = async (data, isBinary) => { + logger.debug('[WS-INIT] Received initial message from agent:' + JSON.stringify({ + isBinary, + url: req.url, + microserviceUuid + })) + + if (!isBinary) { + logger.error('[WS-ERROR] Expected binary message from agent') + ws.close(1008, 'Expected binary message') + return + } + + const buffer = Buffer.from(data) + logger.debug('[WS-INIT] Processing initial message from agent:' + JSON.stringify({ + isBinary, + length: buffer.length, + firstBytes: buffer.subarray(0, 16).toString('hex'), + lastBytes: buffer.subarray(-16).toString('hex') + })) + + let execMsg + try { + execMsg = this.decodeMessage(buffer) + logger.info('[WS-INIT] Decoded MessagePack from agent:' + JSON.stringify(execMsg)) + } catch (err) { + logger.error('[WS-ERROR] Failed to decode MessagePack from agent:' + JSON.stringify({ + error: err.message, + stack: err.stack + })) + ws.close(1008, 'Invalid MessagePack') + return + } + + const { execId, microserviceUuid: msgMicroserviceUuid } = execMsg + if (!execId || !msgMicroserviceUuid) { + logger.error('[WS-ERROR] Agent message missing execId or microserviceUuid:' + JSON.stringify(execMsg)) + ws.close(1008, 'Missing required fields') + return + } + + // Remove the initial message handler + ws.removeListener('message', initialMessageHandler) + + // Try to activate session with the execId from the message + const session = await this.sessionManager.tryActivateSession(msgMicroserviceUuid, execId, ws, true, transaction) + if (session) { + logger.info('[WS-SESSION] Session activated for agent:' + JSON.stringify({ + execId, + microserviceUuid: msgMicroserviceUuid + })) + // Set up message forwarding + logger.debug('[WS-FORWARD] Setting up message forwarding:' + JSON.stringify({ + execId, + microserviceUuid: msgMicroserviceUuid + })) + await this.setupMessageForwarding(execId, transaction) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + const authHeader = req.headers.authorization + let actorId = null + if (authHeader) { + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() === 'bearer' && token) { + try { + const tokenParts = token.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + actorId = payload.sub || null + } + } catch (err) { + // Ignore token parsing errors + } + } + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'agent', + actorId: actorId, + path: req.url, + resourceId: msgMicroserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + } else { + this.attachPendingKeepAliveHandler(ws) + try { + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: execId, status: microserviceExecState.PENDING }, + transaction + ) + logger.debug('[WS-SESSION] Updated microservice exec status to PENDING', { + execId, + microserviceUuid: microserviceUuid + }) + } catch (error) { + logger.error('[WS-SESSION] Failed to update microservice exec status to PENDING', { + execId, + microserviceUuid: microserviceUuid, + error: error.message, + stack: error.stack + }) + // Continue anyway - the in-memory state is correct + } + // Create session with agent only and enable queue bridge for cross-replica support + // This allows the agent to receive messages from users on other replicas via AMQP queues + const agentOnlySession = this.sessionManager.createSession(execId, msgMicroserviceUuid, ws, null, transaction) + try { + // Pass cleanup callback so queue service can notify us when CLOSE is received + await this.queueService.enableForSession(agentOnlySession, (execId) => { + // Clear timeout if it exists (agent responded to CLOSE) + const timeout = this.pendingCloseTimeouts.get(execId) + if (timeout) { + clearTimeout(timeout) + this.pendingCloseTimeouts.delete(execId) + logger.debug('[WS-SESSION] Cleared pending CLOSE timeout - agent responded', { execId }) + } + this.cleanupSession(execId, transaction) + }) + agentOnlySession.queueBridgeEnabled = true + logger.info('[WS-SESSION] No pending user found for agent, added to pending list and enabled queue bridge for cross-replica support:' + JSON.stringify({ + execId, + microserviceUuid: msgMicroserviceUuid + })) + await this.setupMessageForwarding(execId, transaction) + + // Record WebSocket connection event for agent (non-blocking) + // This covers the case when agent connects but no user is waiting (cross-replica or normal) + setImmediate(async () => { + try { + const authHeader = req.headers.authorization + let actorId = null + if (authHeader) { + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() === 'bearer' && token) { + try { + const tokenParts = token.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + actorId = payload.sub || null + } + } catch (err) { + // Ignore token parsing errors + } + } + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'agent', + actorId: actorId, + path: req.url, + resourceId: msgMicroserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + } catch (error) { + logger.warn('[WS-SESSION] Failed to enable queue bridge for pending agent, will use direct relay when user connects:', { + execId, + microserviceUuid: msgMicroserviceUuid, + error: error.message + }) + agentOnlySession.queueBridgeEnabled = false + + // Record WebSocket connection event even if queue bridge failed (non-blocking) + setImmediate(async () => { + try { + const authHeader = req.headers.authorization + let actorId = null + if (authHeader) { + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() === 'bearer' && token) { + try { + const tokenParts = token.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + actorId = payload.sub || null + } + } catch (err) { + // Ignore token parsing errors + } + } + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'agent', + actorId: actorId, + path: req.url, + resourceId: msgMicroserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + } + } + } + + // Bind the message handler BEFORE validation + ws.on('message', initialMessageHandler) + + // Now validate the connection + const fog = await this.validateAgentConnection(token, microserviceUuid, transaction) + logger.debug('[WS-VALIDATE] Agent connection validated:' + JSON.stringify({ + fogUuid: fog.uuid, + microserviceUuid, + url: req.url + })) + + // Handle connection close + ws.on('close', async (code, reason) => { + // Record WebSocket disconnection event (non-blocking) + setImmediate(async () => { + try { + const authHeader = req.headers.authorization + let actorId = null + if (authHeader) { + const [scheme, token] = authHeader.split(' ') + if (scheme.toLowerCase() === 'bearer' && token) { + try { + const tokenParts = token.split('.') + if (tokenParts.length === 3) { + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()) + actorId = payload.sub || null + } + } catch (err) { + // Ignore token parsing errors + } + } + } + await EventService.createWsDisconnectEvent({ + timestamp: Date.now(), + endpointType: 'agent', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null, + closeCode: code + }) + } catch (err) { + logger.error('Failed to create WS_DISCONNECT event (non-blocking):', err) + } + }) + + for (const [execId, session] of this.sessionManager.sessions) { + if (session.agent === ws) { + // In cross-replica scenarios, send CLOSE message to user via queue + // Note: session.user is null on agent's replica in cross-replica scenarios + const queueEnabled = this.queueService.shouldUseQueue(execId) + if (queueEnabled) { + try { + const closeMsg = { + type: MESSAGE_TYPES.CLOSE, + execId: execId, + microserviceUuid: session.microserviceUuid, + timestamp: Date.now(), + data: Buffer.from('Agent closed connection') + } + const encoded = this.encodeMessage(closeMsg) + await this.queueService.publishToUser(execId, encoded, { messageType: MESSAGE_TYPES.CLOSE }) + logger.info('[WS-CLOSE] Sent CLOSE message to user via queue after agent disconnect', { + execId, + microserviceUuid: session.microserviceUuid + }) + } catch (error) { + logger.error('[WS-CLOSE] Failed to send CLOSE message to user via queue', { + execId, + error: error.message + }) + } + } + this.cleanupSession(execId, transaction) + } + } + this.sessionManager.removePendingAgent(microserviceUuid, ws) + logger.debug('[WS-CLOSE] Agent connection closed:' + JSON.stringify({ + url: req.url, + microserviceUuid + })) + }) + + // Handle errors + ws.on('error', (error) => { + logger.error('[WS-ERROR] Agent connection error:' + JSON.stringify({ + error: error.message, + url: req.url, + microserviceUuid + })) + }) + } catch (error) { + logger.error('[WS-ERROR] Error in handleAgentConnection:' + JSON.stringify({ + error: error.message, + stack: error.stack, + url: req.url, + microserviceUuid + })) + if (ws.readyState === ws.OPEN) { + ws.close(1008, error.message || 'Connection error') + } + } + } + + async getPendingAgentExecIdsFromDB (microserviceUuid, transaction) { + try { + const pendingExecStatus = await MicroserviceExecStatusManager.findAllExcludeFields( + { + microserviceUuid: microserviceUuid, + status: microserviceExecState.PENDING + }, + transaction + ) + + const execIds = pendingExecStatus.map(status => status.execSessionId) + logger.debug('Database query for pending agents:' + JSON.stringify({ + microserviceUuid, + foundExecIds: execIds, + count: execIds.length + })) + + return execIds + } catch (error) { + logger.error('Failed to query database for pending agents:' + JSON.stringify({ + error: error.message, + microserviceUuid + })) + return [] + } + } + + async handleUserConnection (ws, req, token, microserviceUuid, transaction) { + try { + this.ensureSocketPongHandler(ws) + await this.validateUserConnection(token, microserviceUuid, transaction) + logger.info('User connection validated successfully for microservice:' + microserviceUuid) + + // Check if there's already an active session for this microservice + const existingSession = Array.from(this.sessionManager.sessions.values()) + .find(session => session.microserviceUuid === microserviceUuid && session.user && session.user.readyState === WebSocket.OPEN) + + if (existingSession) { + logger.debug('Microservice has already active exec session:' + JSON.stringify({ + microserviceUuid, + existingExecId: existingSession.execId + })) + ws.close(1008, 'Microservice has already active exec session.') + return + } + + // Get pending agent execIds from database (multi-replica compatible) + const pendingAgentExecIds = await this.getPendingAgentExecIdsFromDB(microserviceUuid, transaction) + logger.info('Pending agent execIds from database:' + JSON.stringify(pendingAgentExecIds)) + + // Simplified logic: find any available pending agent + const hasPendingAgents = pendingAgentExecIds.length > 0 + + if (hasPendingAgents) { + // Find any available pending agent + const availableExecId = pendingAgentExecIds[0] + const pendingAgent = this.sessionManager.findPendingAgentForExecId(microserviceUuid, availableExecId) + + if (pendingAgent) { + // Activate session using agent's execId (agent is on same replica) + const session = this.sessionManager.tryActivateSession(microserviceUuid, availableExecId, ws, false, transaction) + if (session) { + logger.info('Session activated for user:', { + execId: availableExecId, + microserviceUuid, + userState: ws.readyState, + agentState: pendingAgent.readyState + }) + await this.setupMessageForwarding(availableExecId, transaction) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + // Extract actorId from token (req.kauth not available for WebSocket connections) + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + return + } + } else { + // Agent is on a different replica - create session with just user and enable queue bridge + // The AMQP queues will handle message relay between replicas + logger.info('Found PENDING execId in DB but agent is on different replica, activating session with user only:', { + execId: availableExecId, + microserviceUuid + }) + this.sessionManager.createSession(availableExecId, microserviceUuid, null, ws, transaction) + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: availableExecId, status: microserviceExecState.ACTIVE }, + transaction + ) + await this.setupMessageForwarding(availableExecId, transaction) + logger.info('Cross-replica session activated with user only:', { + execId: availableExecId, + microserviceUuid, + userState: ws.readyState + }) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + // Extract actorId from token (req.kauth not available for WebSocket connections) + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + return + } + } + + // If we reach here, either no pending agent or activation failed + // Add user to pending list to wait for agent + logger.info('No immediate agent available, adding user to pending list:' + JSON.stringify({ + microserviceUuid, + hasPendingAgents, + pendingAgentCount: pendingAgentExecIds.length + })) + this.sessionManager.addPendingUser(microserviceUuid, ws) + this.attachPendingKeepAliveHandler(ws) + + // IMMEDIATE RE-CHECK: Look for any newly available agents after adding user (database query) + const retryPendingAgents = await this.getPendingAgentExecIdsFromDB(microserviceUuid, transaction) + if (retryPendingAgents.length > 0) { + logger.info('Found available agent after adding user, attempting immediate activation:' + JSON.stringify({ + microserviceUuid, + availableExecIds: retryPendingAgents + })) + + // Try to activate session with first available agent + const availableExecId = retryPendingAgents[0] + const pendingAgent = this.sessionManager.findPendingAgentForExecId(microserviceUuid, availableExecId) + + if (pendingAgent) { + // Remove user from pending first since we're activating + this.sessionManager.removePendingUser(microserviceUuid, ws) + const session = this.sessionManager.tryActivateSession(microserviceUuid, availableExecId, ws, false, transaction) + if (session) { + logger.info('Session activated immediately after re-check:' + JSON.stringify({ + execId: availableExecId, + microserviceUuid, + userState: ws.readyState, + agentState: pendingAgent.readyState + })) + + await this.setupMessageForwarding(availableExecId, transaction) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + return // Exit early, session activated successfully + } + } else { + // Agent is on different replica - activate with user only + logger.info('Found PENDING execId in retry but agent is on different replica, activating session with user only:', { + execId: availableExecId, + microserviceUuid + }) + // Remove user from pending first + this.sessionManager.removePendingUser(microserviceUuid, ws) + this.sessionManager.createSession(availableExecId, microserviceUuid, null, ws, transaction) + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: availableExecId, status: microserviceExecState.ACTIVE }, + transaction + ) + await this.setupMessageForwarding(availableExecId, transaction) + logger.info('Cross-replica session activated with user only (retry):', { + execId: availableExecId, + microserviceUuid, + userState: ws.readyState + }) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + return + } + } + + // Only proceed with timeout mechanism if we still couldn't activate + logger.info('No immediate agent available after re-check, proceeding with timeout mechanism') + + // Send status message to user when added to pending using STDERR + try { + const statusMsg = { + type: MESSAGE_TYPES.STDERR, + data: Buffer.from('Waiting for agent connection. Please ensure the microservice/agent is running.\n'), + microserviceUuid: microserviceUuid, + execId: 'pending', // Since we don't have execSessionId anymore + timestamp: Date.now() + } + const encoded = this.encodeMessage(statusMsg) + ws.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + logger.info('Sent waiting status message to user:' + JSON.stringify({ + microserviceUuid, + messageType: 'STDERR', + encodedLength: encoded.length + })) + } catch (error) { + logger.warn('Failed to send status message to user:' + JSON.stringify({ + error: error.message, + microserviceUuid + })) + } + + // Start periodic retry timer for pending users (every 10 seconds) + const RETRY_INTERVAL = 10000 + const startTime = Date.now() + const retryTimer = setInterval(async () => { + if (this.sessionManager.isUserStillPending(microserviceUuid, ws)) { + logger.debug('Periodic retry: checking for available agents:' + JSON.stringify({ + microserviceUuid, + retryCount: Math.floor((Date.now() - startTime) / RETRY_INTERVAL) + })) + + try { + const periodicRetryExecIds = await this.getPendingAgentExecIdsFromDB(microserviceUuid, transaction) + if (periodicRetryExecIds.length > 0) { + logger.info('Periodic retry found available agent:' + JSON.stringify({ + microserviceUuid, + availableExecIds: periodicRetryExecIds + })) + + // Attempt session activation with first available agent + const availableExecId = periodicRetryExecIds[0] + const pendingAgent = this.sessionManager.findPendingAgentForExecId(microserviceUuid, availableExecId) + + if (pendingAgent) { + // Remove user from pending first + this.sessionManager.removePendingUser(microserviceUuid, ws) + const session = this.sessionManager.tryActivateSession(microserviceUuid, availableExecId, ws, false, transaction) + if (session) { + logger.info('Session activated via periodic retry:' + JSON.stringify({ + execId: availableExecId, + microserviceUuid, + userState: ws.readyState, + agentState: pendingAgent.readyState + })) + + await this.setupMessageForwarding(availableExecId, transaction) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + clearInterval(retryTimer) // Stop retry timer + return // Exit early, session activated successfully + } + } else { + // Agent is on different replica - activate with user only + logger.info('Periodic retry found PENDING execId but agent is on different replica, activating session with user only:', { + execId: availableExecId, + microserviceUuid + }) + // Remove user from pending first + this.sessionManager.removePendingUser(microserviceUuid, ws) + this.sessionManager.createSession(availableExecId, microserviceUuid, null, ws, transaction) + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: availableExecId, status: microserviceExecState.ACTIVE }, + transaction + ) + await this.setupMessageForwarding(availableExecId, transaction) + logger.info('Cross-replica session activated with user only (periodic retry):', { + execId: availableExecId, + microserviceUuid, + userState: ws.readyState + }) + + // Record WebSocket connection event (non-blocking) + setImmediate(async () => { + try { + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsConnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null + }) + } catch (err) { + logger.error('Failed to create WS_CONNECT event (non-blocking):', err) + } + }) + clearInterval(retryTimer) // Stop retry timer + return + } + } + } catch (retryError) { + logger.warn('Periodic retry failed:' + JSON.stringify({ + error: retryError.message, + microserviceUuid + })) + } + } else { + // User no longer pending, clear retry timer + clearInterval(retryTimer) + } + }, RETRY_INTERVAL) + + // Store timer reference for cleanup + this.sessionManager.setUserRetryTimer(microserviceUuid, ws, retryTimer) + + // Add timeout mechanism for pending users (60 seconds) + const PENDING_USER_TIMEOUT = 60000 + setTimeout(() => { + if (this.sessionManager.isUserStillPending(microserviceUuid, ws)) { + logger.warn('Pending user timeout, closing connection:' + JSON.stringify({ + microserviceUuid, + timeout: PENDING_USER_TIMEOUT + })) + + // Send timeout message before closing + try { + const timeoutMsg = { + type: MESSAGE_TYPES.STDERR, + data: Buffer.from('Timeout waiting for agent connection. Please try again.\n'), + microserviceUuid: microserviceUuid, + execId: 'pending', // Since we don't have execSessionId anymore + timestamp: Date.now() + } + const encoded = this.encodeMessage(timeoutMsg) + ws.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + logger.info('Sent timeout message to user:' + JSON.stringify({ + microserviceUuid, + messageType: 'STDERR', + encodedLength: encoded.length + })) + } catch (timeoutError) { + logger.warn('Failed to send timeout message to user:' + JSON.stringify({ + error: timeoutError.message, + microserviceUuid + })) + } + + try { + ws.close(1008, 'Timeout waiting for agent connection') + } catch (closeError) { + logger.error('Error closing timed out user connection:' + JSON.stringify({ + error: closeError.message, + microserviceUuid + })) + } + // Clear retry timer before removing user + const retryTimer = this.sessionManager.getUserRetryTimer(microserviceUuid, ws) + if (retryTimer) { + clearInterval(retryTimer) + this.sessionManager.clearUserRetryTimer(microserviceUuid, ws) + } + + this.sessionManager.removePendingUser(microserviceUuid, ws) + } + }, PENDING_USER_TIMEOUT) + + ws.on('close', (code, reason) => { + // Record WebSocket disconnection event (non-blocking) + setImmediate(async () => { + try { + // Extract actorId from token (req.kauth not available for WebSocket connections) + let actorId = null + if (req.headers && req.headers.authorization) { + actorId = EventService.extractUsernameFromToken(req.headers.authorization) + } + await EventService.createWsDisconnectEvent({ + timestamp: Date.now(), + endpointType: 'user', + actorId: actorId, + path: req.url, + resourceId: microserviceUuid, + ipAddress: EventService.extractIPv4Address(req) || null, + closeCode: code + }) + } catch (err) { + logger.error('Failed to create WS_DISCONNECT event (non-blocking):', err) + } + }) + + for (const [execId, session] of this.sessionManager.sessions) { + if (session.user === ws) { + this.cleanupSession(execId, transaction) + } + } + + // Clear retry timer before removing user + const retryTimer = this.sessionManager.getUserRetryTimer(microserviceUuid, ws) + if (retryTimer) { + clearInterval(retryTimer) + this.sessionManager.clearUserRetryTimer(microserviceUuid, ws) + } + + this.sessionManager.removePendingUser(microserviceUuid, ws) + logger.info('User WebSocket disconnected:' + JSON.stringify({ + microserviceUuid, + userState: ws.readyState + })) + }) + } catch (error) { + logger.error('User connection validation failed:' + JSON.stringify({ + error: error.message, + stack: error.stack + })) + // Handle error gracefully instead of throwing + if (ws.readyState === WebSocket.OPEN) { + try { + ws.close(1008, error.message || 'Authentication failed') + } catch (closeError) { + logger.error('Error closing WebSocket:' + JSON.stringify({ + error: closeError.message, + originalError: error.message + })) + } + } + } + } + + // // Helper method - only filter obvious noise + // isNoise(output) { + // // Filter only the most obvious noise + // const noisePatterns = [ + // /^clear: command not found/, // Clear command error + // /^\s*$/, // Empty or whitespace only + // /^.$/ // Single character (usually control chars) + // ] + // return noisePatterns.some(pattern => pattern.test(output)) + // } + + async setupMessageForwarding (execId, transaction) { + const session = this.sessionManager.getSession(execId) + if (!session) { + logger.error('[RELAY] Failed to setup message forwarding: No session found for execId=' + execId) + return + } + + const { agent, user } = session + logger.info('[RELAY] Setting up message forwarding for session:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + agentConnected: !!agent, + userConnected: !!user, + agentState: agent ? agent.readyState : 'N/A', + userState: user ? user.readyState : 'N/A' + })) + this.detachPendingKeepAliveHandler(user) + this.detachPendingKeepAliveHandler(agent) + try { + // Pass cleanup callback so queue service can notify us when CLOSE is received + await this.queueService.enableForSession(session, (execId) => { + // Clear timeout if it exists (agent responded to CLOSE) + const timeout = this.pendingCloseTimeouts.get(execId) + if (timeout) { + clearTimeout(timeout) + this.pendingCloseTimeouts.delete(execId) + logger.debug('[RELAY] Cleared pending CLOSE timeout - agent responded', { execId }) + } + const currentTransaction = session.transaction + this.cleanupSession(execId, currentTransaction) + }) + session.queueBridgeEnabled = true + logger.info('[RELAY] AMQP queue bridge enabled for exec session', { + execId, + microserviceUuid: session.microserviceUuid + }) + } catch (error) { + session.queueBridgeEnabled = false + logger.warn('[RELAY] Failed to enable AMQP queue bridge, falling back to direct WebSocket relay', { + execId, + error: error.message + }) + } + + // Send activation message to agent (works for both direct WebSocket and queue-based forwarding) + const activationMsg = { + type: MESSAGE_TYPES.ACTIVATION, + data: Buffer.from(JSON.stringify({ + execId: execId, + microserviceUuid: session.microserviceUuid, + timestamp: Date.now() + })), + microserviceUuid: session.microserviceUuid, + execId: execId, + timestamp: Date.now() + } + + // sendMessageToAgent handles queue-based forwarding when agent is null + this.sendMessageToAgent(session.agent, activationMsg, execId, session.microserviceUuid) + .then(success => { + if (success) { + logger.info('[RELAY] Session activation complete:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + agentState: session.agent ? session.agent.readyState : 'N/A (cross-replica)', + queueEnabled: this.queueService.shouldUseQueue(execId) + })) + } else { + logger.error('[RELAY] Session activation failed:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + agentState: session.agent ? session.agent.readyState : 'N/A', + queueEnabled: this.queueService.shouldUseQueue(execId) + })) + // Only cleanup if we have a direct agent connection (not queue-based) + if (session.agent) { + this.cleanupSession(execId, transaction) + } + } + }) + .catch(error => { + logger.error('[RELAY] Session activation error:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + error: error.message + })) + // Only cleanup if we have a direct agent connection (not queue-based) + if (session.agent) { + this.cleanupSession(execId, transaction) + } + }) + + // Remove any previous message handlers to avoid duplicates + if (user) { + logger.debug('[RELAY] Removing previous user message handlers for execId=' + execId) + user.removeAllListeners('message') + } + if (agent) { + logger.debug('[RELAY] Removing previous agent message handlers for execId=' + execId) + agent.removeAllListeners('message') + } + + // Forward user -> agent (works for both direct WebSocket and queue-based forwarding) + if (user) { + logger.debug('[RELAY] Setting up user->agent message forwarding for execId=' + execId) + user.on('message', async (data, isBinary) => { + logger.debug('[RELAY] User message received:' + JSON.stringify({ + execId, + isBinary, + dataType: typeof data, + dataLength: data.length, + userState: user.readyState, + agentState: agent ? agent.readyState : 'N/A (cross-replica)', + queueEnabled: this.queueService.shouldUseQueue(execId) + })) + + if (!isBinary) { + // Handle text messages from user + const text = data.toString() + logger.debug('[RELAY] Received text message from user:' + JSON.stringify({ + execId, + text, + length: text.length, + userState: user.readyState, + agentState: session.agent ? session.agent.readyState : 'N/A (cross-replica)', + queueEnabled: this.queueService.shouldUseQueue(execId) + })) + + // Convert text to binary message in agent's expected format + const msg = { + type: MESSAGE_TYPES.STDIN, + data: Buffer.from(text + '\n'), // Add newline for command execution + microserviceUuid: session.microserviceUuid, + execId: execId, + timestamp: Date.now() + } + + // sendMessageToAgent handles queue-based forwarding when agent is null + await this.sendMessageToAgent(session.agent, msg, execId, session.microserviceUuid) + return + } + + const buffer = Buffer.from(data) + try { + const msg = this.decodeMessage(buffer) + // Ensure message has all required fields + if (!msg.microserviceUuid) msg.microserviceUuid = session.microserviceUuid + if (!msg.execId) msg.execId = execId + if (!msg.timestamp) msg.timestamp = Date.now() + + if (msg.type === MESSAGE_TYPES.CLOSE) { + logger.info(`[RELAY] User sent CLOSE for execId=${execId}`) + + const queueEnabled = this.queueService.shouldUseQueue(execId) + + // Forward CLOSE to agent first + await this.sendMessageToAgent(session.agent, msg, execId, session.microserviceUuid) + + if (queueEnabled) { + // Cross-replica scenario: Don't close socket immediately + // Wait for agent's CLOSE response via queue + // The queue service will handle closing the socket in _handleCloseMessage + // when it receives the agent's CLOSE response + logger.debug('[RELAY] Cross-replica CLOSE: waiting for agent response via queue', { + execId, + microserviceUuid: session.microserviceUuid + }) + + // Set timeout in case agent doesn't respond + const timeout = setTimeout(() => { + const currentSession = this.sessionManager.getSession(execId) + if (currentSession && currentSession.user && currentSession.user.readyState === WebSocket.OPEN) { + logger.warn('[RELAY] Agent did not respond to CLOSE within timeout, closing user socket', { + execId, + microserviceUuid: session.microserviceUuid, + timeout: this.config.closeResponseTimeout + }) + try { + currentSession.user.close(1000, 'Session closed (timeout)') + const currentTransaction = currentSession.transaction + this.cleanupSession(execId, currentTransaction) + } catch (error) { + logger.error('[RELAY] Failed to close user socket on timeout', { + execId, + error: error.message + }) + } + } + this.pendingCloseTimeouts.delete(execId) + }, this.config.closeResponseTimeout) + + this.pendingCloseTimeouts.set(execId, timeout) + // Don't cleanup yet - queue service will call cleanup callback when agent responds + return + } else { + // Same replica: Close immediately (existing behavior) + // Close user WebSocket with code 1000 so client's onclose handler shows "Successfully closed" + // The client expects code 1000 (normal closure) to display the success message + if (user && user.readyState === WebSocket.OPEN) { + try { + user.close(1000, 'Session closed') + logger.debug('[RELAY] Closed user WebSocket with code 1000:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid + })) + } catch (error) { + logger.warn('[RELAY] Failed to close user WebSocket:' + JSON.stringify({ + execId, + error: error.message + })) + } + } + + // Get current transaction from the session and cleanup + const currentTransaction = session.transaction + this.cleanupSession(execId, currentTransaction) + return + } + } + + if (msg.type === MESSAGE_TYPES.CONTROL) { + // Handle keep-alive messages from user + const controlData = msg.data.toString() + if (controlData === 'keepalive') { + // Send keep-alive response back to user + const keepAliveResponse = { + type: MESSAGE_TYPES.CONTROL, + data: Buffer.from('keepalive'), + microserviceUuid: session.microserviceUuid, + execId: execId, + timestamp: Date.now() + } + const encoded = this.encodeMessage(keepAliveResponse) + user.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + logger.debug('[RELAY] Sent keep-alive response to user:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid + })) + return // Don't forward keep-alive to agent + } + } + + // sendMessageToAgent handles queue-based forwarding when agent is null + await this.sendMessageToAgent(session.agent, msg, execId, session.microserviceUuid) + } catch (error) { + logger.error('[RELAY] Failed to process binary message:' + JSON.stringify({ + execId, + error: error.message, + stack: error.stack, + bufferLength: buffer.length, + userState: user.readyState, + agentState: session.agent ? session.agent.readyState : 'N/A (cross-replica)' + })) + } + }) + } + + // Forward agent -> user (works for both direct WebSocket and queue-based forwarding) + if (agent) { + logger.debug('[RELAY] Setting up agent->user message forwarding for execId=' + execId) + agent.on('message', async (data, isBinary) => { + logger.debug('[RELAY] Agent message received:' + JSON.stringify({ + execId, + isBinary, + dataType: typeof data, + dataLength: data.length, + userState: session.user ? session.user.readyState : 'N/A (cross-replica)', + agentState: agent.readyState, + queueEnabled: this.queueService.shouldUseQueue(execId) + })) + + try { + const buffer = Buffer.from(data) + const msg = this.decodeMessage(buffer) + logger.debug('[RELAY] Decoded agent message:' + JSON.stringify({ + execId, + type: msg.type, + hasData: !!msg.data, + messageSize: buffer.length + })) + + if (msg.type === MESSAGE_TYPES.CLOSE) { + logger.info(`[RELAY] Agent sent CLOSE for execId=${execId}`) + + const queueEnabled = this.queueService.shouldUseQueue(execId) + + // In cross-replica scenarios, publish CLOSE to queue so user's replica can handle it + if (queueEnabled) { + try { + // Pass message type so queue receiver can detect CLOSE without decoding + await this.queueService.publishToUser(execId, buffer, { messageType: MESSAGE_TYPES.CLOSE }) + logger.debug('[RELAY] Forwarded agent CLOSE message to user via queue:' + JSON.stringify({ + execId, + type: msg.type + })) + } catch (error) { + logger.error('[RELAY] Failed to enqueue CLOSE message for user', { + execId, + error: error.message + }) + } + } else if (session.user && session.user.readyState === WebSocket.OPEN) { + // Direct connection - close user WebSocket immediately + session.user.close(1000, 'Agent closed connection') + } + + // Get current transaction from the session + const currentTransaction = session.transaction + this.cleanupSession(execId, currentTransaction) + return + } + + const queueEnabled = this.queueService.shouldUseQueue(execId) + if (queueEnabled) { + try { + await this.queueService.publishToUser(execId, buffer) + logger.debug('[RELAY] Forwarded agent message to user via queue:' + JSON.stringify({ + execId, + type: msg.type + })) + } catch (error) { + logger.error('[RELAY] Failed to enqueue message for user', { + execId, + error: error.message + }) + } + } else if (session.user && session.user.readyState === WebSocket.OPEN) { + if (msg.type === MESSAGE_TYPES.STDOUT || msg.type === MESSAGE_TYPES.STDERR) { + if (msg.data && msg.data.length > 0) { + // Create MessagePack message for user + const userMsg = { + type: msg.type, + data: msg.data, + microserviceUuid: session.microserviceUuid, + execId: execId, + timestamp: Date.now() + } + // Encode and send as binary + const encoded = this.encodeMessage(userMsg) + session.user.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + + logger.debug('[RELAY] Forwarded agent message to user:' + JSON.stringify({ + execId, + type: msg.type, + encodedLength: encoded.length, + messageType: msg.type + })) + } + } else if (msg.type === MESSAGE_TYPES.CONTROL) { + session.user.send(data, { + binary: true, + compress: false, + mask: false, + fin: true + }) + } + } else { + logger.debug('[RELAY] User not available (cross-replica), message should be delivered via queue:' + JSON.stringify({ + execId, + userState: session.user ? session.user.readyState : 'N/A', + messageType: msg.type, + queueEnabled + })) + } + } catch (error) { + logger.error('[RELAY] Failed to process agent message:', error) + } + }) + } + + logger.info('[RELAY] Message forwarding setup complete for session:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + agentConnected: !!agent, + userConnected: !!user, + agentState: agent ? agent.readyState : 'N/A', + userState: user ? user.readyState : 'N/A' + })) + } + + async validateAgentConnection (token, microserviceUuid, transaction) { + try { + // Use AuthDecorator to validate the token and get the fog + let fog = {} + const req = { headers: { authorization: token }, transaction } + const handler = AuthDecorator.checkFogToken(async (req, fogObj) => { + fog = fogObj + return fogObj + }) + await handler(req) + + if (!fog) { + logger.error('Agent validation failed: Invalid agent token') + throw new WebSocketError(1008, 'Invalid agent token') + } + + // Verify microservice exists and belongs to this fog + const microservice = await MicroserviceManager.findOne({ uuid: microserviceUuid }, transaction) + if (!microservice || microservice.iofogUuid !== fog.uuid) { + logger.error('Agent validation failed: Microservice not found or not associated with this agent' + JSON.stringify({ + microserviceUuid, + fogUuid: fog.uuid, + found: !!microservice, + microserviceFogUuid: microservice ? microservice.iofogUuid : null + })) + throw new WebSocketError(1008, 'Microservice not found or not associated with this agent') + } + + return fog + } catch (error) { + logger.error('Agent validation error:' + JSON.stringify({ + error: error.message, + stack: error.stack, + microserviceUuid + })) + throw error // Propagate the original error + } + } + + async validateUserConnection (token, microserviceUuid, transaction) { + try { + // 1. Authenticate user first (Keycloak) - Direct token verification + let userRoles = [] + + // Extract Bearer token + const bearerToken = token.replace('Bearer ', '') + if (!bearerToken) { + throw new Errors.AuthenticationError('Missing or invalid authorization token') + } + + // Check if we're in development mode (mock Keycloak) + const isDevMode = process.env.SERVER_DEV_MODE || config.get('server.devMode', true) + const hasAuthConfig = this.isAuthConfigured() + + if (!hasAuthConfig && isDevMode) { + // Use mock roles for development + userRoles = ['SRE', 'Developer', 'Viewer'] + logger.debug('Using mock authentication for development mode') + } else { + // Use real Keycloak token verification + try { + // Create a grant from the access token + const grant = await keycloak.grantManager.createGrant({ + access_token: bearerToken + }) + + // Extract roles from the token - get client-specific roles + const clientId = process.env.KC_CLIENT || config.get('auth.client.id') + const resourceAccess = grant.access_token.content.resource_access + + if (resourceAccess && resourceAccess[clientId] && resourceAccess[clientId].roles) { + userRoles = resourceAccess[clientId].roles + } else { + // Fallback to realm roles if client roles not found + userRoles = grant.access_token.content.realm_access && grant.access_token.content.realm_access.roles + ? grant.access_token.content.realm_access.roles + : [] + } + + logger.debug('Token verification successful, user roles:' + JSON.stringify(userRoles)) + } catch (keycloakError) { + logger.error('Keycloak token verification failed:' + JSON.stringify({ + error: keycloakError.message, + stack: keycloakError.stack + })) + throw new Errors.AuthenticationError('Invalid or expired token') + } + } + + // Check if user has required roles + const hasRequiredRole = userRoles.some(role => ['SRE', 'Developer'].includes(role)) + if (!hasRequiredRole) { + throw new Errors.AuthenticationError('Insufficient permissions. Required roles: SRE for Node Exec or Developer for Microservice Exec') + } + + // 2. Only now check microservice, application, etc. + const microservice = await MicroserviceManager.findOne({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError('Microservice not found') + } + + const application = await ApplicationManager.findOne({ id: microservice.applicationId }, transaction) + if (!application) { + throw new Errors.NotFoundError('Application not found') + } + + const statusArr = await MicroserviceStatusManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + if (!statusArr || statusArr.length === 0) { + throw new Errors.NotFoundError('Microservice status not found') + } + const status = statusArr[0] + logger.debug('Microservice status check:' + JSON.stringify({ + status: status.status, + expectedStatus: microserviceState.RUNNING, + isEqual: status.status === microserviceState.RUNNING + })) + if (status.status !== microserviceState.RUNNING) { + throw new Errors.ValidationError('Microservice is not running') + } + + if (application.isSystem && !userRoles.includes('SRE')) { + throw new Errors.AuthenticationError('Only SRE can access system microservices') + } + // For non-system, SRE or Developer is already checked above + + // Check if microservice exec is enabled + if (!microservice.execEnabled) { + throw new Errors.ValidationError('Microservice exec is not enabled') + } + + const execStatusArr = await MicroserviceExecStatusManager.findAllExcludeFields({ microserviceUuid: microserviceUuid }, transaction) + if (!execStatusArr || execStatusArr.length === 0) { + throw new Errors.NotFoundError('Microservice exec status not found') + } + const execStatus = execStatusArr[0] + // logger.debug('Microservice exec status check:' + JSON.stringify({ + // status: execStatus.status, + // expectedStatus: microserviceExecState.ACTIVE, + // isEqual: execStatus.status === microserviceExecState.ACTIVE + // })) + if (execStatus.status === microserviceExecState.ACTIVE) { + throw new Errors.ValidationError('Microservice already has an active session') + } + + return { success: true } // Just indicate validation passed + } catch (error) { + logger.error('User connection validation failed:' + JSON.stringify({ error: error.message, stack: error.stack })) + throw error + } + } + + // Singleton instance + static getInstance () { + if (!WebSocketServer.instance) { + WebSocketServer.instance = new WebSocketServer() + } + return WebSocketServer.instance + } + + // Clean up session and close sockets + cleanupSession (execId, transaction) { + const session = this.sessionManager.getSession(execId) + if (!session) return + + // Clear any pending CLOSE timeout + const timeout = this.pendingCloseTimeouts.get(execId) + if (timeout) { + clearTimeout(timeout) + this.pendingCloseTimeouts.delete(execId) + logger.debug('[RELAY] Cleared pending CLOSE timeout during cleanup', { execId }) + } + + // Send CLOSE message to agent if it's still connected + if (session.agent && session.agent.readyState === WebSocket.OPEN) { + const closeMsg = { + type: MESSAGE_TYPES.CLOSE, + execId: execId, + microserviceUuid: session.microserviceUuid, + timestamp: Date.now(), + data: Buffer.from('Session closed') + } + + try { + const encoded = this.encodeMessage(closeMsg) + session.agent.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + logger.info('[RELAY] Sent CLOSE message to agent for execId=' + execId) + } catch (error) { + logger.error('[RELAY] Failed to send CLOSE message to agent:' + JSON.stringify({ + execId, + error: error.message, + stack: error.stack + })) + } + } + + // Close the connections (only if not already closed) + // Note: User connection may already be closed if user initiated the close + if (session.user && session.user.readyState === WebSocket.OPEN) { + session.user.close(1000, 'Session closed') + } + if (session.agent && session.agent.readyState === WebSocket.OPEN) { + session.agent.close(1000, 'Session closed') + } + + this.sessionManager.removeSession(execId, transaction) + logger.info('[RELAY] Session cleaned up for execId=' + execId) + this.queueService.cleanup(execId) + .catch(error => { + logger.warn('[RELAY] Failed to cleanup queue bridge during session cleanup', { + execId, + error: error.message + }) + }) + } + + // Utility to extract microserviceUuid from path + extractUuidFromPath (path) { + const match = path.match(/([a-f0-9-]{36})/i) + return match ? match[1] : null + } + + registerRoute (path, middleware) { + // Store the route handler + this.routes = this.routes || new Map() + this.routes.set(path, middleware) + + logger.info('Registered WebSocket route: ' + path) + } + + // Helper method for sending messages to agent + async sendMessageToAgent (agent, message, execId, microserviceUuid) { + try { + const encoded = this.encodeMessage(message) + const isQueueEnabled = this.queueService.shouldUseQueue(execId) + const messageType = typeof message.type === 'number' ? message.type : null + + if (isQueueEnabled) { + await this.queueService.publishToAgent(execId, encoded, { messageType }) + logger.debug('[RELAY] Queued message for agent via AMQP:' + JSON.stringify({ + execId, + microserviceUuid, + messageType: message.type, + encodedLength: encoded.length + })) + return true + } + + if (!agent || agent.readyState !== WebSocket.OPEN) { + logger.error('[RELAY] Cannot send message - agent not ready:' + JSON.stringify({ + execId, + microserviceUuid, + agentState: agent ? agent.readyState : 'N/A', + messageType: message.type + })) + return false + } + + agent.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + logger.debug('[RELAY] Message sent to agent:' + JSON.stringify({ + execId, + microserviceUuid, + messageType: message.type, + encodedLength: encoded.length + })) + return true + } catch (error) { + logger.error('[RELAY] Failed to send message to agent:' + JSON.stringify({ + execId, + microserviceUuid, + messageType: message.type, + error: error.message, + stack: error.stack + })) + return false + } + } + + attachPendingKeepAliveHandler (ws) { + if (!ws || ws._pendingKeepAliveHandler) { + return + } + ws._pendingKeepAliveHandler = (data, isBinary) => { + if (!isBinary) return + let msg + try { + msg = this.decodeMessage(Buffer.from(data)) + } catch (error) { + return + } + if (msg.type === MESSAGE_TYPES.CONTROL) { + const controlData = msg.data ? msg.data.toString() : '' + if (controlData === 'keepalive') { + this._sendKeepAliveResponse(ws, msg.execId || 'pending', msg.microserviceUuid || null) + } + } + } + ws.on('message', ws._pendingKeepAliveHandler) + ws.on('ping', () => { + if (ws.readyState === WebSocket.OPEN) { + try { + ws.pong() + } catch (error) { + logger.debug('[RELAY] Failed to send pong on pending connection', { error: error.message }) + } + } + }) + } + + detachPendingKeepAliveHandler (ws) { + if (ws && ws._pendingKeepAliveHandler) { + ws.removeListener('message', ws._pendingKeepAliveHandler) + ws._pendingKeepAliveHandler = null + } + } + + _sendKeepAliveResponse (ws, execId, microserviceUuid) { + try { + const keepAliveResponse = { + type: MESSAGE_TYPES.CONTROL, + data: Buffer.from('keepalive'), + microserviceUuid, + execId, + timestamp: Date.now() + } + const encoded = this.encodeMessage(keepAliveResponse) + ws.send(encoded, { + binary: true, + compress: false, + mask: false, + fin: true + }) + } catch (error) { + logger.debug('[RELAY] Failed to send keepalive response', { error: error.message }) + } + } + + // Helper method to check if auth is configured + isAuthConfigured () { + const requiredConfigs = [ + 'auth.realm', + 'auth.realmKey', + 'auth.url', + 'auth.client.id', + 'auth.client.secret' + ] + return requiredConfigs.every(configKey => { + const value = config.get(configKey) + return value !== undefined && value !== null && value !== '' + }) + } +} + +module.exports = WebSocketServer diff --git a/src/websocket/session-manager.js b/src/websocket/session-manager.js new file mode 100644 index 000000000..cc92bf145 --- /dev/null +++ b/src/websocket/session-manager.js @@ -0,0 +1,544 @@ +const WebSocket = require('ws') +const logger = require('../logger') +const Errors = require('../helpers/errors') +const MicroserviceManager = require('../data/managers/microservice-manager') +const MicroserviceExecStatusManager = require('../data/managers/microservice-exec-status-manager') +const { microserviceExecState } = require('../enums/microservice-state') + +class SessionManager { + constructor (config) { + if (!config || !config.session) { + const error = new Errors.ValidationError('Invalid session manager configuration') + logger.error('Failed to initialize SessionManager:' + error) + throw error + } + this.sessions = new Map() + this.pendingUsers = new Map() // Map> + this.pendingAgents = new Map() // Map> + this.userRetryTimers = new Map() // Map> + this.config = config + this.cleanupInterval = null + logger.info('SessionManager initialized with config:' + JSON.stringify({ + sessionTimeout: config.session.timeout, + maxConnections: config.session.maxConnections, + cleanupInterval: config.session.cleanupInterval + })) + } + + createSession (execId, microserviceUuid, agentWs, userWs, transaction) { + const session = { + execId, + microserviceUuid, + agent: agentWs, + user: userWs, + lastActivity: Date.now(), + transaction + } + this.sessions.set(execId, session) + logger.info('Session created:' + JSON.stringify({ + execId, + microserviceUuid, + agentConnected: !!agentWs, + userConnected: !!userWs + })) + return session + } + + getSession (execId) { + return this.sessions.get(execId) || null + } + + async removeSession (execId, transaction) { + const session = this.sessions.get(execId) + if (session) { + logger.info('Removing session:' + JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid + })) + this.sessions.delete(execId) + await MicroserviceExecStatusManager.update( + { microserviceUuid: session.microserviceUuid }, + { execSessionId: '', status: microserviceExecState.INACTIVE }, + transaction + ) + await MicroserviceManager.update({ uuid: session.microserviceUuid }, { execEnabled: false }, transaction) + } + } + + addPendingUser (microserviceUuid, userWs) { + if (!this.pendingUsers.has(microserviceUuid)) { + this.pendingUsers.set(microserviceUuid, new Map()) + } + const users = this.pendingUsers.get(microserviceUuid) + users.set(userWs, { timestamp: Date.now() }) + + logger.info('Added pending user:' + JSON.stringify({ + microserviceUuid, + pendingUserCount: users.size + })) + } + + async addPendingAgent (microserviceUuid, execId, agentWs, transaction) { + if (!this.pendingAgents.has(microserviceUuid)) { + this.pendingAgents.set(microserviceUuid, new Map()) + // await MicroserviceExecStatusManager.update( + // { microserviceUuid: microserviceUuid }, + // { execSessionId: execId, status: microserviceExecState.PENDING }, + // transaction + // ) + } + const agents = this.pendingAgents.get(microserviceUuid) + + // Check if agent with this execId already exists + if (agents.has(execId)) { + logger.warn('Agent with execId already exists in pending list:' + JSON.stringify({ + microserviceUuid, + execId, + existingAgentState: agents.get(execId).ws.readyState, + newAgentState: agentWs.readyState + })) + // Remove old agent if it's not in OPEN state + if (agents.get(execId).ws.readyState !== WebSocket.OPEN) { + agents.delete(execId) + } else { + return // Skip adding if we already have an active agent with this execId + } + } + + const agentInfo = { ws: agentWs, execId } + agents.set(execId, agentInfo) + logger.info('Added pending agent:' + JSON.stringify({ + microserviceUuid, + execId, + pendingAgentCount: agents.size, + agentState: agentWs.readyState + })) + } + + removePendingUser (microserviceUuid, userWs) { + if (this.pendingUsers.has(microserviceUuid)) { + const users = this.pendingUsers.get(microserviceUuid) + users.delete(userWs) + if (users.size === 0) { + this.pendingUsers.delete(microserviceUuid) + } + + // Clear retry timer when removing user + this.clearUserRetryTimer(microserviceUuid, userWs) + + logger.info('Removed pending user:' + JSON.stringify({ + microserviceUuid, + remainingUsers: users.size + })) + } + } + + removePendingAgent (microserviceUuid, agentWs) { + if (this.pendingAgents.has(microserviceUuid)) { + const agents = this.pendingAgents.get(microserviceUuid) + // Find and remove agent by WebSocket instance + for (const [execId, agentInfo] of agents.entries()) { + if (agentInfo.ws === agentWs) { + agents.delete(execId) + logger.info('Removed pending agent:' + JSON.stringify({ + microserviceUuid, + execId, + remainingAgents: agents.size + })) + break + } + } + + if (agents.size === 0) { + this.pendingAgents.delete(microserviceUuid) + } + } + } + + findPendingUserForExecId (microserviceUuid, execId) { + if (this.pendingUsers.has(microserviceUuid)) { + const users = this.pendingUsers.get(microserviceUuid) + // Find any available user (no execId matching needed) + for (const [userWs] of users.entries()) { + if (userWs.readyState === WebSocket.OPEN) { + return userWs + } + } + } + return null + } + + findPendingAgentForExecId (microserviceUuid, execId) { + if (this.pendingAgents.has(microserviceUuid)) { + const agents = this.pendingAgents.get(microserviceUuid) + const agentInfo = agents.get(execId) + if (agentInfo && agentInfo.ws.readyState === WebSocket.OPEN) { + return agentInfo.ws + } + } + return null + } + + async tryActivateSession (microserviceUuid, execId, newConnection, isAgent, transaction) { + let pendingUser = null + let pendingAgent = null + let session = null + + try { + if (isAgent) { + pendingUser = this.findPendingUserForExecId(microserviceUuid, execId) + if (pendingUser) { + // Atomic operation: remove user and create session + this.removePendingUser(microserviceUuid, pendingUser) + session = this.createSession(execId, microserviceUuid, newConnection, pendingUser, transaction) + logger.info('Session activated with agent first:' + JSON.stringify({ + execId, + microserviceUuid, + userConnected: !!pendingUser, + agentConnected: !!newConnection, + userState: pendingUser.readyState, + agentState: newConnection.readyState + })) + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: execId, status: microserviceExecState.ACTIVE }, + transaction + ) + } else { + await this.addPendingAgent(microserviceUuid, execId, newConnection, transaction) + logger.info('No pending user found for agent, added to pending list:' + JSON.stringify({ + execId, + microserviceUuid, + agentState: newConnection.readyState + })) + } + } else { + pendingAgent = this.findPendingAgentForExecId(microserviceUuid, execId) + if (pendingAgent) { + // Atomic operation: remove agent and create session + this.removePendingAgent(microserviceUuid, pendingAgent) + session = this.createSession(execId, microserviceUuid, pendingAgent, newConnection, transaction) + logger.info('Session activated with user first:' + JSON.stringify({ + execId, + microserviceUuid, + userConnected: !!newConnection, + agentConnected: !!pendingAgent, + userState: newConnection.readyState, + agentState: pendingAgent.readyState + })) + await MicroserviceExecStatusManager.update( + { microserviceUuid: microserviceUuid }, + { execSessionId: execId, status: microserviceExecState.ACTIVE }, + transaction + ) + } else { + this.addPendingUser(microserviceUuid, newConnection) + logger.info('No pending agent found for user, added to pending list:' + JSON.stringify({ + execId, + microserviceUuid, + userState: newConnection.readyState + })) + } + } + } catch (error) { + logger.error('Failed to activate session:' + JSON.stringify({ + error: error.message, + execId, + microserviceUuid, + isAgent, + userState: newConnection.readyState + })) + // Cleanup any partial state + if (session) { + await this.removeSession(execId, transaction) + } + throw error + } + + return session + } + + logSessionState () { + logger.info('--- WebSocket SessionManager State ---') + logger.info('Active sessions:') + for (const [execId, session] of this.sessions) { + logger.info(JSON.stringify({ + execId, + microserviceUuid: session.microserviceUuid, + agentConnected: !!session.agent, + userConnected: !!session.user, + lastActivity: new Date(session.lastActivity).toISOString(), + agentState: session.agent ? session.agent.readyState : 'N/A', + userState: session.user ? session.user.readyState : 'N/A' + })) + } + logger.info('Pending users:') + for (const [microserviceUuid, users] of this.pendingUsers) { + logger.info(JSON.stringify({ + microserviceUuid, + count: users.size, + users: Array.from(users.entries()).map(([ws, info]) => ({ + timestamp: new Date(info.timestamp).toISOString(), + readyState: ws.readyState + })) + })) + } + logger.info('Pending agents:') + for (const [microserviceUuid, agents] of this.pendingAgents) { + logger.info(JSON.stringify({ + microserviceUuid, + count: agents.size, + execIds: Array.from(agents.keys()) + })) + } + logger.info('--------------------------------------') + } + + assignAgentToSession (execId, agentWs) { + const session = this.getSession(execId) + if (session) { + session.agent = agentWs + session.lastActivity = Date.now() + } + } + + assignUserToSession (execId, userWs) { + const session = this.getSession(execId) + if (session) { + session.user = userWs + session.lastActivity = Date.now() + } + } + + addConnection (sessionId, ws) { + try { + const session = this.getSession(sessionId) + session.connections.add(ws) + session.lastActivity = Date.now() + logger.info('Connection added to session' + JSON.stringify({ + sessionId, + connectionCount: session.connections.size + })) + } catch (error) { + logger.error('Failed to add connection:' + error) + throw error + } + } + + removeConnection (sessionId, ws) { + try { + const session = this.getSession(sessionId) + session.connections.delete(ws) + if (session.connections.size === 0) { + session.lastActivity = Date.now() + logger.info('Last connection removed from session' + JSON.stringify({ sessionId })) + } else { + logger.debug('Connection removed from session' + JSON.stringify({ + sessionId, + remainingConnections: session.connections.size + })) + } + } catch (error) { + logger.error('Failed to remove connection:' + error) + throw error + } + } + + handleReconnection (sessionId, ws) { + try { + const session = this.getSession(sessionId) + if (session.reconnectAttempts < this.config.maxReconnectAttempts) { + session.reconnectAttempts++ + this.addConnection(sessionId, ws) + logger.info('Reconnection successful' + JSON.stringify({ + sessionId, + attempt: session.reconnectAttempts + })) + return true + } else { + const error = new Errors.ValidationError('Max reconnection attempts reached') + logger.warn('Max reconnection attempts reached' + JSON.stringify({ + sessionId, + maxAttempts: this.config.maxReconnectAttempts, + error: error.message + })) + throw error + } + } catch (error) { + logger.error('Reconnection failed:' + error) + throw error + } + } + + startCleanup () { + if (this.cleanupInterval) { + logger.debug('Cleanup interval already running') + return + } + logger.info('Starting session cleanup service with interval: ' + this.config.session.cleanupInterval + 'ms') + this.cleanupInterval = setInterval(() => { + const now = Date.now() + let cleanedCount = 0 + logger.debug('Running session cleanup cycle') + for (const [sessionId, session] of this.sessions) { + if (now - session.lastActivity > this.config.session.timeout) { + this.cleanupSession(sessionId) + cleanedCount++ + } + } + if (cleanedCount > 0) { + logger.info('Session cleanup completed' + JSON.stringify({ cleanedCount })) + } + // Log session state after cleanup + this.logSessionState() + }, this.config.session.cleanupInterval) + } + + cleanupSession (sessionId) { + try { + const session = this.getSession(sessionId) + logger.info('Cleaning up session' + JSON.stringify({ + sessionId, + type: session.type, + connectionCount: session.connections.size + })) + for (const ws of session.connections) { + ws.close(1000, 'Session timeout') + } + this.sessions.delete(sessionId) + logger.debug('Session cleanup completed' + JSON.stringify({ sessionId })) + } catch (error) { + logger.error('Failed to cleanup session:' + error) + throw error + } + } + + stopCleanup () { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval) + logger.info('Session cleanup service stopped') + } + } + + getActiveConnections (sessionId) { + try { + const session = this.getSession(sessionId) + const count = session.connections.size + logger.debug('Getting active connections' + JSON.stringify({ sessionId, count })) + return count + } catch (error) { + logger.error('Failed to get active connections:' + error) + throw error + } + } + + broadcastToSession (sessionId, message) { + try { + const session = this.getSession(sessionId) + const messageStr = JSON.stringify(message) + let sentCount = 0 + for (const ws of session.connections) { + if (ws.readyState === WebSocket.OPEN) { + ws.send(messageStr) + sentCount++ + } + } + logger.debug('Broadcast message to session' + JSON.stringify({ + sessionId, + recipients: sentCount, + totalConnections: session.connections.size + })) + } catch (error) { + logger.error('Failed to broadcast message:' + error) + throw error + } + } + + bufferMessage (sessionId, message) { + try { + const session = this.getSession(sessionId) + session.buffer.push(message) + if (session.buffer.length > this.config.maxBufferSize) { + session.buffer.shift() // Remove oldest message + logger.debug('Buffer size limit reached, removed oldest message' + JSON.stringify({ + sessionId, + bufferSize: session.buffer.length + })) + } + } catch (error) { + logger.error('Failed to buffer message:' + error) + throw error + } + } + + getBufferedMessages (sessionId) { + try { + const session = this.getSession(sessionId) + const messages = session.buffer + logger.debug('Retrieved buffered messages' + JSON.stringify({ + sessionId, + messageCount: messages.length + })) + return messages + } catch (error) { + logger.error('Failed to get buffered messages:' + error) + throw error + } + } + + clearBuffer (sessionId) { + try { + const session = this.getSession(sessionId) + const count = session.buffer.length + session.buffer = [] + logger.info('Cleared message buffer' + JSON.stringify({ sessionId, clearedCount: count })) + } catch (error) { + logger.error('Failed to clear buffer:' + error) + throw error + } + } + + getPendingAgentExecIds (microserviceUuid) { + if (this.pendingAgents.has(microserviceUuid)) { + const agents = this.pendingAgents.get(microserviceUuid) + return Array.from(agents.keys()) + } + return [] + } + + isUserStillPending (microserviceUuid, userWs) { + if (this.pendingUsers.has(microserviceUuid)) { + const users = this.pendingUsers.get(microserviceUuid) + return users.has(userWs) + } + return false + } + + setUserRetryTimer (microserviceUuid, userWs, timer) { + if (!this.userRetryTimers.has(microserviceUuid)) { + this.userRetryTimers.set(microserviceUuid, new Map()) + } + const timers = this.userRetryTimers.get(microserviceUuid) + timers.set(userWs, timer) + } + + getUserRetryTimer (microserviceUuid, userWs) { + if (this.userRetryTimers.has(microserviceUuid)) { + const timers = this.userRetryTimers.get(microserviceUuid) + return timers.get(userWs) + } + return null + } + + clearUserRetryTimer (microserviceUuid, userWs) { + if (this.userRetryTimers.has(microserviceUuid)) { + const timers = this.userRetryTimers.get(microserviceUuid) + timers.delete(userWs) + if (timers.size === 0) { + this.userRetryTimers.delete(microserviceUuid) + } + } + } +} + +module.exports = SessionManager diff --git a/swagger.js b/swagger.js new file mode 100644 index 000000000..4240fdbba --- /dev/null +++ b/swagger.js @@ -0,0 +1,41 @@ +// swagger.js +const swaggerJsDoc = require('swagger-jsdoc') + +// Import all schemas +const schemas = require('./src/schemas') + +const swaggerOptions = { + swaggerDefinition: { + openapi: '3.0.0', + info: { + title: 'Datasance PoT Controller REST API Documentation', + version: '3.5.0', + description: 'Datasance PoT Controller REST API Documentation' + }, + servers: [ + { + url: 'http://localhost:51121/api/v3' + } + ], + components: { + securitySchemes: { + authToken: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + description: 'JWT token for authentication (user or agent)' + } + }, + schemas: schemas + }, + security: [ + { + authToken: [] + } + ] + }, + apis: ['./src/routes/*.js'] +} + +const swaggerDocs = swaggerJsDoc(swaggerOptions) +module.exports = swaggerDocs diff --git a/test/OTEL/README.md b/test/OTEL/README.md new file mode 100644 index 000000000..777250873 --- /dev/null +++ b/test/OTEL/README.md @@ -0,0 +1,50 @@ +# OpenTelemetry Test Setup + +This directory contains the necessary configuration files to test OpenTelemetry integration with Jaeger, Prometheus, and Grafana. + +## Prerequisites + +- Docker +- Docker Compose + +## Setup + +1. Start the observability stack: +```bash +docker-compose up -d +``` + +2. Access the UIs: +- Jaeger UI: http://localhost:16686 +- Prometheus: http://localhost:9090 +- Grafana: http://localhost:3000 (login with admin/admin) + +## Testing Your Application + +To test your application with this setup: + +1. Set the environment variables: +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +export OTEL_SERVICE_NAME=your-service-name +``` + +2. Run your application with OpenTelemetry: +```bash +sudo -E node -r dotenv/config src/server.js +``` + +## Verifying the Setup + +1. Make some requests to your application +2. Open Jaeger UI (http://localhost:16686) +3. Select your service from the dropdown +4. Click "Find Traces" +5. You should see traces from your application + +## Cleanup + +To stop and remove all containers: +```bash +docker-compose down -v +``` \ No newline at end of file diff --git a/test/OTEL/docker-compose.yml b/test/OTEL/docker-compose.yml new file mode 100644 index 000000000..6db625c40 --- /dev/null +++ b/test/OTEL/docker-compose.yml @@ -0,0 +1,65 @@ +version: '3.8' + +services: + # Jaeger for trace visualization + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - "16686:16686" # Jaeger UI + - "14250:14250" # gRPC + environment: + - COLLECTOR_OTLP_ENABLED=true + - METRICS_STORAGE_TYPE=prometheus + - PROMETHEUS_SERVER_URL=http://prometheus:9090 + networks: + - telemetry-network + + # OpenTelemetry Collector + otel-collector: + image: otel/opentelemetry-collector:latest + command: ["--config=/etc/otel-collector-config.yaml"] + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + ports: + - "4317:4317" # OTLP gRPC + - "4318:4318" # OTLP HTTP + - "8888:8888" # Prometheus metrics exposed by the collector + - "8889:8889" # Prometheus exporter + - "13133:13133" # health_check extension + - "55679:55679" # zpages extension + depends_on: + - jaeger + - prometheus + networks: + - telemetry-network + + # Prometheus for metrics + prometheus: + image: prom/prometheus:latest + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + networks: + - telemetry-network + + # Grafana for visualization + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + volumes: + - grafana-storage:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + depends_on: + - prometheus + networks: + - telemetry-network + +networks: + telemetry-network: + driver: bridge + +volumes: + grafana-storage: \ No newline at end of file diff --git a/test/OTEL/otel-collector-config.yaml b/test/OTEL/otel-collector-config.yaml new file mode 100644 index 000000000..5c5b03abc --- /dev/null +++ b/test/OTEL/otel-collector-config.yaml @@ -0,0 +1,39 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + grpc: + endpoint: 0.0.0.0:4317 + +processors: + batch: + timeout: 1s + send_batch_size: 1024 + +exporters: + otlp: + endpoint: jaeger:4317 + tls: + insecure: true + prometheus: + endpoint: "0.0.0.0:8889" + namespace: "controller" + resource_to_telemetry_conversion: + enabled: true + debug: + verbosity: detailed + +service: + telemetry: + logs: + level: debug + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp, debug] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp, prometheus, debug] \ No newline at end of file diff --git a/test/OTEL/prometheus.yml b/test/OTEL/prometheus.yml new file mode 100644 index 000000000..de10cdb21 --- /dev/null +++ b/test/OTEL/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 5s + static_configs: + - targets: ['otel-collector:8889'] \ No newline at end of file diff --git a/test/application-template-update.yaml b/test/application-template-update.yaml index d4a55b54c..5ae319646 100644 --- a/test/application-template-update.yaml +++ b/test/application-template-update.yaml @@ -1,5 +1,5 @@ kind: ApplicationTemplate -apiVersion: iofog.org/v3 +apiVersion: datasance.com/v3 metadata: name: 'app-template-from-yaml' spec: diff --git a/test/application-template.yaml b/test/application-template.yaml index 2c2b6ce03..15ac7348d 100644 --- a/test/application-template.yaml +++ b/test/application-template.yaml @@ -1,5 +1,5 @@ kind: ApplicationTemplate -apiVersion: iofog.org/v3 +apiVersion: datasance.com/v3 metadata: name: 'app-template-from-yaml' spec: diff --git a/test/application-test.yaml b/test/application-test.yaml index 7d8bf35ce..ae1f589dd 100644 --- a/test/application-test.yaml +++ b/test/application-test.yaml @@ -1,5 +1,5 @@ kind: Application -apiVersion: iofog.org/v3 +apiVersion: datasance.com/v3 metadata: name: "healthcare-wearable-flow" spec: @@ -9,8 +9,8 @@ spec: agent: name: "{% assign agent = \"\" | findAgent | first %}{{ agent.name }}" images: - arm: "edgeworx/healthcare-heart-rate:arm-v1" - x86: "edgeworx/healthcare-heart-rate:x86-v1" + arm: "edgeworx/healthcare-heart-rate:arm-v3" + x86: "edgeworx/healthcare-heart-rate:x86-v3" container: rootHostAccess: false ports: [] diff --git a/test/application-update-test.yaml b/test/application-update-test.yaml index 2f1b3d3c6..0c7c35b8a 100644 --- a/test/application-update-test.yaml +++ b/test/application-update-test.yaml @@ -1,5 +1,5 @@ kind: Application -apiVersion: iofog.org/v3 +apiVersion: datasance.com/v3 metadata: name: "healthcare-wearable-flow" spec: @@ -9,8 +9,8 @@ spec: agent: name: "{% assign agent = \"\" | findAgent | first %}{{ agent.name }}" images: - arm: "edgeworx/healthcare-heart-rate:arm-v1" - x86: "edgeworx/healthcare-heart-rate:x86-v1" + arm: "edgeworx/healthcare-heart-rate:arm-v3" + x86: "edgeworx/healthcare-heart-rate:x86-v3" container: rootHostAccess: false ports: [] diff --git a/test/postman_collection.json b/test/postman_collection.json index fae782fa3..2a975f602 100644 --- a/test/postman_collection.json +++ b/test/postman_collection.json @@ -20,7 +20,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -36,7 +36,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"xxxx-xxxx-xxxx-xxxx\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -282,7 +282,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.firstName && data.lastName && data.email;" + "tests[\"Response validation passed\"] = data.firstName && data.lastName && data.email && data.subscriptionKey;" ], "type": "text/javascript" } @@ -362,7 +362,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.firstName && data.lastName && data.email;" + "tests[\"Response validation passed\"] = data.firstName && data.lastName && data.email && data.subscriptionKey;" ], "type": "text/javascript" } @@ -382,7 +382,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"Saeid\",\n \"lastName\": \"Rezaei\"\n}" + "raw": "{\n \"firstName\": \"Saeid\",\n \"lastName\": \"Rezaei\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/profile", @@ -700,7 +700,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -716,7 +716,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -1907,7 +1907,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -1923,7 +1923,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -3343,7 +3343,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -3359,7 +3359,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n }" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -4053,9 +4053,9 @@ "tests[\"Response validation passed\"] = data.hasOwnProperty('uuid') && data.name;", "", "if (tests[\"Response validation passed\"]) {", - " const env1 = data.env.find(e => e.key === \"KEY1\")", + " const env3 = data.env.find(e => e.key === \"KEY1\")", " const env2 = data.env.find(e => e.key === \"KEY2\")", - " tests[\"Env variable were parsed properly\"] = env1.value === \"12345\" && env2.value === \"test42\";", + " tests[\"Env variable were parsed properly\"] = env3.value === \"12345\" && env2.value === \"test42\";", "}" ], "type": "text/javascript" @@ -4398,7 +4398,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -4414,7 +4414,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -4812,8 +4812,8 @@ "", "tests[\"Response returns microservice errorMessage\"] = data.microservices[0].status.hasOwnProperty('errorMessage');", "", - "tests[\"msv1 gets env application name\"]=data.microservices[0].env[0].value === pm.globals.get(\"application-msvc-name\").toUpperCase()", - "tests[\"msv1 sets env value from another\"]=data.microservices[0].env[3].value === data.microservices[0].env[2].value", + "tests[\"msv3 gets env application name\"]=data.microservices[0].env[0].value === pm.globals.get(\"application-msvc-name\").toUpperCase()", + "tests[\"msv3 sets env value from another\"]=data.microservices[0].env[3].value === data.microservices[0].env[2].value", "", "tests[\"msv2 gets env application name\"]=data.microservices[1].env[0].value === pm.globals.get(\"application-msvc-name\").toUpperCase()", "tests[\"msv2 sets env value from env service msvc1\"]=data.microservices[1].env[1].value === data.microservices[0].env[1].value", @@ -5530,7 +5530,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -5546,7 +5546,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -5965,7 +5965,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -5981,7 +5981,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -7792,7 +7792,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -7808,7 +7808,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -8192,7 +8192,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -8208,7 +8208,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -8682,7 +8682,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -8698,7 +8698,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -10513,7 +10513,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -10529,7 +10529,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -11401,7 +11401,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -11417,7 +11417,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -12351,7 +12351,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -12367,7 +12367,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", @@ -12698,7 +12698,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.subscriptionKey && data.hasOwnProperty('emailActivated');" ], "type": "text/javascript" } @@ -12714,7 +12714,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\",\n \"subscriptionKey\": \"XXXX-XXXX-XXXX-XXXX\"\n}" }, "url": { "raw": "{{host}}/api/v3/user/signup", diff --git a/test/src/services/microservices-service.test.js b/test/src/services/microservices-service.test.js index cfe2d9ef9..a6744122a 100644 --- a/test/src/services/microservices-service.test.js +++ b/test/src/services/microservices-service.test.js @@ -20,7 +20,6 @@ const MicroserviceEnvManager = require('../../../src/data/managers/microservice- const MicroserviceArgManager = require('../../../src/data/managers/microservice-arg-manager') const RegistryManager = require('../../../src/data/managers/registry-manager') const Op = require('sequelize').Op -const MicroservicePublicModeManager = require('../../../src/data/managers/microservice-public-mode-manager') const MicroservicePublicPortManager = require('../../../src/data/managers/microservice-public-port-manager') const ioFogManager = require('../../../src/data/managers/iofog-manager') const ioFogService = require('../../../src/services/iofog-service') @@ -118,7 +117,6 @@ describe('Microservices Service', () => { $sandbox.stub(MicroserviceExtraHostManager, 'findAll').returns($findExtraHostsResponse) $sandbox.stub(MicroserviceEnvManager, 'findAllExcludeFields').returns($envResponse) $sandbox.stub(MicroserviceArgManager, 'findAllExcludeFields').returns($cmdResponse) - $sandbox.stub(MicroservicePublicModeManager, 'findAll').returns($publicModeResponse) $sandbox.stub(CatalogItemImageManager, 'findAll').returns($imgResponse) $sandbox.stub(MicroserviceStatusManager, 'findAllExcludeFields').returns($statusResponse) $sandbox.stub(ApplicationManager, 'findOne').returns(Promise.resolve(application)) @@ -198,7 +196,6 @@ describe('Microservices Service', () => { $sandbox.stub(MicroserviceExtraHostManager, 'findAll').returns($findExtraHostsResponse) $sandbox.stub(MicroserviceEnvManager, 'findAllExcludeFields').returns($envResponse) $sandbox.stub(MicroserviceArgManager, 'findAllExcludeFields').returns($cmdResponse) - $sandbox.stub(MicroservicePublicModeManager, 'findAll').returns($publicModeResponse) $sandbox.stub(CatalogItemImageManager, 'findAll').returns($imgResponse) $sandbox.stub(MicroserviceStatusManager, 'findAllExcludeFields').returns($statusResponse) $sandbox.stub(ApplicationManager, 'findOne').returns(Promise.resolve(application)) diff --git a/test/src/template/app.yml b/test/src/template/app.yml index 778dd9699..756d62eeb 100755 --- a/test/src/template/app.yml +++ b/test/src/template/app.yml @@ -1,6 +1,6 @@ --- -apiVersion: iofog.org/v2 +apiVersion: datasance.com/v3 kind: Application metadata: name: edai-smartbuilding-rules-engine diff --git a/test/src/template/simple.yml b/test/src/template/simple.yml index 8b5dbe9d5..a271b3855 100755 --- a/test/src/template/simple.yml +++ b/test/src/template/simple.yml @@ -1,6 +1,6 @@ --- -apiVersion: iofog.org/v2 +apiVersion: datasance.com/v3 kind: Application metadata: name: edai-smartbuilding-rules-engine