diff --git a/.config/.editorconfig b/.config/.editorconfig new file mode 100644 index 0000000..8841bc6 --- /dev/null +++ b/.config/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/.config/ansible-lint.yml b/.config/ansible-lint.yml new file mode 100644 index 0000000..b1190e2 --- /dev/null +++ b/.config/ansible-lint.yml @@ -0,0 +1,13 @@ +--- +# check this website for detailed configuration options: +# https://ansible-lint.readthedocs.io/configuring/#ansible-lint-configuration + +profile: shared + +exclude_paths: + - "../.github" + - "../.git" + - "../**/docker-compose.yaml" + +warn_list: + - yaml[line-length] diff --git a/.github/actions/config_cli_tools/action.yaml b/.github/actions/config_cli_tools/action.yaml new file mode 100644 index 0000000..bd1c0b8 --- /dev/null +++ b/.github/actions/config_cli_tools/action.yaml @@ -0,0 +1,24 @@ +name: 'Configure CLI tools for CI/CD' +description: 'Setup CI/CD tools and authentication' +inputs: + google_workload_identity_provider: + required: true + description: "Google workload identity provider, created in `deploy/setup.sh`." + google_service_account: + required: true + description: "Google service account email, created in `deploy/setup.sh`." +runs: + using: "composite" + steps: + - uses: google-github-actions/auth@v1 + with: + workload_identity_provider: ${{ inputs.google_workload_identity_provider }} + service_account: ${{ inputs.google_service_account }} + - uses: google-github-actions/setup-gcloud@v1 + - uses: giantswarm/install-binary-action@v1 + with: + binary: 'atlas' + version: '1.4.0' + smoke_test: "${binary} --version" + tarball_binary_path: "*/bin/${binary}" + download_url: 'https://fastdl.mongodb.org/mongocli/mongodb-atlas-cli_${version}_linux_x86_64.tar.gz' diff --git a/.github/actions/config_docker/action.yaml b/.github/actions/config_docker/action.yaml new file mode 100644 index 0000000..f35893e --- /dev/null +++ b/.github/actions/config_docker/action.yaml @@ -0,0 +1,28 @@ +name: 'Configure Docker tools' +description: 'Setup Docker tools and authentication' +inputs: + google_workload_identity_provider: + required: true + description: "Google workload identity provider, created in `deploy/setup.sh`." + google_service_account: + required: true + description: "Google service account email, created in `deploy/setup.sh`." + container_registry: + required: true + description: "Container registry to login to" +runs: + using: "composite" + steps: + - uses: google-github-actions/auth@v1 + id: google_auth + with: + token_format: 'access_token' + workload_identity_provider: ${{ inputs.google_workload_identity_provider }} + service_account: ${{ inputs.google_service_account }} + access_token_lifetime: 300s + - uses: docker/login-action@v2 + with: + registry: ${{ inputs.container_registry }} + username: oauth2accesstoken + password: ${{ steps.google_auth.outputs.access_token }} + - uses: docker/setup-buildx-action@v2 diff --git a/.github/package.json b/.github/package.json deleted file mode 100644 index 8ec5e98..0000000 --- a/.github/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "serverless-personal-finance", - "description": "Personal finance application build on serverless cloud functions. Keeping running cost at a minimum while using high end hosting." -} \ No newline at end of file diff --git a/.github/project_matrix.sh b/.github/project_matrix.sh new file mode 100755 index 0000000..80eb645 --- /dev/null +++ b/.github/project_matrix.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$(realpath "$0")")/.."; + +function isFunction() { + PROJECT_NAME=$1 + [[ ! "${PROJECT_NAME}" =~ .Tests$ ]] && [[ "${PROJECT_NAME}" =~ ^App.Function ]] +} + +function isDocker() { + PROJECT_NAME=$1 + [[ -f "${PROJECT_DIRECTORY}/Dockerfile" ]] +} + +function isTest() { + PROJECT_NAME=$1 + [[ "${PROJECT_NAME}" =~ \.Tests$ && "${PROJECT_NAME}" != "App.Lib.Tests" ]] +} + +function isTypescript() { + PROJECT_NAME=$1 + [[ -f "${PROJECT_DIRECTORY}/tsconfig.json" ]] +} + + +RESULT_PROJECTS=() +PROJECT_DIRECTORIES=( $(find . -maxdepth 1 -type d) ) +for PROJECT_DIRECTORY in "${PROJECT_DIRECTORIES[@]}" +do + PROJECT_NAME=$(basename "${PROJECT_DIRECTORY}") + + case $1 in + functions) + if isFunction "${PROJECT_NAME}"; then + RESULT_PROJECTS+=($PROJECT_NAME) + fi + ;; + docker) + if isDocker "${PROJECT_NAME}"; then + RESULT_PROJECTS+=($PROJECT_NAME) + fi + ;; + tests) + if isTest "${PROJECT_NAME}"; then + RESULT_PROJECTS+=($PROJECT_NAME) + fi + ;; + typescript) + if isTypescript "${PROJECT_NAME}"; then + RESULT_PROJECTS+=($PROJECT_NAME) + fi + ;; + *) + echo "Unknown project type ${1}" + echo "Usage: project_matrix.sh {functions|tests|typescript|docker} [--json]" + exit 1 + ;; + esac +done + +if [[ "${2}" == "--json" ]]; then + jq --compact-output --null-input '$ARGS.positional' --args -- "${RESULT_PROJECTS[@]}" +else + for PROJECT in "${RESULT_PROJECTS[@]}" + do + echo "${PROJECT}" + done +fi \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..3d281d8 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,56 @@ +name: Build +on: + workflow_call: + inputs: + environment: + required: true + type: string + tag: + required: true + type: string + secrets: + GOOGLE_WORKLOAD_IDENTITY_PROVIDER: + required: true + GOOGLE_SERVICE_ACCOUNT_EMAIL: + required: true + GOOGLE_PROJECT_ID: + required: true + GOOGLE_REGION: + required: true + +jobs: + project_matrix: + uses: ./.github/workflows/project_matrix.yaml + + build_images: + runs-on: ubuntu-latest + needs: [project_matrix] + strategy: + matrix: + project: ${{ fromJson(needs.project_matrix.outputs.docker) }} + permissions: + contents: 'read' + id-token: 'write' + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/config_docker + with: + google_workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} + google_service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + container_registry: ${{ secrets.GOOGLE_REGION }}-docker.pkg.dev + - uses: docker/metadata-action@v4 + id: meta + with: + images: ${{ secrets.GOOGLE_REGION }}-docker.pkg.dev/${{ secrets.GOOGLE_PROJECT_ID }}/docker/${{ inputs.environment }}/${{ matrix.project }} + tags: ${{ inputs.tag }} + - uses: docker/build-push-action@v3 + with: + context: . + file: '${{ matrix.project }}/Dockerfile' + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + # @see https://github.com/docker/buildx/issues/1533 + provenance: false diff --git a/.github/workflows/delete.yaml b/.github/workflows/delete.yaml new file mode 100644 index 0000000..68dfb1e --- /dev/null +++ b/.github/workflows/delete.yaml @@ -0,0 +1,74 @@ +name: Cleanup +on: + workflow_call: + inputs: + environment: + required: true + type: string + + secrets: + GOOGLE_WORKLOAD_IDENTITY_PROVIDER: + required: true + GOOGLE_SERVICE_ACCOUNT_EMAIL: + required: true + GOOGLE_PROJECT_ID: + required: true + GOOGLE_REGION: + required: true + SENTRY_DSN: + required: true + CLOUDFLARE_API_TOKEN: + required: true + CLOUDFLARE_ACCOUNT_ID: + required: true + MONGODB_ATLAS_PUBLIC_KEY: + required: true + MONGODB_ATLAS_PRIVATE_KEY: + required: true + MONGODB_ATLAS_PROJECT_ID: + required: true + +jobs: + delete_containers: + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/config_docker + with: + google_workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} + google_service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + container_registry: ${{ secrets.GOOGLE_REGION }}-docker.pkg.dev + - uses: docker://europe-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli + with: + args: >- + -repo=${{ secrets.GOOGLE_REGION }}-docker.pkg.dev/${{ secrets.GOOGLE_PROJECT_ID }}/docker/${{ inputs.environment }} + -tag-filter-all='.*' + -recursive=true + + delete_resources: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/config_cli_tools + with: + google_workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} + google_service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + - run: ./deploy/run-ansible.sh src/delete.yml + env: + APP_TAG: ${{ inputs.tag }} + APP_ENVIRONMENT: ${{ inputs.environment }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + GOOGLE_REGION: ${{ secrets.GOOGLE_REGION }} + GOOGLE_PROJECT_ID: ${{ secrets.GOOGLE_PROJECT_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + MONGODB_ATLAS_PROJECT_ID: ${{ secrets.MONGODB_ATLAS_PROJECT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} \ No newline at end of file diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9a87cf8..2739d2a 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,104 +1,57 @@ name: Deploy + on: - push: - branches: - - "main" - - "beta" - - "alpha" + workflow_call: + inputs: + environment: + required: true + type: string + tag: + required: true + type: string + secrets: + GOOGLE_WORKLOAD_IDENTITY_PROVIDER: + required: true + GOOGLE_SERVICE_ACCOUNT_EMAIL: + required: true + GOOGLE_PROJECT_ID: + required: true + GOOGLE_REGION: + required: true + SENTRY_DSN: + required: true + CLOUDFLARE_API_TOKEN: + required: true + CLOUDFLARE_ACCOUNT_ID: + required: true + MONGODB_ATLAS_PUBLIC_KEY: + required: true + MONGODB_ATLAS_PRIVATE_KEY: + required: true + MONGODB_ATLAS_PROJECT_ID: + required: true jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - project: - - name: 'App.Function.Banktransaction.Import' - service: 'function-banktransaction-import' - dotnet-version: '7.0' - permissions: - contents: 'read' - id-token: 'write' - steps: - - uses: actions/checkout@v3 - - uses: google-github-actions/auth@v0 - id: google_auth - with: - token_format: 'access_token' - workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT }} - - uses: docker/login-action@v2 - with: - registry: gcr.io - username: oauth2accesstoken - password: ${{ steps.google_auth.outputs.access_token }} - - run: echo "slug=$(echo ${GITHUB_SHA} | cut -c1-7)" >> $GITHUB_ENV - - uses: docker/setup-buildx-action@v2 - - uses: docker/build-push-action@v3 - with: - context: . - file: '${{ matrix.project.name }}/Dockerfile' - push: true - build-args: DOTNET_VERSION=${{ matrix.project.dotnet-version }} - tags: gcr.io/${{ secrets.GOOGLE_PROJECT_ID }}/${{ matrix.project.service }}:${{env.slug}} - cache-from: type=gha - cache-to: type=gha,mode=max - deploy: runs-on: ubuntu-latest - needs: - - build permissions: - contents: 'read' - id-token: 'write' - strategy: - matrix: - project: - - 'App.Function.Banktransaction.Import' - - 'App.Function.Integration.Ynab' - region: [ 'europe-central2' ] + contents: read + id-token: write steps: - uses: actions/checkout@v3 - - uses: google-github-actions/auth@v0 - with: - workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT }} - # Deploy function - - run: echo "slug=$(echo ${GITHUB_SHA} | cut -c1-7)" >> $GITHUB_ENV - - uses: actions/github-script@v6 - id: environment + - uses: ./.github/actions/config_cli_tools with: - script: | - if (context.ref.includes('beta')) { - return 'Staging'; - } - return 'Production'; - result-encoding: string - - run: echo "environment_lowercase=$(echo ${{ steps.environment.outputs.result }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - - uses: mshick/fast-envsubst@v1 + google_workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} + google_service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + - run: ./deploy/run-ansible.sh src/deploy.yml env: - SERVICE: ${{ matrix.project }} - DEPLOY_ENV: ${{ env.environment_lowercase }} - DOTNET_ENVIRONMENT: ${{ steps.environment.outputs.result }} - IMAGE: gcr.io/${{ secrets.GOOGLE_PROJECT_ID }}/${{ matrix.project }}:${{env.slug}} - Database__ConnectionString: ${{ secrets.DATABASE__CONNECTIONSTRING }} - Database__DatabaseName: ${{ steps.environment.outputs.result }} - Sentry__Dsn: ${{ secrets.SENTRY__DSN }} - with: - in-file: ${{ matrix.project }}/service.yaml - out-file: ${{ matrix.project }}/service.deploy.yaml - - run: cat ${{ matrix.project }}/service.deploy.yaml - - uses: google-github-actions/deploy-cloudrun@v0 - id: cloudrun - with: - metadata: ${{ matrix.project }}/service.deploy.yaml - region: ${{ matrix.region }} - # Deploy infra (terraform) - - uses: hashicorp/setup-terraform@v2 - - run: | - test -d ${{ matrix.project }}/terraform || exit 0 - cd ${{ matrix.project }}/terraform - terraform init - terraform plan -out plan.tf \ - -var "environment=${{ env.environment_lowercase }}" \ - -var "cloudrun_url=${{ steps.cloudrun.outputs.url }}" - terraform apply -auto-approve plan.tf + APP_TAG: ${{ inputs.tag }} + APP_ENVIRONMENT: ${{ inputs.environment }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + GOOGLE_REGION: ${{ secrets.GOOGLE_REGION }} + GOOGLE_PROJECT_ID: ${{ secrets.GOOGLE_PROJECT_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + MONGODB_ATLAS_PROJECT_ID: ${{ secrets.MONGODB_ATLAS_PROJECT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} \ No newline at end of file diff --git a/.github/workflows/project_matrix.yaml b/.github/workflows/project_matrix.yaml new file mode 100644 index 0000000..f04516b --- /dev/null +++ b/.github/workflows/project_matrix.yaml @@ -0,0 +1,25 @@ +name: ProjectMatrix +on: + workflow_call: + outputs: + tests: + value: ${{ jobs.project_matrix.outputs.tests }} + typescript: + value: ${{ jobs.project_matrix.outputs.typescript }} + docker: + value: ${{ jobs.project_matrix.outputs.docker }} +jobs: + project_matrix: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo "matrix=$(.github/project_matrix.sh tests --json)" >> $GITHUB_OUTPUT + id: tests + - run: echo "matrix=$(.github/project_matrix.sh typescript --json)" >> $GITHUB_OUTPUT + id: typescript + - run: echo "matrix=$(.github/project_matrix.sh docker --json)" >> $GITHUB_OUTPUT + id: docker + outputs: + tests: ${{ steps.tests.outputs.matrix }} + typescript: ${{ steps.typescript.outputs.matrix }} + docker: ${{ steps.docker.outputs.matrix }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index 5bc344e..0000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,124 +0,0 @@ -name: Test -on: - pull_request: - -jobs: - ######################################################### - # Code quality and unit tests - ######################################################### - test_quality_dotnet: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.0 - - run: dotnet format --verify-no-changes - - - test_quality_terraform: - runs-on: ubuntu-latest - strategy: - matrix: - project: - - name: 'App.Function.Banktransaction.Import' - service: 'function-banktransaction-import' - steps: - - uses: actions/checkout@v3 - - uses: hashicorp/setup-terraform@v2 - - run: | - test -d ${{ matrix.project.name }}/terraform || exit 0 - cd ${{ matrix.project.name }}/terraform - terraform init - terraform validate - - uses: reviewdog/action-tflint@master - with: - github_token: ${{ secrets.github_token }} - working_directory: ${{ matrix.project.name }}/terraform - reporter: github-pr-review - - test_quality_typescript: - runs-on: ubuntu-latest - strategy: - matrix: - project: - - name: 'frontend-web' - nodejs-version: '18.10' - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.project.nodejs-version }} - - uses: reviewdog/action-eslint@v1 - with: - reporter: github-pr-review - workdir: ${{ matrix.project.name }} - fail_on_error: true - - test_unit_typescript_ensure: - runs-on: ubuntu-latest - strategy: - matrix: - project: - - name: 'frontend-web' - nodejs-version: '18.10' - steps: - - uses: actions/checkout@v3 - - run: cd ${{ matrix.project.name }} && ./ensure_tests.sh - - test_unit_typescript: - runs-on: ubuntu-latest - strategy: - matrix: - project: - - name: 'frontend-web' - nodejs-version: '18.10' - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.project.nodejs-version }} - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.0 - - run: dotnet tool restore - - run: | - cd ${{ matrix.project.name }} - npm ci - ./generate_api_types.sh - - run: | - cd ${{ matrix.project.name }} - npm run test -- --reporters="default" --reporters="jest-md-dashboard" - - uses: marocchino/sticky-pull-request-comment@v2 - if: always() - with: - path: ${{ matrix.project.name }}/test-dashboard.md - header: unit_typescript - - test_unit_dotnet: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - project: - - name: 'App.Function.Banktransaction.Import' - - name: 'App.Function.Integration.Ynab' - - name: 'App.Lib.Database' - - name: 'App.Lib.Ynab' - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.0 - - run: | - echo "title=Test Run for PR #${{github.event.number}} Run #${{github.run_number}}" >> $GITHUB_ENV - echo "file_name=TestReport.${{ matrix.project.name }}.${{github.sha}}.md" >> $GITHUB_ENV - - run: dotnet restore ${{ matrix.project.name }}.Tests - - run: dotnet build --no-restore ${{ matrix.project.name }}.Tests - - run: docker-compose -f App.Lib.Tests/docker-compose.yaml up -d - - run: dotnet test --no-restore --no-build --logger "liquid.md;LogFileName=${{github.workspace}}/${{env.file_name}};Title=${{env.title}};" ${{ matrix.project.name }}.Tests - - uses: marocchino/sticky-pull-request-comment@v2 - if: always() - with: - path: ${{github.workspace}}/${{env.file_name}} - header: unit_dotnet diff --git a/.github/workflows/test_integration.yaml b/.github/workflows/test_integration.yaml new file mode 100644 index 0000000..ce43b9a --- /dev/null +++ b/.github/workflows/test_integration.yaml @@ -0,0 +1,49 @@ +name: Integration tests +on: + pull_request: + +jobs: + deploy_slug: + runs-on: ubuntu-latest + steps: + - uses: rlespinasse/github-slug-action@v4 + - id: environment + run: echo "var=it-${{ env.GITHUB_REF_SLUG_URL }}" >> $GITHUB_OUTPUT + - id: tag + run: echo "var=${{ env.GITHUB_SHA_SHORT }}" >> $GITHUB_OUTPUT + outputs: + environment: ${{ steps.environment.outputs.var }} + tag: ${{ steps.tag.outputs.var }} + + build: + needs: [deploy_slug] + uses: ./.github/workflows/build.yaml + secrets: inherit + with: + environment: ${{ needs.deploy_slug.outputs.environment }} + tag: ${{ needs.deploy_slug.outputs.tag }} + + deploy: + needs: [deploy_slug, build] + uses: ./.github/workflows/deploy.yaml + secrets: inherit + concurrency: ${{ needs.deploy_slug.outputs.environment }} + with: + environment: ${{ needs.deploy_slug.outputs.environment }} + tag: ${{ needs.deploy_slug.outputs.tag }} + + test: + runs-on: ubuntu-latest + needs: [deploy_slug, deploy] + concurrency: ${{ needs.deploy_slug.outputs.environment }} + steps: + - run: echo "Do some integration tests on this tmp environment" + + delete: + needs: [deploy_slug, test] + uses: ./.github/workflows/delete.yaml + secrets: inherit + concurrency: ${{ needs.deploy_slug.outputs.environment }} + if: ${{ always() }} + with: + environment: ${{ needs.deploy_slug.outputs.environment }} diff --git a/.github/workflows/test_quality.yaml b/.github/workflows/test_quality.yaml new file mode 100644 index 0000000..0f47ca5 --- /dev/null +++ b/.github/workflows/test_quality.yaml @@ -0,0 +1,35 @@ +name: Code quality +on: + pull_request: + +jobs: + ######################################################### + # Code quality tests + ######################################################### + test_quality_dotnet: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0 + - run: dotnet format --verify-no-changes + + test_quality_ansible: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ansible/ansible-lint-action@v6 + + test_quality_frontend_web_typescript: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version-file: frontend-web/package.json + - uses: reviewdog/action-eslint@v1 + with: + reporter: github-pr-review + fail_on_error: true + workdir: frontend-web diff --git a/.github/workflows/test_unit.yaml b/.github/workflows/test_unit.yaml new file mode 100644 index 0000000..dbc791d --- /dev/null +++ b/.github/workflows/test_unit.yaml @@ -0,0 +1,67 @@ +name: Unit test +on: + pull_request: + +jobs: + project_matrix: + uses: ./.github/workflows/project_matrix.yaml + + ######################################################### + # Code quality and unit tests + ######################################################### + test_unit_typescript_ensure: + runs-on: ubuntu-latest + needs: [project_matrix] + strategy: + matrix: + project: ${{ fromJson(needs.project_matrix.outputs.typescript) }} + steps: + - uses: actions/checkout@v3 + - run: cd ${{ matrix.project }} && ./ensure_tests.sh + + test_unit_typescript: + runs-on: ubuntu-latest + needs: [project_matrix] + strategy: + matrix: + project: ${{fromJson(needs.project_matrix.outputs.typescript)}} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version-file: ${{ matrix.project }}/package.json + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0 + - run: cd ${{ matrix.project }} && npm ci + - run: cd ${{ matrix.project }} && npm run test -- --reporters="default" --reporters="jest-md-dashboard" + - uses: marocchino/sticky-pull-request-comment@v2 + if: always() + with: + path: ${{ matrix.project }}/test-dashboard.md + header: unit_typescript + + test_unit_dotnet: + runs-on: ubuntu-latest + needs: [project_matrix] + strategy: + fail-fast: false + matrix: + project: ${{fromJson(needs.project_matrix.outputs.tests)}} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0 + - run: | + echo "title=Test Run for PR #${{github.event.number}} Run #${{github.run_number}}" >> $GITHUB_ENV + echo "file_name=TestReport.${{ matrix.project }}.${{github.sha}}.md" >> $GITHUB_ENV + - run: dotnet restore ${{ matrix.project }} + - run: dotnet build --no-restore ${{ matrix.project }} + - run: docker-compose -f App.Lib.Tests/docker-compose.yaml up -d + - run: dotnet test --no-restore --no-build --logger "liquid.md;LogFileName=${{github.workspace}}/${{env.file_name}};Title=${{env.title}};" ${{ matrix.project }} + - uses: marocchino/sticky-pull-request-comment@v2 + if: always() + with: + path: ${{github.workspace}}/${{env.file_name}} + header: unit_dotnet diff --git a/.gitignore b/.gitignore index 5d1bd1a..27c9f5a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,12 @@ bin/ obj/ /packages/ riderModule.iml -terraform.tfstate +*.tfstate +*.tfstate.backup .terraform/ +terraform/plan.tf /_ReSharper.Caches/ /.idea */swagger.json +.env.local +.env.*.local diff --git a/App.Function.Banktransaction.Import/service.yaml b/App.Function.Banktransaction.Import/service.yaml deleted file mode 100644 index 01e79af..0000000 --- a/App.Function.Banktransaction.Import/service.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: serving.knative.dev/v1 -kind: Service -metadata: - name: ${SERVICE}-${DEPLOY_ENV} - labels: - environment: ${DEPLOY_ENV} -spec: - template: - spec: - containerConcurrency: 1 - containers: - - image: ${IMAGE} - env: - - name: DOTNET_ENVIRONMENT - value: ${DOTNET_ENVIRONMENT} - - name: Database__ConnectionString - value: ${Database__ConnectionString} - - name: Database__DatabaseName - value: ${Database__DatabaseName} - - name: Sentry__Dsn - value: ${Sentry__Dsn} - resources: - limits: - cpu: "1" - memory: 512Mi \ No newline at end of file diff --git a/App.Function.Banktransaction.Import/terraform/.terraform.lock.hcl b/App.Function.Banktransaction.Import/terraform/.terraform.lock.hcl deleted file mode 100644 index f4176d0..0000000 --- a/App.Function.Banktransaction.Import/terraform/.terraform.lock.hcl +++ /dev/null @@ -1,22 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/google" { - version = "4.40.0" - constraints = "~> 4.40.0" - hashes = [ - "h1:dBRMh+QRcivqOC8+mWC32/uN5TAi7ecEo2E+MTCIeiE=", - "zh:30cf1230864a162f6811e1ba892da662ae5de7c393cac548397463e7ad6471be", - "zh:33ba8d9fc8288de4eedd3d37e44229ab7466b71aa8e50170ed310a551f6c9189", - "zh:53748600ebb4707f039a05d75475148a13914aeae7416947db6d2d80b3991a14", - "zh:75331e3af45929f7eb696e8e18a84e48cef87df5f66d4924b63487e807003836", - "zh:76531a0bf9736a022c5b5bfb14a51f727cff28857f81b9ca0437dc3bf1d7f643", - "zh:879ee4fd1f07557e981ebe22e0961ceca37ddda69d18a203e702574dc30f47dd", - "zh:ae497e030c6926f897950f1cf714847c4459159dc33afedb9055eadb82159165", - "zh:da2c517396d109e319f279a14139b43e959ba7694da2a55db90f41aa3e446bbd", - "zh:ebd03dc3f5b1287feaf0cb619701f13646824bd5af7e93e3b55e821f96f9c6c3", - "zh:f05dcdc5701882c4a2cce34fe8ebe8069af4d35daf4f2dda3492fa2314109adb", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - "zh:f6232e0db8716daa6526ed9c10873b3e892c2602c0521b4079a24203e56d366d", - ] -} diff --git a/App.Function.Banktransaction.Import/terraform/main.tf b/App.Function.Banktransaction.Import/terraform/main.tf deleted file mode 100644 index d7e0665..0000000 --- a/App.Function.Banktransaction.Import/terraform/main.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "environment" { - type = string -} - -variable "cloudrun_url" { - type = string -} - -terraform { - required_providers { - google = { - source = "hashicorp/google" - version = "~> 4.40.0" - } - } -} - -provider "google" {} - -resource "google_pubsub_topic" "bankstransaction-import" { - name = "bankstransaction-import-${var.environment}" -} - -resource "google_pubsub_subscription" "bankstransaction-import" { - name = "function-bankstransaction-import-${var.environment}" - topic = google_pubsub_topic.bankstransaction-import.name - - push_config { - push_endpoint = var.cloudrun_url - } -} \ No newline at end of file diff --git a/App.Function.Integration.Ynab/appsettings.json b/App.Function.Integration.Ynab/appsettings.json index 162ea39..e9acb38 100644 --- a/App.Function.Integration.Ynab/appsettings.json +++ b/App.Function.Integration.Ynab/appsettings.json @@ -1,4 +1,7 @@ { + "App": { + "Frontend": "http://localhost:3000" + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/App.Function.Integration.Ynab/deploy/deploy.yml b/App.Function.Integration.Ynab/deploy/deploy.yml new file mode 100644 index 0000000..764f83f --- /dev/null +++ b/App.Function.Integration.Ynab/deploy/deploy.yml @@ -0,0 +1,33 @@ +--- +- name: Deploy App.Function.Integration.Ynab + gather_facts: false + hosts: localhost + vars: + project_name: Function.Integration.Ynab + project_slug: "{{ app_environment }}-{{ project_name | lower | replace('.', '-') }}" + + tasks: + - name: Deploy + shell: gcloud run deploy "{{ project_slug }}" \ + --image="{{ google_region }}-docker.pkg.dev/{{ google_project_id }}/docker/{{ app_environment }}/app.{{ project_name | lower }}:{{ app_image_tag }}" \ + --project="{{ google_project_id }}" \ + --region="{{ google_region }}" \ + --service-account="" \ + --set-env-vars=" \ + App__Frontend={{ app_frontend }}, \ + Sentry__Dsn={{ sentry_dsn }}, \ + Database__ConnectionString={{ database_connection_string }}, \ + Database__DatabaseName={{ project_slug }} \ + " \ + --labels="environment={{ app_environment }}" \ + --platform=managed \ + --port=80 + - name: Get service URL + shell: gcloud run services describe "{{ project_slug }}" \ + --project="{{ google_project_id }}" \ + --region="{{ google_region }}" \ + --platform=managed \ + --format='value(status.url)' + register: service_url + - name: Set service URL + set_fact: "function_integration_ynab_url={{ service_url.stdout }}" diff --git a/App.Lib.Tests/docker-compose.yaml b/App.Lib.Tests/docker-compose.yaml index 8eaf41e..dbdde18 100644 --- a/App.Lib.Tests/docker-compose.yaml +++ b/App.Lib.Tests/docker-compose.yaml @@ -1,9 +1,10 @@ +--- version: "3.2" -services: +services: mongodb: image: mongo:6.0 ports: - - "27017:27017" + - 27017:27017 environment: MONGO_INITDB_ROOT_USERNAME: username - MONGO_INITDB_ROOT_PASSWORD: password \ No newline at end of file + MONGO_INITDB_ROOT_PASSWORD: password diff --git a/App.Lib/src/AppOptions.cs b/App.Lib/src/AppOptions.cs new file mode 100644 index 0000000..f4b6a1c --- /dev/null +++ b/App.Lib/src/AppOptions.cs @@ -0,0 +1,8 @@ +namespace App.Lib; + +public class AppOptions +{ + public const string OptionsKey = "App"; + + public string Frontend { get; set; } = "http://localhost:3000"; +} \ No newline at end of file diff --git a/App.Lib/src/AppWebApplication.cs b/App.Lib/src/AppWebApplication.cs index 6aa9704..ef3b544 100644 --- a/App.Lib/src/AppWebApplication.cs +++ b/App.Lib/src/AppWebApplication.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using Newtonsoft.Json.Converters; namespace App.Lib; @@ -114,6 +115,7 @@ public static async Task CreateAndRun( Func configureApp) { var builder = WebApplication.CreateBuilder(args); + builder.Services.Configure(builder.Configuration.GetSection(AppOptions.OptionsKey)); builder.Configuration .AddEnvironmentVariables() .AddUserSecrets(Assembly.GetExecutingAssembly(), true); @@ -130,31 +132,27 @@ public static async Task CreateAndRun( builder.Services.AddSwaggerGenNewtonsoftSupport(); builder.Services.AddLogging(); builder.Services.AddDataProtection(); - builder.Services.AddCors(options => - { - options.AddPolicy(name: CORSDevelopmentPolicy, - policy => - { - policy.WithOrigins("http://localhost:3000"); - }); - options.AddPolicy(name: CORSProductionPolicy, _ => { }); - }); + builder.Services.AddCors(); await configureBuilder(builder); builder.WebHost.UseSentry(); var app = builder.Build(); app.MapControllers(); + app.UseCors(policy => + { + var settings = app.Services.GetRequiredService>(); + policy.WithOrigins(settings.Value.Frontend); + }); + if (!app.Environment.IsDevelopment()) { app.UseSentryTracing(); - app.UseCors(CORSProductionPolicy); } else { app.UseSwagger(); app.UseSwaggerUI(); - app.UseCors(CORSDevelopmentPolicy); } await configureApp(app); diff --git a/README.md b/README.md index 385aaa1..b88d304 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ [![Test](https://github.com/Fgruntjes/serverless-personal-finance/actions/workflows/test.yaml/badge.svg)](https://github.com/Fgruntjes/serverless-personal-finance/actions/workflows/test.yaml) + +# Install instructions +To install run `./deploy/setup.sh`, this will create the required github secrets and google cloud +resources. To override any variables create an `./deploy/.env.local` file. \ No newline at end of file diff --git a/deploy/.env b/deploy/.env new file mode 100644 index 0000000..1b3686b --- /dev/null +++ b/deploy/.env @@ -0,0 +1,25 @@ +############################### +# Required for initial setup +############################### +# If variable is required a change also needs to run `setup.sh` to update the respective secrets in github and `.env.deploy.local`. +PROJECT_NAME="Svl Personal finance" +GOOGLE_REGION=europe-west1 +GITHUB_REPOSITORY="Fgruntjes/serverless-personal-finance" + +# Required for setup / deploy +# Should only be used if repspective cloud provider does not provide a scriptable way to create the variables. +# If so they should be added to `setup.sh` + +#(Must have `Account.Cloudflare Pages` permissions) +CLOUDFLARE_API_TOKEN= +CLOUDFLARE_ACCOUNT_ID= + +# gcloud does not provide a way to OAUTH client ids (or concent screen) +GOOGLE_OAUTH_CLIENT_ID= +GOOGLE_OAUTH_CLIENT_SECRET= + +############################### +# Optional +############################### +SENTRY_DSN= +GOOGLE_BILLING_ACCOUNT_ID= diff --git a/deploy/run-ansible.sh b/deploy/run-ansible.sh new file mode 100755 index 0000000..832fcaf --- /dev/null +++ b/deploy/run-ansible.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$(realpath "$0")")"; + +set -a +test -f .env.deploy.local && source .env.deploy.local +test -f .env.local && source .env.local +set +a + +ansible-playbook "${@}" \ + -e "APP_TAG=${APP_TAG}" \ + -e "APP_ENVIRONMENT=${APP_ENVIRONMENT}" \ + -e "APP_FRONTEND=${APP_FRONTEND}" \ + -e "SENTRY_DSN=${SENTRY_DSN}" \ + -e "GOOGLE_REGION=${GOOGLE_REGION}" \ + -e "GOOGLE_PROJECT_ID=${GOOGLE_PROJECT_ID}" \ + -e "MONGODB_ATLAS_PUBLIC_KEY=${MONGODB_ATLAS_PUBLIC_KEY}" \ + -e "MONGODB_ATLAS_PRIVATE_KEY=${MONGODB_ATLAS_PRIVATE_KEY}" \ + -e "MONGODB_ATLAS_PROJECT_ID=${MONGODB_ATLAS_PROJECT_ID}" \ + -e "CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}" \ + -e "CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID}" \ + -e "GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID}" \ + -e "GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET}" + \ No newline at end of file diff --git a/deploy/setup.sh b/deploy/setup.sh new file mode 100755 index 0000000..3ae35bb --- /dev/null +++ b/deploy/setup.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$(realpath "$0")")"; + +# TODO ensure required cli tools are installed / configured: gcloud (google), gh (github), atlas (mongodb) and cf (cloudflare) + +# Load env variables +set -a +source .env +test -f .env.local && source .env.local +set +a + +function slugify { + iconv -t "ascii//TRANSLIT" | sed -r "s/[^a-zA-Z0-9]+/-/g" | sed -r "s/^-+\|-+$//g" | tr "[:upper:]" "[:lower:]" +} + +# Env defaults +PROJECT_SLUG=$(echo "${PROJECT_NAME}" | slugify ) +SERVICE_ACCOUNT_NAME="github-cicd" +IDENTITY_POOL_NAME="github-pool" +IDENTITY_PROVIDER_NAME="github-provider" + +# Create project +if ! gcloud projects describe "${PROJECT_SLUG}" > /dev/null; then + echo "Creating project: ${PROJECT_SLUG}" + gcloud projects create "${PROJECT_SLUG}" --name "${PROJECT_NAME}" +else + echo "Project already exists: ${PROJECT_SLUG}" +fi +GOOGLE_PROJECT_ID=$PROJECT_SLUG +echo "" + +# Enable required services +echo "Enabling required services" +gcloud services enable iamcredentials.googleapis.com --project "${PROJECT_SLUG}" +gcloud services enable artifactregistry.googleapis.com --project "${PROJECT_SLUG}" +gcloud services enable run.googleapis.com --project "${PROJECT_SLUG}" +echo "" + +# Link billing account +GOOGLE_BILLING_ACCOUNT_FIRST=$(gcloud beta billing accounts list --filter="open=true" --format="value(name)" --limit=1) +GOOGLE_BILLING_ACCOUNT_ID=${GOOGLE_BILLING_ACCOUNT_ID:-${GOOGLE_BILLING_ACCOUNT_FIRST}} +echo "Ensure project ${PROJECT_SLUG} is linked to billing account ${GOOGLE_BILLING_ACCOUNT_ID}" +gcloud beta billing projects link "${PROJECT_SLUG}" "--billing-account=${GOOGLE_BILLING_ACCOUNT_ID}" +echo "" + +# Create identity pool +# @see (https://github.com/google-github-actions/auth#setup) +if ! gcloud iam workload-identity-pools describe --location="global" --project="${PROJECT_SLUG}" "${IDENTITY_POOL_NAME}" > /dev/null; then + echo "Creating identity pool: ${IDENTITY_POOL_NAME}" + gcloud iam workload-identity-pools create "${IDENTITY_POOL_NAME}" \ + --project="${PROJECT_SLUG}" \ + --location="global" \ + --display-name="${IDENTITY_POOL_NAME}" +else + echo "Identity pool already exists: ${IDENTITY_POOL_NAME}" +fi +IDENTITY_POOL_ID=$( + gcloud iam workload-identity-pools describe "${IDENTITY_POOL_NAME}" \ + --project="${PROJECT_SLUG}" \ + --location="global" \ + --format="value(name)" +) +echo "" + +if ! gcloud iam workload-identity-pools providers describe \ + --workload-identity-pool="${IDENTITY_POOL_NAME}" \ + --location="global" \ + --project="${PROJECT_SLUG}" "${IDENTITY_PROVIDER_NAME}" > /dev/null +then + echo "Creating identity pool provider: ${IDENTITY_PROVIDER_NAME}" + gcloud iam workload-identity-pools providers create-oidc "${IDENTITY_PROVIDER_NAME}" \ + --project="${PROJECT_SLUG}" \ + --location="global" \ + --workload-identity-pool="${IDENTITY_POOL_NAME}" \ + --display-name="${IDENTITY_PROVIDER_NAME}" \ + --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \ + --issuer-uri="https://token.actions.githubusercontent.com" +else + echo "Identity pool provider already exists: ${IDENTITY_PROVIDER_NAME}" +fi +echo "" + + +# Add service account and permissions +GOOGLE_SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_NAME}@${PROJECT_SLUG}.iam.gserviceaccount.com" +if ! gcloud iam service-accounts describe --project="${PROJECT_SLUG}" "${GOOGLE_SERVICE_ACCOUNT_EMAIL}" > /dev/null 2>&1; then + echo "Creating service account: ${SERVICE_ACCOUNT_NAME}" + gcloud iam service-accounts create --project="${PROJECT_SLUG}" ${SERVICE_ACCOUNT_NAME} +else + echo "Service account already exists: ${SERVICE_ACCOUNT_NAME}" +fi + +echo "Setting gcloud roles" +gcloud iam service-accounts add-iam-policy-binding "${GOOGLE_SERVICE_ACCOUNT_EMAIL}" \ + --project="${PROJECT_SLUG}" \ + --role="roles/iam.workloadIdentityUser" \ + --member="principalSet://iam.googleapis.com/${IDENTITY_POOL_ID}/attribute.repository/${GITHUB_REPOSITORY}" +GCLOUD_ROLES=( + "roles/artifactregistry.repoAdmin" + "roles/storage.objectViewer" + "roles/run.developer" + "roles/iam.serviceAccountUser" +) +for GCLOUD_ROLE in "${GCLOUD_ROLES[@]}"; do + gcloud projects add-iam-policy-binding "${PROJECT_SLUG}" \ + --role="${GCLOUD_ROLE}" \ + --member="serviceAccount:${GOOGLE_SERVICE_ACCOUNT_EMAIL}" +done +GOOGLE_WORKLOAD_IDENTITY_PROVIDER=$( + gcloud iam workload-identity-pools providers describe "${IDENTITY_PROVIDER_NAME}" \ + --project="${PROJECT_SLUG}" \ + --location="global" \ + --workload-identity-pool="${IDENTITY_POOL_NAME}" \ + --format="value(name)" +) +echo "" + + +# Create container registry +if ! gcloud artifacts repositories describe --project="${PROJECT_SLUG}" --location="${GOOGLE_REGION}" docker > /dev/null; then + echo "Creating artifact repository" + gcloud artifacts repositories create docker \ + --repository-format=docker \ + --description="Docker repository" \ + --location="${GOOGLE_REGION}" \ + --project="${PROJECT_SLUG}" +else + echo "Artifact repository already created" +fi +echo "" + + +# Create mongodb atlas project +if ! atlas project list | grep "${PROJECT_SLUG}" > /dev/null; then + echo "Creating MongoDB Atlas project" + atlas project create "${PROJECT_SLUG}" +else + echo "MongoDB Atlas project already created" +fi +MONGODB_ATLAS_PROJECT_ID=$(atlas project list --output=json | jq -r ".results[] | select(.name == \"${PROJECT_SLUG}\") | .id") +echo "MongoDB project ID: ${MONGODB_ATLAS_PROJECT_ID}" +echo "" + + +# Recreate api key +MONGODB_ATLAS_API_KEY_ID=$(atlas project apiKeys list --projectId="${MONGODB_ATLAS_PROJECT_ID}" --output=json | jq -r ".[] | select(.desc == \"${SERVICE_ACCOUNT_NAME}\") | .id") +if [ -n "${MONGODB_ATLAS_API_KEY_ID}" ]; then + echo "Delete old MongoDB Atlas api key ${MONGODB_ATLAS_API_KEY_ID}" + atlas projects apiKeys delete --force "${MONGODB_ATLAS_API_KEY_ID}" +fi + +echo "Creating MongoDB Atlas api key" +MONGODB_ATLAS_API_KEY_CREATE_OUTPUT=$( + atlas projects apiKeys create \ + --role=ORG_OWNER,ORG_GROUP_CREATOR \ + --output=json \ + --projectId="${MONGODB_ATLAS_PROJECT_ID}" \ + --desc="${SERVICE_ACCOUNT_NAME}" +) +echo "${MONGODB_ATLAS_API_KEY_CREATE_OUTPUT}" +echo "" + +MONGODB_ATLAS_PUBLIC_KEY=$(echo "${MONGODB_ATLAS_API_KEY_CREATE_OUTPUT}" | jq -r ".publicKey") +MONGODB_ATLAS_PRIVATE_KEY=$(echo "${MONGODB_ATLAS_API_KEY_CREATE_OUTPUT}" | jq -r ".privateKey") + + +# Ensure Github secrets are set +echo "Creating github secrets" +function storeSecret { + SECRET_NAME="${1}" + SECRET_VALUE="${!SECRET_NAME}" + if [ -z "${SECRET_VALUE}" ]; then + echo "Missing environment variable '${SECRET_NAME}', please configure one in your '.env.local' file." + echo "${SECRET_VALUE}" + exit 1 + fi + + echo "${SECRET_VALUE}" | gh secret set "${SECRET_NAME}" --app actions + echo "${SECRET_NAME}=\"${SECRET_VALUE}\"" >> .env.deploy.local +} +cat /dev/null > .env.deploy.local +storeSecret GOOGLE_WORKLOAD_IDENTITY_PROVIDER +storeSecret GOOGLE_SERVICE_ACCOUNT_EMAIL +storeSecret GOOGLE_REGION +storeSecret GOOGLE_PROJECT_ID + +storeSecret MONGODB_ATLAS_PRIVATE_KEY +storeSecret MONGODB_ATLAS_PUBLIC_KEY +storeSecret MONGODB_ATLAS_PROJECT_ID + +storeSecret GOOGLE_OAUTH_CLIENT_ID +storeSecret GOOGLE_OAUTH_CLIENT_SECRET +storeSecret CLOUDFLARE_API_TOKEN +storeSecret CLOUDFLARE_ACCOUNT_ID +storeSecret SENTRY_DSN \ No newline at end of file diff --git a/deploy/src/delete.yml b/deploy/src/delete.yml new file mode 100644 index 0000000..7c4c430 --- /dev/null +++ b/deploy/src/delete.yml @@ -0,0 +1,16 @@ +--- +# Infra (database) +- import_playbook: mongodb/delete.yml + vars: + app_environment: "{{ APP_ENVIRONMENT }}" + project_id: "{{ MONGODB_ATLAS_PROJECT_ID }}" + public_api_key: "{{ MONGODB_ATLAS_PUBLIC_KEY }}" + private_api_key: "{{ MONGODB_ATLAS_PRIVATE_KEY }}" + +# Google cloud resources +- import_playbook: gcloud/delete.yml + vars: + app_environment: "{{ APP_ENVIRONMENT }}" + app_image_tag: "{{ APP_TAG }}" + google_region: "{{ GOOGLE_REGION }}" + google_project_id: "{{ GOOGLE_PROJECT_ID }}" diff --git a/deploy/src/deploy.yml b/deploy/src/deploy.yml new file mode 100644 index 0000000..a3b9176 --- /dev/null +++ b/deploy/src/deploy.yml @@ -0,0 +1,31 @@ +--- +# Infra (database) +- import_playbook: mongodb/create.yml + vars: + app_environment: "{{ APP_ENVIRONMENT }}" + google_region: "{{ GOOGLE_REGION }}" + project_id: "{{ MONGODB_ATLAS_PROJECT_ID }}" + public_api_key: "{{ MONGODB_ATLAS_PUBLIC_KEY }}" + private_api_key: "{{ MONGODB_ATLAS_PRIVATE_KEY }}" + +# Backend functions +- import_playbook: ../../App.Function.Integration.Ynab/deploy/deploy.yml + vars: + app_environment: "{{ APP_ENVIRONMENT }}" + app_image_tag: "{{ APP_TAG }}" + app_frontend: "{{ APP_FRONTEND }}" + google_region: "{{ GOOGLE_REGION }}" + google_project_id: "{{ GOOGLE_PROJECT_ID }}" + sentry_dsn: "{{ SENTRY_DSN }}" + database_connection_string: "{{ mongodb_connection_string_admin }}" + +# Frontend +- import_playbook: ../../frontend-web/deploy/deploy.yml + vars: + app_environment: "{{ APP_ENVIRONMENT }}" + app_project_name: "{{ GOOGLE_PROJECT_ID }}" + cloudflare_api_token: "{{ CLOUDFLARE_API_TOKEN }}" + cloudflare_account_id: "{{ CLOUDFLARE_ACCOUNT_ID }}" + oauth_client_id: "{{ GOOGLE_OAUTH_CLIENT_ID }}" + service_urls: + function_integration_ynab_url: "{{ function_integration_ynab_url }}" diff --git a/deploy/src/gcloud/delete.yml b/deploy/src/gcloud/delete.yml new file mode 100644 index 0000000..029d7c0 --- /dev/null +++ b/deploy/src/gcloud/delete.yml @@ -0,0 +1,19 @@ +--- +- name: Delete gcloud resources + gather_facts: false + hosts: localhost + tasks: + # Cloud run services + - name: Delete services + shell: gcloud run services list \ + --project="{{ google_project_id }}" \ + --region="{{ google_region }}" \ + --filter="metadata.labels.environment='{{ app_environment }}'" \ + --format="value(metadata.name)" \ + | xargs -I {} gcloud run services delete \ + --project="{{ google_project_id }}" \ + --region="{{ google_region }}" \ + --quiet {} + # Pubsub + # Storage + # Scheduler jobs diff --git a/deploy/src/mongodb/create.yml b/deploy/src/mongodb/create.yml new file mode 100644 index 0000000..3a877f1 --- /dev/null +++ b/deploy/src/mongodb/create.yml @@ -0,0 +1,50 @@ +--- +- name: Create mongodb database + gather_facts: false + hosts: localhost + environment: + MONGODB_ATLAS_PUBLIC_API_KEY: "{{ public_api_key }}" + MONGODB_ATLAS_PRIVATE_API_KEY: "{{ private_api_key }}" + MONGODB_ATLAS_PROJECT_ID: "{{ project_id }}" + tasks: + - name: Set variables + set_fact: + admin_user: "{{ admin_user | default('admin') }}" + read_user: "{{ read_user | default('read') }}" + write_user: "{{ write_user | default('write') }}" + + # Create instance + - name: Get MongoDB Atlas serverless instance info + shell: atlas serverless describe "{{ app_environment }}" -o json + register: instance_info + changed_when: false + failed_when: false + - name: Create server instance + when: instance_info.rc != 0 + shell: atlas serverless create "{{ app_environment }}" \ + --provider=GCP \ + --region="{{ google_region | replace('europe-west1', 'WESTERN_EUROPE') }}" + + # Configure network access + - name: Get network access list + shell: atlas accessLists list -o json | jq -e -r ".results[] | select(.comment == \"public\")" > /dev/null + register: access_info + changed_when: false + failed_when: false + - name: Create network access list + when: access_info.rc != 0 + shell: atlas accessLists create "0.0.0.0/0" --comment="public" + + # Create users + - include_tasks: create_user.yml + vars: + - username: "{{ admin_user }}" + role: atlasAdmin + - include_tasks: create_user.yml + vars: + - username: "{{ read_user }}" + role: readAnyDatabase + - include_tasks: create_user.yml + vars: + - username: "{{ write_user }}" + role: readWriteAnyDatabase diff --git a/deploy/src/mongodb/create_user.yml b/deploy/src/mongodb/create_user.yml new file mode 100644 index 0000000..9926cd5 --- /dev/null +++ b/deploy/src/mongodb/create_user.yml @@ -0,0 +1,38 @@ +--- +- name: Set variables + set_fact: + password: "{{ lookup('community.general.random_string', length=12, special=false) }}" + +- name: Get atlas user info {{ username }} + shell: atlas dbusers describe "{{ username }}" --projectId="{{ project_id }}" -o json + register: user_info + changed_when: false + failed_when: false + +# Create new +- name: Create atlas user "{{ username }}" + when: user_info.rc != 0 + shell: atlas dbusers create \ + --projectId="{{ project_id }}" \ + --scope="CLUSTER,{{ app_environment }}" \ + --username="{{ username }}" \ + --password="{{ password }}" \ + --role="{{ role }}" + +# Update / roll password +- name: Update atlas user "{{ username }}" + when: user_info.rc == 0 + shell: atlas dbusers update "{{ username }}" \ + --projectId="{{ project_id }}" \ + --password="{{ password }}" + +- name: Find connection string + register: connection_string_info + shell: atlas serverless describe "{{ app_environment }}" \ + --projectId="{{ project_id }}" \ + -o json | jq -r ".connectionStrings.standardSrv" + +- name: Set connection string variable + set_fact: >- + mongodb_connection_string_{{ username }}={{ connection_string_info.stdout | + replace('mongodb+srv://', 'mongodb+srv://' + username + ':' + password) }} diff --git a/deploy/src/mongodb/delete.yml b/deploy/src/mongodb/delete.yml new file mode 100644 index 0000000..52ac27f --- /dev/null +++ b/deploy/src/mongodb/delete.yml @@ -0,0 +1,19 @@ +--- +- name: Delete mongodb database + gather_facts: false + hosts: localhost + environment: + MONGODB_ATLAS_PUBLIC_API_KEY: "{{ public_api_key }}" + MONGODB_ATLAS_PRIVATE_API_KEY: "{{ private_api_key }}" + MONGODB_ATLAS_PROJECT_ID: "{{ project_id }}" + tasks: + # Get instance info + - name: Get MongoDB Atlas serverless instance info + shell: atlas serverless describe "{{ app_environment }}" -o json | jq -e -r ".stateName" | grep -v "DELETING" + register: instance_info + changed_when: false + failed_when: false + # Delete instance if exists + - name: Delete server instance + when: instance_info.rc == 0 + shell: atlas serverless delete "{{ app_environment }}" --force diff --git a/frontend-web/deploy/deploy.yml b/frontend-web/deploy/deploy.yml new file mode 100644 index 0000000..b3396bc --- /dev/null +++ b/frontend-web/deploy/deploy.yml @@ -0,0 +1,35 @@ +--- +- name: Deploy frontend-web + hosts: localhost + gather_facts: false + environment: + CLOUDFLARE_API_TOKEN: "{{ cloudflare_api_token }}" + CLOUDFLARE_ACCOUNT_ID: "{{ cloudflare_account_id }}" + vars: + project_dir: "{{ playbook_dir }}/.." + tasks: + - name: Build + shell: npm ci && npm run build + args: + chdir: "{{ project_dir }}" + environment: + REACT_APP_FUNCTION_INTEGRATION_YNAB_BASE: "{{ function_integration_ynab_url }}" + REACT_APP_OAUTH_CLIENT_ID: "{{ oauth_client_id }}" + + - name: Get project info + shell: npx wrangler pages project list | grep "{{ app_project_name }}" > /dev/null + args: + chdir: "{{ project_dir }}" + register: project_info + changed_when: false + failed_when: false + - name: Create project + when: project_info.rc != 0 + shell: npx wrangler pages project create "{{ app_project_name }}" \ --production-branch=main + args: + chdir: "{{ project_dir }}" + + - name: Publish the website to Cloudflare Pages + shell: npx wrangler pages publish build \ --branch="{{ app_environment }}" \ --project-name="{{ app_project_name }}" + args: + chdir: "{{ project_dir }}" diff --git a/frontend-web/generate_api_types.sh b/frontend-web/generate_api_types.sh index 43b6c08..c51e303 100755 --- a/frontend-web/generate_api_types.sh +++ b/frontend-web/generate_api_types.sh @@ -5,15 +5,13 @@ cd "$(dirname "$(realpath "$0")")"; set -e set -x -API_PROJECTS=( - "App.Function.Integration.Ynab" -) +API_PROJECTS=( $(../.github/project_matrix.sh functions) ) # Clear old generated code rm -Rf src/generated/*/* -# Build dotnet projects -(cd ../ && dotnet build) +# Build dotnet projects and restore tools +(cd ../ && dotnet build && dotnet tool restore) for PROJECT in "${API_PROJECTS[@]}" do diff --git a/frontend-web/package-lock.json b/frontend-web/package-lock.json index fbdf7b3..d094338 100644 --- a/frontend-web/package-lock.json +++ b/frontend-web/package-lock.json @@ -7,13 +7,14 @@ "": { "name": "frontend-web", "version": "0.1.0", + "hasInstallScript": true, "dependencies": { "@emotion/react": "^11.10.4", "@emotion/styled": "^11.10.4", "@fontsource/roboto": "^4.5.8", "@mui/icons-material": "^5.10.9", "@mui/joy": "^5.0.0-alpha.49", - "@react-oauth/google": "^0.2.8", + "@react-oauth/google": "^0.6.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -40,7 +41,11 @@ "recoil-persist": "^4.2.0", "sass": "^1.55.0", "typescript": "^4.8.4", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "wrangler": "^2.8.1" + }, + "engines": { + "node": ">=v16.18" } }, "node_modules/@adobe/css-tools": { @@ -1917,6 +1922,25 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", + "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", + "dependencies": { + "mime": "^3.0.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler/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" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@csstools/normalize.css": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", @@ -2352,6 +2376,37 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" }, + "node_modules/@esbuild-plugins/node-globals-polyfill": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", + "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-modules-polyfill": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", + "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", + "dependencies": { + "escape-string-regexp": "^4.0.0", + "rollup-plugin-node-polyfills": "^0.2.1" + }, + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-modules-polyfill/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -2450,6 +2505,11 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, "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", @@ -3187,6 +3247,315 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@miniflare/cache": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.11.0.tgz", + "integrity": "sha512-L/kc9AzidPwFuk2fwHpAEePi0kNBk6FWUq3ln+9beRCDrPEpfVrDRFpNleF1NFZz5//oeVMuo8F0IVUQGzR7+Q==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "http-cache-semantics": "^4.1.0", + "undici": "5.9.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/cli-parser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.11.0.tgz", + "integrity": "sha512-JUmyRzEGAS6CouvXJwBh8p44onfw3KRpfq5JGXEuHModOGjTp6li7PQyCTNPV2Hv/7StAXWnTFGXeAqyDHuTig==", + "dependencies": { + "@miniflare/shared": "2.11.0", + "kleur": "^4.1.4" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/cli-parser/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@miniflare/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.11.0.tgz", + "integrity": "sha512-UFMFiCG0co36VpZkgFrSBnrxo71uf1x+cjlzzJi3khmMyDlnLu4RuIQsAqvKbYom6fi3G9Q8lTgM7JuOXFyjhw==", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@miniflare/queues": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/watcher": "2.11.0", + "busboy": "^1.6.0", + "dotenv": "^10.0.0", + "kleur": "^4.1.4", + "set-cookie-parser": "^2.4.8", + "undici": "5.9.1", + "urlpattern-polyfill": "^4.0.3" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/core/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@miniflare/d1": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/d1/-/d1-2.11.0.tgz", + "integrity": "sha512-aDdBVQZ2C0Zs3+Y9ZbRctmuQxozPfpumwJ/6NG6fBadANvune/hW7ddEoxyteIEU9W3IgzVj8s4by4VvasX90A==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.7" + } + }, + "node_modules/@miniflare/durable-objects": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.11.0.tgz", + "integrity": "sha512-0cKJaMgraTEU1b4kqK8cjD2oTeOjA6QU3Y+lWiZT/k1PMHZULovrSFnjii7qZ8npf4VHSIN6XYPxhyxRyEM65Q==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/storage-memory": "2.11.0", + "undici": "5.9.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/html-rewriter": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.11.0.tgz", + "integrity": "sha512-olTqmuYTHnoTNtiA0vjQ/ixRfbwgPzDrAUbtXDCYW45VFbHfDVJrJGZX3Jg0HpSlxy86Zclle1SUxGbVDzxsBg==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "html-rewriter-wasm": "^0.4.1", + "undici": "5.9.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/http-server": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.11.0.tgz", + "integrity": "sha512-sMLcrDFzqqAvnQmAUH0hRTo8sBjW79VZYfnIH5FAGSGcKX6kdAGs9RStdYZ4CftQCBAEQScX0KBsMx5FwJRe9Q==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/web-sockets": "2.11.0", + "kleur": "^4.1.4", + "selfsigned": "^2.0.0", + "undici": "5.9.1", + "ws": "^8.2.2", + "youch": "^2.2.2" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/http-server/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@miniflare/http-server/node_modules/ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "engines": { + "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/@miniflare/kv": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.11.0.tgz", + "integrity": "sha512-3m9dL2HBBN170V1JvwjjucR5zl4G3mlcsV6C1E7A2wLl2Z2TWvIx/tSY9hrhkD96dFnejwJ9qmPMbXMMuynhjg==", + "dependencies": { + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/queues": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/queues/-/queues-2.11.0.tgz", + "integrity": "sha512-fLHjdrNLKhn0LZM/aii/9GsAttFd+lWlGzK8HOg1R0vhfKBwEub4zntjMmOfFbDm1ntc21tdMK7n3ldUphwh5w==", + "dependencies": { + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.7" + } + }, + "node_modules/@miniflare/r2": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.11.0.tgz", + "integrity": "sha512-MKuyJ/gGNsK3eWbGdygvozqcyaZhM3C6NGHvoaZwH503dwN569j5DpatTWiHGFeDeSu64VqcIsGehz05GDUaag==", + "dependencies": { + "@miniflare/shared": "2.11.0", + "undici": "5.9.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/runner-vm": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.11.0.tgz", + "integrity": "sha512-bkVSuvCf5+VylqN8lTiLxIYqYcKFbl+BywZGwGQndPC/3wh42J00mM0jw4hRbvXgwuBhlUyCVpEXtYlftFFT/g==", + "dependencies": { + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/scheduler": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.11.0.tgz", + "integrity": "sha512-DPdzINhdWeS99eIicGoluMsD4pLTTAWNQbgCv3CTwgdKA3dxdvMSCkNqZzQLiALzvk9+rSfj46FlH++HE7o7/w==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "cron-schedule": "^3.0.4" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/shared": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.11.0.tgz", + "integrity": "sha512-fWMqq3ZkWAg+k7CnyzMV/rZHugwn+/JxvVzCxrtvxzwotTN547THlOxgZe8JAP23U9BiTxOfpTfnLvFEjAmegw==", + "dependencies": { + "@types/better-sqlite3": "^7.6.0", + "kleur": "^4.1.4", + "npx-import": "^1.1.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/shared/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@miniflare/sites": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.11.0.tgz", + "integrity": "sha512-qbefKdWZUJgsdLf+kCw03sn3h/92LZgJAbkOpP6bCrfWkXlJ37EQXO4KWdhn4Ghc7A6GwU1s1I/mdB64B3AewQ==", + "dependencies": { + "@miniflare/kv": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/storage-file": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/storage-file": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.11.0.tgz", + "integrity": "sha512-beWF/lTX74x7AiaSB+xQxywPSNdhtEKvqDkRui8eOJ5kqN2o4UaleLKQGgqmCw3WyHRIsckV7If1qpbNiLtWMw==", + "dependencies": { + "@miniflare/shared": "2.11.0", + "@miniflare/storage-memory": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/storage-memory": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.11.0.tgz", + "integrity": "sha512-s0AhPww7fq/Jz80NbPb+ffhcVRKnfPi7H1dHTRTre2Ud23EVJjAWl2gat42x8NOT/Fu3/o/7A72DWQQJqfO98A==", + "dependencies": { + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/watcher": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.11.0.tgz", + "integrity": "sha512-RUfjz2iYcsQXLcGySemJl98CJ2iierbWsPGWZhIVZI+NNhROkEy77g/Q+lvP2ATwexG3/dUSfdJ3P8aH+sI4Ig==", + "dependencies": { + "@miniflare/shared": "2.11.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/web-sockets": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.11.0.tgz", + "integrity": "sha512-NC8RKrmxrO0hZmwpzn5g4hPGA2VblnFTIBobmWoxuK95eW49zfs7dtE/PyFs+blsGv3CjTIjHVSQ782K+C6HFA==", + "dependencies": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "undici": "5.9.1", + "ws": "^8.2.2" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/web-sockets/node_modules/ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "engines": { + "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/@mui/base": { "version": "5.0.0-alpha.101", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.101.tgz", @@ -3611,9 +3980,9 @@ } }, "node_modules/@react-oauth/google": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.2.8.tgz", - "integrity": "sha512-W3sRcU6kSZMGUOk10Vy5kPZPzvsi7+UpM2MxnT6fMVp+whDMKCVope5R01gwRydK9OI+0rozAARCD2NgrbkV7w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.6.0.tgz", + "integrity": "sha512-F+VtUk40C5zjL/E3VW5/nsIfm7gmad4HgpGjW3shHqsL6hopOEIncWIGlEzKs2H/hlZw5mHkyq2ONmw5TI2j/g==", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" @@ -4215,6 +4584,14 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.3.tgz", + "integrity": "sha512-YS64N9SNDT/NAvou3QNdzAu3E2om/W/0dhORimtPGLef+zSK5l1vDzfsWb4xgXOgfhtOI5ZDTRxnvRPb22AIVQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -4472,6 +4849,11 @@ "@types/node": "*" } }, + "node_modules/@types/stack-trace": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", + "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==" + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -5628,6 +6010,11 @@ "node": ">=8" } }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==" + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -5795,6 +6182,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dependencies": { + "semver": "^7.0.0" + } + }, + "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" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6264,6 +6670,11 @@ "node": ">=0.8" } }, + "node_modules/cron-schedule": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", + "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7135,83 +7546,417 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dependencies": { - "stackframe": "^1.3.4" + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "dependencies": { + "call-bind": "^1.0.2", + "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", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "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-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.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } + }, + "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==", + "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/esbuild": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz", + "integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.51", + "esbuild-android-arm64": "0.14.51", + "esbuild-darwin-64": "0.14.51", + "esbuild-darwin-arm64": "0.14.51", + "esbuild-freebsd-64": "0.14.51", + "esbuild-freebsd-arm64": "0.14.51", + "esbuild-linux-32": "0.14.51", + "esbuild-linux-64": "0.14.51", + "esbuild-linux-arm": "0.14.51", + "esbuild-linux-arm64": "0.14.51", + "esbuild-linux-mips64le": "0.14.51", + "esbuild-linux-ppc64le": "0.14.51", + "esbuild-linux-riscv64": "0.14.51", + "esbuild-linux-s390x": "0.14.51", + "esbuild-netbsd-64": "0.14.51", + "esbuild-openbsd-64": "0.14.51", + "esbuild-sunos-64": "0.14.51", + "esbuild-windows-32": "0.14.51", + "esbuild-windows-64": "0.14.51", + "esbuild-windows-arm64": "0.14.51" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz", + "integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz", + "integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz", + "integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz", + "integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz", + "integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz", + "integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz", + "integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz", + "integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz", + "integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz", + "integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz", + "integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz", + "integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz", + "integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz", + "integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz", + "integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz", + "integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "dependencies": { - "call-bind": "^1.0.2", - "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", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "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-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.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz", + "integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "node_modules/esbuild-windows-32": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz", + "integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dependencies": { - "has": "^1.0.3" + "node_modules/esbuild-windows-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz", + "integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "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==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz", + "integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, "node_modules/escalade": { @@ -9065,6 +9810,11 @@ "void-elements": "3.1.0" } }, + "node_modules/html-rewriter-wasm": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", + "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==" + }, "node_modules/html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -9105,6 +9855,11 @@ "entities": "^2.0.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -12438,6 +13193,64 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/miniflare": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.11.0.tgz", + "integrity": "sha512-QA18I1VQXdCo4nBtPJUcUDxW8c9xbc5ex5F61jwhkGVOISSnYdEheolESmjr8MYk28xwi0XD1ozS4rLaTONd+w==", + "dependencies": { + "@miniflare/cache": "2.11.0", + "@miniflare/cli-parser": "2.11.0", + "@miniflare/core": "2.11.0", + "@miniflare/d1": "2.11.0", + "@miniflare/durable-objects": "2.11.0", + "@miniflare/html-rewriter": "2.11.0", + "@miniflare/http-server": "2.11.0", + "@miniflare/kv": "2.11.0", + "@miniflare/queues": "2.11.0", + "@miniflare/r2": "2.11.0", + "@miniflare/runner-vm": "2.11.0", + "@miniflare/scheduler": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/sites": "2.11.0", + "@miniflare/storage-file": "2.11.0", + "@miniflare/storage-memory": "2.11.0", + "@miniflare/web-sockets": "2.11.0", + "kleur": "^4.1.4", + "semiver": "^1.1.0", + "source-map-support": "^0.5.20", + "undici": "5.9.1" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "@miniflare/storage-redis": "2.11.0", + "cron-schedule": "^3.0.4", + "ioredis": "^4.27.9" + }, + "peerDependenciesMeta": { + "@miniflare/storage-redis": { + "optional": true + }, + "cron-schedule": { + "optional": true + }, + "ioredis": { + "optional": true + } + } + }, + "node_modules/miniflare/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -12498,6 +13311,14 @@ "multicast-dns": "cli.js" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nano-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", @@ -12600,6 +13421,119 @@ "node": ">=8" } }, + "node_modules/npx-import": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/npx-import/-/npx-import-1.1.4.tgz", + "integrity": "sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==", + "dependencies": { + "execa": "^6.1.0", + "parse-package-name": "^1.0.0", + "semver": "^7.3.7", + "validate-npm-package-name": "^4.0.0" + } + }, + "node_modules/npx-import/node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/npx-import/node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/npx-import/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -12935,6 +13869,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-package-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-package-name/-/parse-package-name-1.0.0.tgz", + "integrity": "sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==" + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -15315,6 +16254,30 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", + "dependencies": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + } + }, + "node_modules/rollup-plugin-inject/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, + "node_modules/rollup-plugin-node-polyfills": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", + "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", + "dependencies": { + "rollup-plugin-inject": "^3.0.0" + } + }, "node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -15369,6 +16332,19 @@ "node": ">=8" } }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -15543,6 +16519,14 @@ "node": ">=10" } }, + "node_modules/semiver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", + "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", + "engines": { + "node": ">=6" + } + }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -15690,6 +16674,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -15917,6 +16906,14 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", @@ -15949,6 +16946,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -16682,6 +17687,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.9.1.tgz", + "integrity": "sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==", + "engines": { + "node": ">=12.18" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -16810,6 +17823,11 @@ "requires-port": "^1.0.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", + "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16863,6 +17881,17 @@ "node": ">=10.12.0" } }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -17646,6 +18675,43 @@ "workbox-core": "6.5.4" } }, + "node_modules/wrangler": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.8.1.tgz", + "integrity": "sha512-O4wUr6/FUpk9KVstOUVYbiiZcWc1jKo7q0FfdwEjnMB3oN7Ofs6cIiX++Lzj1ldFSCOw2/aW3UYgixch6B2WCA==", + "dependencies": { + "@cloudflare/kv-asset-handler": "^0.2.0", + "@esbuild-plugins/node-globals-polyfill": "^0.1.1", + "@esbuild-plugins/node-modules-polyfill": "^0.1.4", + "@miniflare/core": "2.11.0", + "@miniflare/d1": "2.11.0", + "@miniflare/durable-objects": "2.11.0", + "blake3-wasm": "^2.1.5", + "chokidar": "^3.5.3", + "esbuild": "0.14.51", + "miniflare": "2.11.0", + "nanoid": "^3.3.3", + "path-to-regexp": "^6.2.0", + "selfsigned": "^2.0.1", + "source-map": "^0.7.4", + "xxhash-wasm": "^1.0.1" + }, + "bin": { + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/wrangler/node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -17746,6 +18812,11 @@ "node": ">=0.4" } }, + "node_modules/xxhash-wasm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", + "integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -17802,6 +18873,25 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/youch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", + "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", + "dependencies": { + "@types/stack-trace": "0.0.29", + "cookie": "^0.4.1", + "mustache": "^4.2.0", + "stack-trace": "0.0.10" + } + }, + "node_modules/youch/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" + } } }, "dependencies": { @@ -19075,6 +20165,21 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "@cloudflare/kv-asset-handler": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", + "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", + "requires": { + "mime": "^3.0.0" + }, + "dependencies": { + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" + } + } + }, "@csstools/normalize.css": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", @@ -19328,6 +20433,28 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" }, + "@esbuild-plugins/node-globals-polyfill": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", + "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", + "requires": {} + }, + "@esbuild-plugins/node-modules-polyfill": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", + "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", + "requires": { + "escape-string-regexp": "^4.0.0", + "rollup-plugin-node-polyfills": "^0.2.1" + }, + "dependencies": { + "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==" + } + } + }, "@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -19397,6 +20524,11 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -19948,6 +21080,231 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@miniflare/cache": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.11.0.tgz", + "integrity": "sha512-L/kc9AzidPwFuk2fwHpAEePi0kNBk6FWUq3ln+9beRCDrPEpfVrDRFpNleF1NFZz5//oeVMuo8F0IVUQGzR7+Q==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "http-cache-semantics": "^4.1.0", + "undici": "5.9.1" + } + }, + "@miniflare/cli-parser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.11.0.tgz", + "integrity": "sha512-JUmyRzEGAS6CouvXJwBh8p44onfw3KRpfq5JGXEuHModOGjTp6li7PQyCTNPV2Hv/7StAXWnTFGXeAqyDHuTig==", + "requires": { + "@miniflare/shared": "2.11.0", + "kleur": "^4.1.4" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "@miniflare/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.11.0.tgz", + "integrity": "sha512-UFMFiCG0co36VpZkgFrSBnrxo71uf1x+cjlzzJi3khmMyDlnLu4RuIQsAqvKbYom6fi3G9Q8lTgM7JuOXFyjhw==", + "requires": { + "@iarna/toml": "^2.2.5", + "@miniflare/queues": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/watcher": "2.11.0", + "busboy": "^1.6.0", + "dotenv": "^10.0.0", + "kleur": "^4.1.4", + "set-cookie-parser": "^2.4.8", + "undici": "5.9.1", + "urlpattern-polyfill": "^4.0.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "@miniflare/d1": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/d1/-/d1-2.11.0.tgz", + "integrity": "sha512-aDdBVQZ2C0Zs3+Y9ZbRctmuQxozPfpumwJ/6NG6fBadANvune/hW7ddEoxyteIEU9W3IgzVj8s4by4VvasX90A==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/durable-objects": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.11.0.tgz", + "integrity": "sha512-0cKJaMgraTEU1b4kqK8cjD2oTeOjA6QU3Y+lWiZT/k1PMHZULovrSFnjii7qZ8npf4VHSIN6XYPxhyxRyEM65Q==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/storage-memory": "2.11.0", + "undici": "5.9.1" + } + }, + "@miniflare/html-rewriter": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.11.0.tgz", + "integrity": "sha512-olTqmuYTHnoTNtiA0vjQ/ixRfbwgPzDrAUbtXDCYW45VFbHfDVJrJGZX3Jg0HpSlxy86Zclle1SUxGbVDzxsBg==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "html-rewriter-wasm": "^0.4.1", + "undici": "5.9.1" + } + }, + "@miniflare/http-server": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.11.0.tgz", + "integrity": "sha512-sMLcrDFzqqAvnQmAUH0hRTo8sBjW79VZYfnIH5FAGSGcKX6kdAGs9RStdYZ4CftQCBAEQScX0KBsMx5FwJRe9Q==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/web-sockets": "2.11.0", + "kleur": "^4.1.4", + "selfsigned": "^2.0.0", + "undici": "5.9.1", + "ws": "^8.2.2", + "youch": "^2.2.2" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + }, + "ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "requires": {} + } + } + }, + "@miniflare/kv": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.11.0.tgz", + "integrity": "sha512-3m9dL2HBBN170V1JvwjjucR5zl4G3mlcsV6C1E7A2wLl2Z2TWvIx/tSY9hrhkD96dFnejwJ9qmPMbXMMuynhjg==", + "requires": { + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/queues": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/queues/-/queues-2.11.0.tgz", + "integrity": "sha512-fLHjdrNLKhn0LZM/aii/9GsAttFd+lWlGzK8HOg1R0vhfKBwEub4zntjMmOfFbDm1ntc21tdMK7n3ldUphwh5w==", + "requires": { + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/r2": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.11.0.tgz", + "integrity": "sha512-MKuyJ/gGNsK3eWbGdygvozqcyaZhM3C6NGHvoaZwH503dwN569j5DpatTWiHGFeDeSu64VqcIsGehz05GDUaag==", + "requires": { + "@miniflare/shared": "2.11.0", + "undici": "5.9.1" + } + }, + "@miniflare/runner-vm": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.11.0.tgz", + "integrity": "sha512-bkVSuvCf5+VylqN8lTiLxIYqYcKFbl+BywZGwGQndPC/3wh42J00mM0jw4hRbvXgwuBhlUyCVpEXtYlftFFT/g==", + "requires": { + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/scheduler": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.11.0.tgz", + "integrity": "sha512-DPdzINhdWeS99eIicGoluMsD4pLTTAWNQbgCv3CTwgdKA3dxdvMSCkNqZzQLiALzvk9+rSfj46FlH++HE7o7/w==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "cron-schedule": "^3.0.4" + } + }, + "@miniflare/shared": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.11.0.tgz", + "integrity": "sha512-fWMqq3ZkWAg+k7CnyzMV/rZHugwn+/JxvVzCxrtvxzwotTN547THlOxgZe8JAP23U9BiTxOfpTfnLvFEjAmegw==", + "requires": { + "@types/better-sqlite3": "^7.6.0", + "kleur": "^4.1.4", + "npx-import": "^1.1.3", + "picomatch": "^2.3.1" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "@miniflare/sites": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.11.0.tgz", + "integrity": "sha512-qbefKdWZUJgsdLf+kCw03sn3h/92LZgJAbkOpP6bCrfWkXlJ37EQXO4KWdhn4Ghc7A6GwU1s1I/mdB64B3AewQ==", + "requires": { + "@miniflare/kv": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/storage-file": "2.11.0" + } + }, + "@miniflare/storage-file": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.11.0.tgz", + "integrity": "sha512-beWF/lTX74x7AiaSB+xQxywPSNdhtEKvqDkRui8eOJ5kqN2o4UaleLKQGgqmCw3WyHRIsckV7If1qpbNiLtWMw==", + "requires": { + "@miniflare/shared": "2.11.0", + "@miniflare/storage-memory": "2.11.0" + } + }, + "@miniflare/storage-memory": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.11.0.tgz", + "integrity": "sha512-s0AhPww7fq/Jz80NbPb+ffhcVRKnfPi7H1dHTRTre2Ud23EVJjAWl2gat42x8NOT/Fu3/o/7A72DWQQJqfO98A==", + "requires": { + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/watcher": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.11.0.tgz", + "integrity": "sha512-RUfjz2iYcsQXLcGySemJl98CJ2iierbWsPGWZhIVZI+NNhROkEy77g/Q+lvP2ATwexG3/dUSfdJ3P8aH+sI4Ig==", + "requires": { + "@miniflare/shared": "2.11.0" + } + }, + "@miniflare/web-sockets": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.11.0.tgz", + "integrity": "sha512-NC8RKrmxrO0hZmwpzn5g4hPGA2VblnFTIBobmWoxuK95eW49zfs7dtE/PyFs+blsGv3CjTIjHVSQ782K+C6HFA==", + "requires": { + "@miniflare/core": "2.11.0", + "@miniflare/shared": "2.11.0", + "undici": "5.9.1", + "ws": "^8.2.2" + }, + "dependencies": { + "ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "requires": {} + } + } + }, "@mui/base": { "version": "5.0.0-alpha.101", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.101.tgz", @@ -20165,9 +21522,9 @@ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, "@react-oauth/google": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.2.8.tgz", - "integrity": "sha512-W3sRcU6kSZMGUOk10Vy5kPZPzvsi7+UpM2MxnT6fMVp+whDMKCVope5R01gwRydK9OI+0rozAARCD2NgrbkV7w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.6.0.tgz", + "integrity": "sha512-F+VtUk40C5zjL/E3VW5/nsIfm7gmad4HgpGjW3shHqsL6hopOEIncWIGlEzKs2H/hlZw5mHkyq2ONmw5TI2j/g==", "requires": {} }, "@remix-run/router": { @@ -20570,6 +21927,14 @@ "@babel/types": "^7.3.0" } }, + "@types/better-sqlite3": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.3.tgz", + "integrity": "sha512-YS64N9SNDT/NAvou3QNdzAu3E2om/W/0dhORimtPGLef+zSK5l1vDzfsWb4xgXOgfhtOI5ZDTRxnvRPb22AIVQ==", + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -20827,6 +22192,11 @@ "@types/node": "*" } }, + "@types/stack-trace": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", + "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==" + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -21675,6 +23045,11 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, + "blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==" + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -21809,6 +23184,22 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "requires": { + "semver": "^7.0.0" + } + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -22164,6 +23555,11 @@ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" }, + "cron-schedule": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", + "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==" + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -22844,6 +24240,153 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz", + "integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==", + "requires": { + "esbuild-android-64": "0.14.51", + "esbuild-android-arm64": "0.14.51", + "esbuild-darwin-64": "0.14.51", + "esbuild-darwin-arm64": "0.14.51", + "esbuild-freebsd-64": "0.14.51", + "esbuild-freebsd-arm64": "0.14.51", + "esbuild-linux-32": "0.14.51", + "esbuild-linux-64": "0.14.51", + "esbuild-linux-arm": "0.14.51", + "esbuild-linux-arm64": "0.14.51", + "esbuild-linux-mips64le": "0.14.51", + "esbuild-linux-ppc64le": "0.14.51", + "esbuild-linux-riscv64": "0.14.51", + "esbuild-linux-s390x": "0.14.51", + "esbuild-netbsd-64": "0.14.51", + "esbuild-openbsd-64": "0.14.51", + "esbuild-sunos-64": "0.14.51", + "esbuild-windows-32": "0.14.51", + "esbuild-windows-64": "0.14.51", + "esbuild-windows-arm64": "0.14.51" + } + }, + "esbuild-android-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz", + "integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==", + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz", + "integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==", + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz", + "integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==", + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz", + "integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==", + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz", + "integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==", + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz", + "integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==", + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz", + "integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==", + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz", + "integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==", + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz", + "integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==", + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz", + "integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==", + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz", + "integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==", + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz", + "integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==", + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz", + "integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==", + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz", + "integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==", + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz", + "integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==", + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz", + "integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==", + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz", + "integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==", + "optional": true + }, + "esbuild-windows-32": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz", + "integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==", + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz", + "integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==", + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.51", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz", + "integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==", + "optional": true + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -24187,6 +25730,11 @@ "void-elements": "3.1.0" } }, + "html-rewriter-wasm": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", + "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==" + }, "html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -24210,6 +25758,11 @@ "entities": "^2.0.0" } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -26622,6 +28175,41 @@ } } }, + "miniflare": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.11.0.tgz", + "integrity": "sha512-QA18I1VQXdCo4nBtPJUcUDxW8c9xbc5ex5F61jwhkGVOISSnYdEheolESmjr8MYk28xwi0XD1ozS4rLaTONd+w==", + "requires": { + "@miniflare/cache": "2.11.0", + "@miniflare/cli-parser": "2.11.0", + "@miniflare/core": "2.11.0", + "@miniflare/d1": "2.11.0", + "@miniflare/durable-objects": "2.11.0", + "@miniflare/html-rewriter": "2.11.0", + "@miniflare/http-server": "2.11.0", + "@miniflare/kv": "2.11.0", + "@miniflare/queues": "2.11.0", + "@miniflare/r2": "2.11.0", + "@miniflare/runner-vm": "2.11.0", + "@miniflare/scheduler": "2.11.0", + "@miniflare/shared": "2.11.0", + "@miniflare/sites": "2.11.0", + "@miniflare/storage-file": "2.11.0", + "@miniflare/storage-memory": "2.11.0", + "@miniflare/web-sockets": "2.11.0", + "kleur": "^4.1.4", + "semiver": "^1.1.0", + "source-map-support": "^0.5.20", + "undici": "5.9.1" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -26670,6 +28258,11 @@ "thunky": "^1.0.2" } }, + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" + }, "nano-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", @@ -26745,6 +28338,76 @@ "path-key": "^3.0.0" } }, + "npx-import": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/npx-import/-/npx-import-1.1.4.tgz", + "integrity": "sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==", + "requires": { + "execa": "^6.1.0", + "parse-package-name": "^1.0.0", + "semver": "^7.3.7", + "validate-npm-package-name": "^4.0.0" + }, + "dependencies": { + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==" + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" + } + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -26983,6 +28646,11 @@ "lines-and-columns": "^1.1.6" } }, + "parse-package-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-package-name/-/parse-package-name-1.0.0.tgz", + "integrity": "sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==" + }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -28497,6 +30165,31 @@ "fsevents": "~2.3.2" } }, + "rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "requires": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + } + } + }, + "rollup-plugin-node-polyfills": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", + "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", + "requires": { + "rollup-plugin-inject": "^3.0.0" + } + }, "rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -28541,6 +30234,21 @@ } } }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -28637,6 +30345,11 @@ "node-forge": "^1" } }, + "semiver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", + "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==" + }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -28766,6 +30479,11 @@ "send": "0.18.0" } }, + "set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -28929,6 +30647,11 @@ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, "stack-utils": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", @@ -28954,6 +30677,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -29497,6 +31225,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.9.1.tgz", + "integrity": "sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -29584,6 +31317,11 @@ "requires-port": "^1.0.0" } }, + "urlpattern-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", + "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -29625,6 +31363,14 @@ "source-map": "^0.7.3" } }, + "validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "requires": { + "builtins": "^5.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -30249,6 +31995,36 @@ "workbox-core": "6.5.4" } }, + "wrangler": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.8.1.tgz", + "integrity": "sha512-O4wUr6/FUpk9KVstOUVYbiiZcWc1jKo7q0FfdwEjnMB3oN7Ofs6cIiX++Lzj1ldFSCOw2/aW3UYgixch6B2WCA==", + "requires": { + "@cloudflare/kv-asset-handler": "^0.2.0", + "@esbuild-plugins/node-globals-polyfill": "^0.1.1", + "@esbuild-plugins/node-modules-polyfill": "^0.1.4", + "@miniflare/core": "2.11.0", + "@miniflare/d1": "2.11.0", + "@miniflare/durable-objects": "2.11.0", + "blake3-wasm": "^2.1.5", + "chokidar": "^3.5.3", + "esbuild": "0.14.51", + "fsevents": "~2.3.2", + "miniflare": "2.11.0", + "nanoid": "^3.3.3", + "path-to-regexp": "^6.2.0", + "selfsigned": "^2.0.1", + "source-map": "^0.7.4", + "xxhash-wasm": "^1.0.1" + }, + "dependencies": { + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + } + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -30319,6 +32095,11 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "xxhash-wasm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", + "integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -30357,6 +32138,24 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "youch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", + "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", + "requires": { + "@types/stack-trace": "0.0.29", + "cookie": "^0.4.1", + "mustache": "^4.2.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + } + } } } } diff --git a/frontend-web/package.json b/frontend-web/package.json index 1995d9e..471c854 100644 --- a/frontend-web/package.json +++ b/frontend-web/package.json @@ -8,7 +8,7 @@ "@fontsource/roboto": "^4.5.8", "@mui/icons-material": "^5.10.9", "@mui/joy": "^5.0.0-alpha.49", - "@react-oauth/google": "^0.2.8", + "@react-oauth/google": "^0.6.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -35,11 +35,17 @@ "recoil-persist": "^4.2.0", "sass": "^1.55.0", "typescript": "^4.8.4", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "wrangler": "^2.8.1" + }, + "engines": { + "node": ">=v16.18" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", + "deploy": "wrangler pages publish ./build", + "postinstall": "./generate_api_types.sh", "test": "react-scripts test", "eject": "react-scripts eject", "format": "eslint . --fix" diff --git a/frontend-web/src/components/AuthProvider.test.tsx b/frontend-web/src/components/AuthProvider.test.tsx index 02dd9ea..3bc39d4 100644 --- a/frontend-web/src/components/AuthProvider.test.tsx +++ b/frontend-web/src/components/AuthProvider.test.tsx @@ -26,7 +26,7 @@ describe(AuthProvider.name, () => { render(

child element

); expect(ErrorPage).toHaveBeenCalledWith( - expect.objectContaining({error: expect.stringContaining("REACT_APP_GOOGLE_AUTH_CLIENT_ID")}), + expect.objectContaining({error: expect.stringContaining("REACT_APP_OAUTH_CLIENT_ID")}), expect.anything(), ); }); diff --git a/frontend-web/src/components/AuthProvider.tsx b/frontend-web/src/components/AuthProvider.tsx index e12733e..9804467 100644 --- a/frontend-web/src/components/AuthProvider.tsx +++ b/frontend-web/src/components/AuthProvider.tsx @@ -1,16 +1,27 @@ import {GoogleOAuthProvider} from "@react-oauth/google"; import React from "react"; +import {configure} from "../configure"; +import {useAuth} from "../hooks/auth"; import ErrorPage from "../pages/ErrorPage"; +const AuthProviderConfigure = ({children}: { children: JSX.Element }) => { + + const {authState} = useAuth(); + configure(authState); + + return children; +} + + function AuthProvider({children, clientId}: { children: JSX.Element, clientId?: string }) { if (!clientId) { - return + return } return ( - {children} + {children} ); } diff --git a/frontend-web/src/configure.ts b/frontend-web/src/configure.ts index ffa0308..2f6a7f9 100644 --- a/frontend-web/src/configure.ts +++ b/frontend-web/src/configure.ts @@ -1,6 +1,9 @@ import {OpenAPI as FunctionIntegrationYnab} from "./generated/App.Function.Integration.Ynab"; +import {AuthState} from "./hooks/auth"; -export function configure() +export function configure(authState: AuthState | null) { + FunctionIntegrationYnab.TOKEN = authState?.token; + FunctionIntegrationYnab.BASE = process.env.REACT_APP_FUNCTION_INTEGRATION_YNAB_BASE || FunctionIntegrationYnab.BASE; } \ No newline at end of file diff --git a/frontend-web/src/index.test.tsx b/frontend-web/src/index.test.tsx index 8e54729..6825406 100644 --- a/frontend-web/src/index.test.tsx +++ b/frontend-web/src/index.test.tsx @@ -4,3 +4,5 @@ describe("bootstrap.index", () => { expect(true).toBeTruthy(); }); }); + +export {}; \ No newline at end of file diff --git a/frontend-web/src/index.tsx b/frontend-web/src/index.tsx index a76aee6..9e63350 100644 --- a/frontend-web/src/index.tsx +++ b/frontend-web/src/index.tsx @@ -18,13 +18,11 @@ import {ToastContainer} from "react-toastify"; import {RecoilRoot} from "recoil"; import AuthProvider from "./components/AuthProvider"; -import {configure} from "./configure"; import createQueryClient from "./createQueryClient"; import reportWebVitals from "./reportWebVitals"; import {routes} from "./routes"; import theme from "./theme"; -configure(); const root = ReactDOM.createRoot( document.getElementById("root") as HTMLElement ); @@ -39,7 +37,7 @@ root.render( - +