diff --git a/.github/actions/before-build/action.yml b/.github/actions/before-build/action.yml index a0f74cbaa6..9dbb3e1a4d 100644 --- a/.github/actions/before-build/action.yml +++ b/.github/actions/before-build/action.yml @@ -36,5 +36,5 @@ runs: shell: bash - name: "Build the project" - run: lerna run build:prod --scope quiet + run: lerna run build:prod --scope @quiet/desktop shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..3ffaa8c891 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,108 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + + - package-ecosystem: docker + directory: /3rd-party/tor + schedule: + interval: weekly + + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/backend-bundle + schedule: + interval: weekly + + - package-ecosystem: docker + directory: /packages/backend + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/backend + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/common + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/desktop + schedule: + interval: weekly + + - package-ecosystem: docker + directory: /packages/e2e-tests/docker + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/e2e-tests + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/eslint-config-custom + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/identity + schedule: + interval: weekly + + # Currently, the integration tests pkg is waiting for a clean up. + # @SEE: https://github.com/TryQuiet/quiet/pull/1734#discussion_r1302831794 + # - package-ecosystem: docker + # directory: /packages/integration-tests + # schedule: + # interval: weekly + + # - package-ecosystem: npm + # directory: /packages/integration-tests + # schedule: + # interval: weekly + + - package-ecosystem: npm + directory: /packages/logger + schedule: + interval: weekly + + - package-ecosystem: bundler + directory: /packages/mobile + schedule: + interval: weekly + + - package-ecosystem: docker + directory: /packages/mobile/android-environment + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/mobile/nodejs-modules/builtin_modules/rn-bridge + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/mobile + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/state-manager + schedule: + interval: weekly + + - package-ecosystem: npm + directory: /packages/types + schedule: + interval: weekly \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..80a688c61e --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ + +### Pull Request Checklist + +- [ ] I have linked this PR to related GitHub issue. +- [ ] I have updated the CHANGELOG.md file with relevant changes (the file is located at the root of monorepo). + +### (Optional) Mobile checklist + +Please ensure you completed the following checks if you did any changes to the mobile package: + +- [ ] I have run e2e tests for mobile +- [ ] I have updated base screenshots for visual regression tests diff --git a/.github/secrets/CI.mobileprovision.gpg b/.github/secrets/CI.mobileprovision.gpg deleted file mode 100644 index fa7e8e7346..0000000000 Binary files a/.github/secrets/CI.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/Certificates.p12.gpg b/.github/secrets/Certificates.p12.gpg index 30d97a6468..bd1a14ef78 100644 Binary files a/.github/secrets/Certificates.p12.gpg and b/.github/secrets/Certificates.p12.gpg differ diff --git a/.github/secrets/decrypt_secrets.sh b/.github/secrets/decrypt_secrets.sh index 5bdd715a42..19dfa83dc7 100755 --- a/.github/secrets/decrypt_secrets.sh +++ b/.github/secrets/decrypt_secrets.sh @@ -1,11 +1,13 @@ #!/bin/sh -gpg --quiet --batch --yes --decrypt --passphrase="$IOS_PROFILE_KEY" --output ./.github/secrets/CI.mobileprovision ./.github/secrets/CI.mobileprovision.gpg +set -e + +gpg --quiet --batch --yes --decrypt --passphrase="$IOS_PROFILE_KEY" --output ./.github/secrets/match_AppStore_comquietmobile.mobileprovision ./.github/secrets/match_AppStore_comquietmobile.mobileprovision.gpg gpg --quiet --batch --yes --decrypt --passphrase="$IOS_CERTIFICATE_KEY" --output ./.github/secrets/Certificates.p12 ./.github/secrets/Certificates.p12.gpg mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles -cp ./.github/secrets/CI.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/654a2214-095f-4939-a9e5-09f7a2ccf530.mobileprovision +cp ./.github/secrets/match_AppStore_comquietmobile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/718ac015-309f-49b6-9653-f6cf84a6377c.mobileprovision security create-keychain -p "" build.keychain @@ -17,4 +19,3 @@ security unlock-keychain -p "" ~/Library/Keychains/build.keychain security set-keychain-settings ~/Library/Keychains/build.keychain #this removes autolock security set-key-partition-list -S apple-tool:,apple: -s -k "" ~/Library/Keychains/build.keychain - diff --git a/.github/secrets/match_AppStore_comquietmobile.mobileprovision.gpg b/.github/secrets/match_AppStore_comquietmobile.mobileprovision.gpg new file mode 100644 index 0000000000..4f35bf8199 Binary files /dev/null and b/.github/secrets/match_AppStore_comquietmobile.mobileprovision.gpg differ diff --git a/.github/workflows/waggle-tests.yml b/.github/workflows/backend-tests.yml similarity index 94% rename from .github/workflows/waggle-tests.yml rename to .github/workflows/backend-tests.yml index 6bae30f594..1f41fdd237 100644 --- a/.github/workflows/waggle-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -17,7 +17,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env @@ -38,7 +38,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env @@ -59,7 +59,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env diff --git a/.github/workflows/check-visual-regression.yml b/.github/workflows/check-desktop-visual-regression.yml similarity index 81% rename from .github/workflows/check-visual-regression.yml rename to .github/workflows/check-desktop-visual-regression.yml index 1edc35e5d6..f86cfc14fd 100644 --- a/.github/workflows/check-visual-regression.yml +++ b/.github/workflows/check-desktop-visual-regression.yml @@ -1,4 +1,4 @@ -name: Visual regressions +name: Desktop visual regressions on: pull_request: @@ -17,18 +17,18 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Required to retrieve git history - name: Setup environment uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Publish to Chromatic" uses: chromaui/action@v1 with: workingDir: ./packages/desktop token: ${{ secrets.GH_TOKEN }} - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + projectToken: 3218757eee04 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 863782b5e1..7a05a53f69 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,7 +16,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Remove test files workaround (jest types conflicting with cypress types)" if: ${{ runner.os == 'Windows' }} @@ -27,14 +27,13 @@ jobs: uses: ./.github/actions/setup-env if: ${{ runner.os != 'Windows' }} with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,@quiet/mobile,e2e-tests,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,@quiet/mobile,e2e-tests,backend-bundle" - - name: "Setup environment for Windows" uses: ./.github/actions/setup-env if: ${{ runner.os == 'Windows' }} with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,e2e-tests,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,e2e-tests,backend-bundle" - name: "Lint" if: ${{ runner.os != 'Windows' }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..07ee9e671c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,73 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["develop"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["develop"] + schedule: + - cron: "0 0 * * 1" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["javascript", "python", "ruby", "typescript"] + # CodeQL supports [ $supported-codeql-languages ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/depencency-review.yml b/.github/workflows/depencency-review.yml new file mode 100644 index 0000000000..7989380270 --- /dev/null +++ b/.github/workflows/depencency-review.yml @@ -0,0 +1,22 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - name: 'Dependency Review' + uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 # v3.1.0 diff --git a/.github/workflows/build-release.yml b/.github/workflows/desktop-build.yml similarity index 77% rename from .github/workflows/build-release.yml rename to .github/workflows/desktop-build.yml index c02ec9b3a4..84e6c79ab5 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/desktop-build.yml @@ -1,4 +1,4 @@ -name: Build release +name: Desktop release build on: release: @@ -6,18 +6,27 @@ on: [released, prereleased] jobs: + run-e2e-tests-linux: + if: | + startsWith(github.ref, 'refs/tags/@quiet/desktop') uses: ./.github/workflows/e2e-linux.yml + run-e2e-tests-mac: + if: | + startsWith(github.ref, 'refs/tags/@quiet/desktop') uses: ./.github/workflows/e2e-mac.yml + run-e2e-tests-win: + if: | + startsWith(github.ref, 'refs/tags/@quiet/desktop') uses: ./.github/workflows/e2e-win.yml build-linux: - needs: run-e2e-tests-linux + # needs: run-e2e-tests-linux runs-on: ubuntu-22.04 if: | - startsWith(github.ref, 'refs/tags/quiet') + startsWith(github.ref, 'refs/tags/@quiet/desktop') env: TEST_MODE: ${{ github.event.action == 'prereleased' }} @@ -25,12 +34,12 @@ jobs: CHECKSUM_PATH: ${{ github.event.action == 'released' && 'packages/desktop/dist/latest-linux.yml' || 'packages/desktop/dist/alpha-linux.yml' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: Install libfuse run: sudo apt install libfuse2 @@ -52,7 +61,7 @@ jobs: run: cd packages/desktop && USE_HARD_LINKS=false node_modules/.bin/electron-builder -p always --linux ${{ env.ELECTRON_BUILDER_PROPS }} - name: "Calculate new checksum for electron updater" - run: lerna run postBuild --scope quiet + run: lerna run postBuild --scope @quiet/desktop - name: "Push electron-updater new checksum to S3" uses: vinkabuki/upload-s3-action@master @@ -86,17 +95,17 @@ jobs: asset_content_type: application/.AppImage build-macos: - needs: run-e2e-tests-mac + # needs: run-e2e-tests-mac runs-on: macos-latest if: | - startsWith(github.ref, 'refs/tags/quiet') + startsWith(github.ref, 'refs/tags/@quiet/desktop') env: TEST_MODE: ${{ github.event.action == 'prereleased' }} S3_BUCKET: ${{ github.event.action == 'released' && 'quiet' || 'test.quiet' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/components-nightly@v1 with: @@ -111,7 +120,7 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: Before build uses: ./.github/actions/before-build @@ -144,7 +153,7 @@ jobs: - name: Extract version id: extract_version - uses: Saionaro/extract-package-version@v1.1.1 + uses: Saionaro/extract-package-version@v1.2.1 with: path: packages/desktop @@ -162,14 +171,14 @@ jobs: # needs: run-e2e-tests-win runs-on: windows-2019 if: | - startsWith(github.ref, 'refs/tags/quiet') + startsWith(github.ref, 'refs/tags/@quiet/desktop') env: TEST_MODE: ${{ github.event.action == 'prereleased' }} S3_BUCKET: ${{ github.event.action == 'released' && 'quiet' || 'test.quiet' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Fetch jsign" shell: bash @@ -189,58 +198,13 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle,e2e-tests" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle,e2e-tests" - name: Before build uses: ./.github/actions/before-build with: source-path: win32 - #E2E - Start - # - name: E2E - electron-builder - # env: - # CERTIFICATE_PATH: ${{ steps.write_file.outputs.filePath }} - # WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} - # WINDOWS_ALIAS: ${{ secrets.WIN_ALIAS }} - # working-directory: ./packages/desktop - # run: node_modules/.bin/electron-builder --win - # shell: bash - - # - name: E2E - Extract version - # id: extract_version - # uses: Saionaro/extract-package-version@v1.1.1 - # with: - # path: packages/desktop - - # - name: E2E - FILE_NAME env - # working-directory: ./packages/desktop/dist - # run: echo "FILE_NAME=Quiet Setup ${{ steps.extract_version.outputs.version }}.exe" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - # shell: powershell - - # - name: E2E - Chmod - # working-directory: ./packages/desktop/dist - # run: chmod +x "$FILE_NAME" - # shell: bash - - # - name: E2E - Install exe - # run: Start-Process "Quiet Setup ${{ steps.extract_version.outputs.version }}.exe" -Wait - # working-directory: ./packages/desktop/dist - # shell: powershell - - # - name: E2E - Kill exe - # run: Stop-Process -Name "Quiet" -Force - # working-directory: ./packages/desktop/dist - # shell: powershell - - # - name: E2E - Run smoke test - # uses: nick-fields/retry@v2 - # with: - # timeout_minutes: 25 - # max_attempts: 3 - # shell: bash - # command: cd packages/e2e-tests && npm run test smoke.crossplatform.test.ts - #E2E - End - - name: "Set electron-builder props" shell: bash run: echo "ELECTRON_BUILDER_PROPS=-c.publish.bucket=$S3_BUCKET" >> $GITHUB_ENV @@ -265,7 +229,7 @@ jobs: - name: Extract version id: extract_version - uses: Saionaro/extract-package-version@v1.1.1 + uses: Saionaro/extract-package-version@v1.2.1 with: path: packages/desktop diff --git a/.github/workflows/desktop-rtl-tests.yml b/.github/workflows/desktop-rtl-tests.yml new file mode 100644 index 0000000000..8e8343b736 --- /dev/null +++ b/.github/workflows/desktop-rtl-tests.yml @@ -0,0 +1,31 @@ +name: Desktop - state-manager bracket tests (RTL) + +on: + pull_request: + paths: + - packages/desktop/** + - packages/state-manager/** + +jobs: + desktop-tests: + timeout-minutes: 25 + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-20.04, macos-latest] + + steps: + - name: "Print OS" + run: echo ${{ matrix.os }} + + - uses: actions/checkout@v3 + + - name: "Setup environment" + uses: ./.github/actions/setup-env + with: + cachePrefix: "desktop-tests" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" + + - name: "Desktop - state-manager bracket tests" + run: lerna run rtl-test --scope @quiet/desktop --stream diff --git a/.github/workflows/regression-test.yml b/.github/workflows/desktop-test-scroll.yml similarity index 84% rename from .github/workflows/regression-test.yml rename to .github/workflows/desktop-test-scroll.yml index 1eb554931e..bb9a6c6a7c 100644 --- a/.github/workflows/regression-test.yml +++ b/.github/workflows/desktop-test-scroll.yml @@ -1,4 +1,4 @@ -name: Scroll regression tests +name: Desktop scroll regression tests on: pull_request: @@ -18,12 +18,12 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Install libs" run: sudo apt-get update && sudo apt-get install -y libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb @@ -31,14 +31,14 @@ jobs: - name: "Remove test files workaround" run: find packages/desktop/src -name '*.test.*' -delete && find packages/backend/src -name '*.test.*' -delete - - uses: cypress-io/github-action@v2 + - uses: cypress-io/github-action@v6 with: install: false command: npm run regression-test:ci working-directory: packages/desktop - name: Archive test screenshots - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: always() with: name: test-screenshots-linux diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/desktop-tests.yml similarity index 71% rename from .github/workflows/frontend-tests.yml rename to .github/workflows/desktop-tests.yml index fbdbdf19d9..bba3708c19 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/desktop-tests.yml @@ -18,16 +18,13 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env with: cachePrefix: "desktop-tests" - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Unit tests" - run: lerna run test --scope quiet --stream - - - name: "desktop-state-manager bracket tests" - run: lerna run rtl-test --scope quiet --stream + run: lerna run test --scope @quiet/desktop --stream diff --git a/.github/workflows/e2e-android.yml b/.github/workflows/e2e-android.yml new file mode 100644 index 0000000000..1181a13983 --- /dev/null +++ b/.github/workflows/e2e-android.yml @@ -0,0 +1,34 @@ +name: E2E Android + +on: + push: + paths: + - packages/mobile/** + +jobs: + detox-android: + timeout-minutes: 10 + runs-on: [self-hosted, macOS, ARM64, android] + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + npm i + npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle + + - name: Pull binaries + run: | + git lfs install + git lfs pull + + - name: Build Detox + run: | + cd packages/mobile + detox build -c android.emu.debug.ci + + - name: Run basic tests + run: | + cd packages/mobile + detox test starter -c android.emu.debug.ci diff --git a/.github/workflows/e2e-crossplatform.yml b/.github/workflows/e2e-crossplatform.yml index 42f4065efb..b45d5caddb 100644 --- a/.github/workflows/e2e-crossplatform.yml +++ b/.github/workflows/e2e-crossplatform.yml @@ -1,6 +1,14 @@ name: E2E cross platform -on: [pull_request] +on: + pull_request: + paths: + - packages/desktop/** + - packages/backend/** + - packages/state-manager/** + - packages/identity/** + - packages/common/** + jobs: mac: uses: ./.github/workflows/e2e-mac.yml diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml new file mode 100644 index 0000000000..a834ada8e7 --- /dev/null +++ b/.github/workflows/e2e-ios.yml @@ -0,0 +1,39 @@ +name: E2E iOS + +on: + push: + paths: + - packages/mobile/** + +jobs: + detox-ios: + timeout-minutes: 10 + runs-on: [self-hosted, macOS, ARM64, iOS] + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + npm i + npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle + + - name: Pull binaries + run: | + git lfs install + git lfs pull + + - name: Install pods + run: | + cd packages/mobile/ios + pod install + + - name: Build Detox + run: | + cd packages/mobile + detox build -c ios.sim.debug.ci + + - name: Run basic tests + run: | + cd packages/mobile + detox test starter -c ios.sim.debug.ci diff --git a/.github/workflows/e2e-linux.yml b/.github/workflows/e2e-linux.yml index 6fcfe83493..2ed3a4995e 100644 --- a/.github/workflows/e2e-linux.yml +++ b/.github/workflows/e2e-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, ubuntu-22.04] + os: [ubuntu-latest-m] timeout-minutes: 180 @@ -18,7 +18,7 @@ jobs: TEST_MODE: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install WM run: sudo apt install fluxbox @@ -30,7 +30,7 @@ jobs: uses: ./.github/actions/setup-env with: cachePrefix: "e2e-crossplatform-linux" - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle,e2e-tests" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle,e2e-tests" - name: Run X11 run: | @@ -53,18 +53,18 @@ jobs: max_attempts: 3 command: cd packages/e2e-tests && npm run test oneClient.test.ts - - name: Run two clients test + - name: Run multiple clients test uses: nick-fields/retry@v2 with: timeout_minutes: 25 max_attempts: 3 - command: cd packages/e2e-tests && npm run test twoClients.test.ts + command: cd packages/e2e-tests && npm run test multipleClients.test.ts - name: Run invitation link test - Includes 2 separate application clients uses: nick-fields/retry@v2 with: timeout_minutes: 25 - max_attempts: 3 + max_attempts: 1 command: cd packages/e2e-tests && npm run test invitationLink.test.ts - name: Download App Image 1.2.0 @@ -74,10 +74,10 @@ jobs: - name: Chmod App Image 1.2.0 working-directory: ./packages/e2e-tests/Quiet run: chmod +x Quiet-1.2.0.AppImage - + - name: Run Backwards Compatibility test uses: nick-fields/retry@v2 with: timeout_minutes: 15 max_attempts: 3 - command: cd packages/e2e-tests && npm run test backwardsCompatibility.test.ts \ No newline at end of file + command: cd packages/e2e-tests && npm run test backwardsCompatibility.test.ts diff --git a/.github/workflows/e2e-mac.yml b/.github/workflows/e2e-mac.yml index 8e1b0c5e17..52154c22dd 100644 --- a/.github/workflows/e2e-mac.yml +++ b/.github/workflows/e2e-mac.yml @@ -11,13 +11,13 @@ jobs: IS_E2E: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env with: cachePrefix: "e2e-crossplatform-mac" - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle,e2e-tests" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle,e2e-tests" - name: Before build uses: ./.github/actions/before-build @@ -61,11 +61,11 @@ jobs: max_attempts: 3 command: cd packages/e2e-tests && npm run test oneClient.test.ts - - name: Run two clients test + - name: Run multiple clients test uses: nick-fields/retry@v2 with: timeout_minutes: 25 max_attempts: 3 - command: cd packages/e2e-tests && npm run test twoClients.test.ts + command: cd packages/e2e-tests && npm run test multipleClients.test.ts diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index d03967a883..9f614ca00f 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -3,21 +3,23 @@ name: E2E Windows on: [workflow_call] jobs: windows: - runs-on: windows-2019 + runs-on: windows-latest-l + timeout-minutes: 180 + env: ELECTRON_CUSTOM_VERSION: 23.0.0 TEST_MODE: true E2E: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env with: cachePrefix: "e2e-crossplatform-windows" - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle,e2e-tests" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle,e2e-tests" - name: "Fetch jsign" shell: bash @@ -34,12 +36,12 @@ jobs: - name: electron-builder working-directory: ./packages/desktop - run: node_modules/.bin/electron-builder --win + run: node_modules/.bin/electron-builder --win shell: bash - name: Extract version id: extract_version - uses: Saionaro/extract-package-version@v1.1.1 + uses: Saionaro/extract-package-version@v1.2.1 with: path: packages/desktop @@ -54,7 +56,7 @@ jobs: shell: bash - name: "Upload built app" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: quiet-windows path: ./packages/desktop/dist/Quiet Setup ${{ steps.extract_version.outputs.version }}.exe @@ -64,13 +66,18 @@ jobs: run: Start-Process "Quiet Setup ${{ steps.extract_version.outputs.version }}.exe" -Wait working-directory: ./packages/desktop/dist shell: powershell + + - name: Check if Quiet installed properly + run: Get-ChildItem -Path C:\Users\runneradmin\AppData\Local\Programs\@quietdesktop + shell: powershell - name: Kill Quiet - run: Stop-Process -Name "Quiet" -Force + run: Get-Process -Name "Quiet" -ErrorAction SilentlyContinue | Stop-Process -Force shell: powershell + if: always() - name: Kill tor - run: Stop-Process -Name "tor" -Force + run: Get-Process -Name "tor" -ErrorAction SilentlyContinue | Stop-Process -Force shell: powershell - name: Delay @@ -85,13 +92,13 @@ jobs: shell: bash command: cd packages/e2e-tests && npm run test oneClient.test.ts - - name: Run two clients test + - name: Run multiple clients test uses: nick-fields/retry@v2 with: timeout_minutes: 30 max_attempts: 3 shell: bash - command: cd packages/e2e-tests && npm run test twoClients.test.ts + command: cd packages/e2e-tests && npm run test multipleClients.test.ts - name: Run invitation link test - Includes 2 separate application clients uses: nick-fields/retry@v2 @@ -99,4 +106,4 @@ jobs: timeout_minutes: 25 max_attempts: 3 shell: bash - command: cd packages/e2e-tests && npm run test invitationLink.test.ts \ No newline at end of file + command: cd packages/e2e-tests && npm run test invitationLink.test.ts diff --git a/.github/workflows/identity-tests.yml b/.github/workflows/identity-tests.yml index 6a83d07a21..033308b099 100644 --- a/.github/workflows/identity-tests.yml +++ b/.github/workflows/identity-tests.yml @@ -18,7 +18,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 48bc41d289..18680ddb4f 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,7 +20,7 @@ jobs: - name: 'Print OS' run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env diff --git a/.github/workflows/deploy-android.yaml b/.github/workflows/mobile-deploy-android.yaml similarity index 90% rename from .github/workflows/deploy-android.yaml rename to .github/workflows/mobile-deploy-android.yaml index 065ba52f2c..ccb0aed4ee 100644 --- a/.github/workflows/deploy-android.yaml +++ b/.github/workflows/mobile-deploy-android.yaml @@ -1,25 +1,26 @@ -name: Deploy Android to Google Play (internal testing) +name: Deploy Android to Google Play on: release: - types: + types: [prereleased, released] jobs: build-android: + # needs: detox-android runs-on: ${{ matrix.os }} if: | startsWith(github.ref, 'refs/tags/@quiet/mobile') strategy: matrix: - os: [ubuntu-20.04] + os: [ubuntu-latest-m] steps: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Set up JDK" uses: actions/setup-java@v3 @@ -33,7 +34,7 @@ jobs: with: ndk-version: r21e add-to-path: false - + - name: "Setup environment" uses: ./.github/actions/setup-env with: @@ -41,13 +42,13 @@ jobs: - name: "Create gradle configuration directory" run: mkdir -p $HOME/.gradle - + - name: "Prepare ndk configuration" run: | printf "\ NDK_PATH=${{ steps.setup-ndk.outputs.ndk-path }}\n\ " > $HOME/.gradle/gradle.properties - + - name: "Prepare signing configuration" run: | printf "\ @@ -64,7 +65,8 @@ jobs: run: cd ./packages/mobile/android && ENVFILE=../.env.production ./gradlew bundleStandardRelease - name: "Upload Artifact" - uses: actions/upload-artifact@v2 + continue-on-error: true + uses: actions/upload-artifact@v3 with: name: app-standard-release.aab path: ./packages/mobile/android/app/build/outputs/bundle/standardRelease/app-standard-release.aab @@ -75,9 +77,9 @@ jobs: mkdir -p distribution/whatsnew git log -1 --pretty=format:%s > distribution/whatsnew/whatsnew-pl-PL echo ${{ SECRETS.SERVICE_ACCOUNT_JSON }} | base64 --decode > google-play.json - + - name: "Upload to Google Play" - uses: r0adkll/upload-google-play@v1.0.17 + uses: r0adkll/upload-google-play@v1.1.2 with: serviceAccountJson: google-play.json packageName: com.quietmobile @@ -85,4 +87,3 @@ jobs: status: completed track: internal whatsNewDirectory: distribution/whatsnew - changesNotSentForReview: true diff --git a/.github/workflows/deploy-ios.yml b/.github/workflows/mobile-deploy-ios.yml similarity index 93% rename from .github/workflows/deploy-ios.yml rename to .github/workflows/mobile-deploy-ios.yml index 65964761c3..e5c85d39f4 100644 --- a/.github/workflows/deploy-ios.yml +++ b/.github/workflows/mobile-deploy-ios.yml @@ -7,6 +7,7 @@ on: jobs: build-ios: + # needs: detox-ios runs-on: ${{ matrix.os }} if: | startsWith(github.ref, 'refs/tags/@quiet/mobile') @@ -19,7 +20,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: lfs: true @@ -57,7 +58,7 @@ jobs: -scheme Quiet \ -configuration Release \ -archivePath build/Quiet.xcarchive \ - PROVISIONING_PROFILE="654a2214-095f-4939-a9e5-09f7a2ccf530" \ + PROVISIONING_PROFILE="718ac015-309f-49b6-9653-f6cf84a6377c" \ CODE_SIGN_IDENTITY="Apple Distribution: Zbay LLC (CTYKSWN9T4)" - name: Export .ipa @@ -70,7 +71,7 @@ jobs: -exportPath build/ - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: Quiet.ipa path: ./packages/mobile/ios/build/Quiet.ipa diff --git a/.github/workflows/mobile-tests.yml b/.github/workflows/mobile-tests.yml index ad9e047059..46f8ba291c 100644 --- a/.github/workflows/mobile-tests.yml +++ b/.github/workflows/mobile-tests.yml @@ -18,7 +18,7 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-env diff --git a/.github/workflows/nectar-tests.yml b/.github/workflows/state-manager-tests.yml similarity index 77% rename from .github/workflows/nectar-tests.yml rename to .github/workflows/state-manager-tests.yml index b5e3b966bb..2ecad2b0b1 100644 --- a/.github/workflows/nectar-tests.yml +++ b/.github/workflows/state-manager-tests.yml @@ -18,15 +18,12 @@ jobs: - name: "Print OS" run: echo ${{ matrix.os }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: "Setup environment" uses: ./.github/actions/setup-env with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,quiet,backend-bundle" + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Unit tests" run: lerna run test --scope @quiet/state-manager --stream - - - name: "desktop-state-manager bracket tests" - run: lerna run rtl-test --scope quiet --stream diff --git a/.gitignore b/.gitignore index fc30407b99..8b22c51fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules lerna-debug.log +c4/.structurizr +c4/workspace.json diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..72c7744b30 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.12.1 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bf50cb71ea..3f8f4279fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,89 @@ -[unreleased] +[2.0.3-alpha.1] + +* Temporarily hiding leave community button from Possible impersonation attack + +[2.0.3-alpha.0] + +* Filter CSRs - remove old csrs and replace with new for each pubkey + +* Fixed mobile bugs - joining by QR code and not showing username taken screen for user who has unique name + +* Use context menu for information about unregistered username instead screen + +* Shorter dots-placeholder for invite link + +* Display a shorter invite link on a mobile + +* Removed registration attempts selector and corresponding usage. + +* Revert adjusting bootstrap scripts for developing on Windows + +* Channel input - replaced ContentEditable with textarea + +* Fix - up/down arrows now work properly inside channel input (textarea) + +[2.0.1-alpha.2] + +* UI layer for taken usernames for desktop and mobile + +* Change nickname for taken username + +* Map messages sent before changing username + +* Update registrar service to match new registration flow. + +* Add possible impersonation attack UI for desktop and mobile + +* Fix truncated long messages in channelInput component + +* Unblock mobile e2e tests + +* Prettify loading component on Chat screen (mobile) + +* Running Chromatic tests for forked PRs + +* Added e2e test for user joining community when owner is offline. Improved e2e tests + +* Bump github actions/* to versions using node16 + +* Project can now be bootstraped on Windows (powershell) + +* Placeholder(...) for community name + +* No unregistered/duplicated label for system messages + +[2.0.0-alpha.11] + +* Customize Launch Screen on iOS + +* Suspends certain websocket events until backend becomes fully operative (faster and dumber frontend). + +* Replaced greying out inputs with splash screen on joining/creating screens. + +* Fixes empty space between chat's input and a soft keyboard on iOS devices. + +* Changed registration process - user connects to the libp2p network directly instead of using registrar. Invitation link format changed. User csr is now saved to database. * Fixed android stucking on username registration screen introduced in previous alpha. -* Removed code responsible for data translation from channel address to channel id from state manager transforms and storage service \ No newline at end of file +* Added creator username to initial channel message. + +* Fixed bug with changing joining community/create community screens with required field. + +* Fixed bug with displaying incorrect default settings tab. + +* Replaced source of publicKey in sendMessage saga to CSR + +* Labels for unregistered and duplicate usernames with modals + +* Fixed LoadingPanel useEffect bug. + +* Use csrs instead of certificates as a source of user data + +* Integration state manager layer with UI layer(desktop and mobile) + +* Clarify autoupdate language in update modal to let users know that the app will update on restart. + +* C4 for Quiet architecture. Context and Container diagrams. + +* Invite tab as default in settings diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..92d8a7ac26 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing + +## Development workflow + +> **New to open source?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). + +1. Fork the repo and create your branch from `develop` (a guide on [how to fork a repository](https://help.github.com/articles/fork-a-repo/)). +2. Being in the root directory, run `npm i` and then `lerna bootstrap`, to setup the development environment. +3. Check out [desktop](https://github.com/TryQuiet/quiet/blob/develop/packages/desktop/README.md) and [mobile](https://github.com/TryQuiet/quiet/blob/develop/packages/mobile/README.md) README.md for more instructions. + +## Commit message convention + +Prefix commit messages with one of the following to signify the kind of change: + +- `fix:` bug fixes, e.g. fix incorrect error message. +- `feat:` new features, e.g. add useful API. +- `refactor:` code/structure refactor, e.g. new folder structure. +- `docs:` changes into documentation, e.g. add usage example for getByText. +- `test:` adding or updating tests, eg unit, snapshot testing. +- `chore:` tooling changes, e.g. change circle ci config. +- `BREAKING:` for changes that break existing usage, e.g. change API. + +Commit message should be followed by the issue number, eg. +`fix: typo #2002` + +### (optional) Setting up a local git hook for verifying message convention: + +1. Navigate to git repository in your terminal. +2. Go to the .git/hooks directory: +``` +cd .git/hooks +``` +3. Create (or edit if it already exists) the commit-msg file +4. Make the file executable: +``` +chmod +x commit-msg +``` +5. Edit the commit-msg file and paste the following shell script: +``` +#!/bin/bash + +COMMIT_MSG_FILE=$1 +COMMIT_MSG=$(cat $COMMIT_MSG_FILE) + +if ! echo "$COMMIT_MSG" | grep -E '^(fix:|feat:|refactor:|docs:|test:|chore:|BREAKING:|Publish)' > /dev/null; then + echo "error: invalid commit message format" + echo "Valid formats are fix: feat: refactor: docs: test: chore: BREAKING:" + exit 1 +fi +``` diff --git a/README.md b/README.md index 61769a5524..d0c76e8899 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Encrypted p2p team chat with no servers, just Tor.
- Downloads | + Downloads | How it Works | Features | Threat Model | @@ -52,7 +52,7 @@ See our [FAQ](https://github.com/TryQuiet/monorepo/wiki/Quiet-FAQ) for answers t ## Getting started -To try Quiet, download the [latest release](https://github.com/TryQuiet/quiet/releases/tag/quiet%401.8.0) for your platform (.dmg for macOS, .exe for Windows, etc.) and install it in the normal way. Then create a community and open the community's settings to invite members. +To try Quiet, download the [latest release](https://github.com/TryQuiet/quiet/releases/tag/quiet%401.9.6) for your platform (.dmg for macOS, .exe for Windows, etc.) and install it in the normal way. Then create a community and open the community's settings to invite members. If you'd like to help develop Quiet, see [Contributing to Quiet](#contributing-to-quiet). @@ -68,11 +68,12 @@ If you'd like to help develop Quiet, see [Contributing to Quiet](#contributing-t * **Keyboard Controls** - Navigate channels without using the mouse. * **Desktop Apps** - Desktop apps for Mac, Windows, and Linux. * **Android App** - A fully peer-to-peer Android app with working notifications. +* **iOS App** - A fully peer-to-peer iOS app (TestFlight) without notifications. * **No email or phone number required** - Unlike Slack, Discord, WhatsApp, Telegram, and Signal, no email or phone number is required to create or join a community. ## Planned (but still-missing) features -* **iOS App** - Join communities and sync messages on iOS, with no central server. +* **iOS Notifications** - Receive notifications on iOS, with help from a service Apple requires to be centralized. * **Direct Messages** - Send and receive direct messages that are encrypted to the recipient and unreadable by other community members. * **Mentions** - Send @ mentions that notify other users. * **Removal** - Remove users from your community. @@ -87,7 +88,7 @@ If you'd like to help develop Quiet, see [Contributing to Quiet](#contributing-t ## Post-1.0 Features * **Large Communities** - Create a community with 1000 members or more (right now ~30-100 members is the limit.) -* **Moderation** - Appoint moderators who can hide messages and shadowban or remove users. +* **Moderation** - Appoint moderators who can hide messages and silence or remove users. * **Spam and Denial-of-Service Protection** - Settings to automatically remove users who send disruptive messages. * **Search** - Robust message search. * **Threads** - Reply to messages in threads. diff --git a/c4/README.md b/c4/README.md new file mode 100644 index 0000000000..084e027ea8 --- /dev/null +++ b/c4/README.md @@ -0,0 +1,28 @@ +# Quiet C4 Diagrams + +Quiet architecture presented on C4 diagrams. Currently it uses 2 diagrams - Context and Container. + +Made in Structurzir + +Current views saved in `/current` + +## Run locally + +1. Make sure you have Docker installed +2. Run command in terminal: ```docker pull structurizr/lite``` +3. Run docker container inside your Quiet directory and choosen porsts, example : ```docker run -it --rm -p 8080:8080 -v /__PATH_TO_REPOSITORY__/c4:/usr/local/structurizr structurizr/lite``` +4. Open `http://localhost:8080/` + +## Editing + +Everything is in file `workspace.dsl` + +After change, save file and reload page. + +Highly recommend extenstions for VSC: C4 DSL Extension and Structurizr. + +## Docs + +https://c4model.com/ + +https://docs.structurizr.com/ diff --git a/c4/current/C1-Context.png b/c4/current/C1-Context.png new file mode 100644 index 0000000000..3a57192bc3 Binary files /dev/null and b/c4/current/C1-Context.png differ diff --git a/c4/current/C2-Container.png b/c4/current/C2-Container.png new file mode 100644 index 0000000000..85113f55ad Binary files /dev/null and b/c4/current/C2-Container.png differ diff --git a/c4/workspace.dsl b/c4/workspace.dsl new file mode 100644 index 0000000000..e8c6056b9a --- /dev/null +++ b/c4/workspace.dsl @@ -0,0 +1,156 @@ +workspace { + + model { + + properties { + "structurizr.groupSeparator" "/" + } + + userA = person "User A" "Owner of Quiet community" + userB = person "User B" "Member of Quiet community" + userC = person "User C" "Member of Quiet community" + userD = person "User D" "Member of Quiet community" + + quietB = softwareSystem "Quiet App B" "Linux/MacOS/Windows/Android/iOS" { + userB -> this + } + + quietC = softwareSystem "Quiet App C" "Linux/MacOS/Windows/Android/iOS" { + userC -> this + } + + quietD = softwareSystem "Quiet App D" "Linux/MacOS/Windows/Android/iOS" { + userD -> this "One peer is enough for replicating all data" + } + + quietA = softwareSystem "Quiet App" "Linux/MacOS/Windows/Android/iOS" { + + desktops = group "Desktop Apps" { + + linux = container "Linux" "React & Electron" { + userA -> this + } + + macOS = container "MacOS" "React & Electron" { + userA -> this + } + + windows = container "Windows" "React & Electron" { + userA -> this + } + } + + mobiles = group "Mobiles" { + + android = container "Android" "UI layer" "React Native"{ + userA -> this + } + + iOS = container "iOS" "UI layer" "React Native"{ + userA -> this + } + + androidBackgroundWorker = group "Android Background Worker" { + + cpp = container "CPP" { + + } + + nodeJSAndroid = container "Node.js Android" { + android -> this + cpp -> this + } + + notifications = container "Notifications" "Java" { + nodeJSAndroid -> this "Conntected via WebSocket" + } + + } + + nodeJSiOS = container "Node.js iOS" { + iOS -> this + } + + + } + + stateManager = container "State Manager" "Redux Toolkit & Redux Saga" { + linux -> this + macOS -> this + windows -> this + android -> this + iOS -> this + } + + + backend = group "Backend" { + + nest = container "Nest JS" "Connection Manager as a core mediator between other services" { + stateManager -> this "Conntected via Socket IO" + nodeJSAndroid -> this + nodeJSiOS -> this + } + + reigstration = container "Registration Service" { + nest -> this + } + + levelDB = container "levelDB" "local database" { + nest -> this + } + + storage = group "Storage" { + + orbitDB = container "orbitDB" { + nest -> this + } + + ipfs = container "IPFS" { + orbitDB -> this + } + + libp2p = container "libp2p"{ + ipfs -> this + } + + } + + } + + tor = container "Tor"{ + nest -> this + } + + } + + + quietA -> quietB + quietA -> quietC + + quietB -> quietA + quietB -> quietC + + quietC -> quietA + quietC -> quietB + + quietD -> quietA + quietA -> quietD + + } + + + views { + systemLandscape { + include * + autolayout + } + + container quietA { + include * + autolayout + } + + + theme default + } +} \ No newline at end of file diff --git a/docs/example.md b/docs/example.md new file mode 100644 index 0000000000..c2dd809d77 --- /dev/null +++ b/docs/example.md @@ -0,0 +1,19 @@ +# Title: + +## Status: +(accepted | rejected | proposed | deprecated) + +## Context: +(what is our problem) + +## Decision: +(how it will affect app or individual parts of the app) + +## Consequences: +(how it will affect app or individual parts of the app) + +## Code location: +(package->file) + +## Date: +(dd.mm.rrrr) \ No newline at end of file diff --git a/docs/handleCsrReplicationEvent.md b/docs/handleCsrReplicationEvent.md new file mode 100644 index 0000000000..7835ba10c6 --- /dev/null +++ b/docs/handleCsrReplicationEvent.md @@ -0,0 +1,20 @@ +# Title: +Handle possibility of multiple replication events in certificate requests at the same time + +## Status: +accepted + +## Context: +Kinga discovered a bug where when the owner is offline and several (3) users with the same name have joined, sometimes the owner can register two users with the same name + +## Decision: +Introducing a locking mechanism to prevent the next event from being processed if the previous one is still being processed + +## Consequences: +We added queue concept and events will be processed individually in chronological order + +## Code location: +backend -> storage.service.ts + +## Date: +07-11-2023 \ No newline at end of file diff --git a/lerna.json b/lerna.json index 2501ad2db2..aca35a8710 100644 --- a/lerna.json +++ b/lerna.json @@ -5,7 +5,7 @@ "version": "independent", "command": { "version": { - "allowBranch": ["master", "develop", "workflows/require-e2e-before-build-release"], + "allowBranch": ["master", "develop"], "conventionalCommits": true, "createRelease": "github" }, diff --git a/package-lock.json b/package-lock.json index 821fe489f1..96ce43b537 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1083,86 +1083,6 @@ "node": ">=10" } }, - "node_modules/@nrwl/nx-darwin-arm64": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.4.tgz", - "integrity": "sha512-XnvrnT9BJsgThY/4xUcYtE077ERq/img8CkRj7MOOBNOh0/nVcR4LGbBKDHtwE3HPk0ikyS/SxRyNa9msvi3QQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nrwl/nx-darwin-x64": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.4.tgz", - "integrity": "sha512-WKSfSlpVMLchpXkax0geeUNyhvNxwO7qUz/s0/HJWBekt8fizwKDwDj1gP7fOu+YWb/tHiSscbR1km8PtdjhQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nrwl/nx-linux-arm-gnueabihf": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.4.tgz", - "integrity": "sha512-a/b4PP7lP/Cgrh0LjC4O2YTt5pyf4DQTGtuE8qlo8o486UiofCtk4QGJX72q80s23L0ejCaKY2ULKx/3zMLjuA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nrwl/nx-linux-arm64-gnu": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.4.tgz", - "integrity": "sha512-ibBV8fMhSfLVd/2WzcDuUm32BoZsattuKkvMmOoyU6Pzoznc3AqyDjJR4xCIoAn5Rf+Nu1oeQONr5FAtb1Ugow==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nrwl/nx-linux-arm64-musl": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.4.tgz", - "integrity": "sha512-iIjvVYd7+uM4jVD461+PvU5XTALgSvJOODUaMRGOoDl0KlMuTe6pQZlw0eXjl5rcTd6paKaVFWT5j6awr8kj7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nrwl/nx-linux-x64-gnu": { "version": "15.9.4", "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.4.tgz", @@ -1195,38 +1115,6 @@ "node": ">= 10" } }, - "node_modules/@nrwl/nx-win32-arm64-msvc": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.4.tgz", - "integrity": "sha512-2rEsq3eOGVCYpYJn2tTJkOGNJm/U8rP/FmqtZXYa6VJv/00XP3Gl00IXFEDaYV6rZo7SWqLxtEPUbjK5LwPzZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nrwl/nx-win32-x64-msvc": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.4.tgz", - "integrity": "sha512-bogVju4Z/hy1jbppqaTNbmV1R4Kg0R5fKxXAXC2LaL7FL0dup31wPumdV+mXttXBNOBDjV8V/Oz1ZqdmxpOJUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nrwl/tao": { "version": "15.9.4", "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.9.4.tgz", @@ -3245,10 +3133,13 @@ "dev": true }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "4.0.4", @@ -3592,18 +3483,6 @@ "node": ">=6" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3619,6 +3498,18 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hosted-git-info": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", @@ -3967,12 +3858,12 @@ } }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7282,12 +7173,12 @@ } }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -8111,9 +8002,9 @@ } }, "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "node_modules/tuf-js": { @@ -9386,41 +9277,6 @@ } } }, - "@nrwl/nx-darwin-arm64": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.4.tgz", - "integrity": "sha512-XnvrnT9BJsgThY/4xUcYtE077ERq/img8CkRj7MOOBNOh0/nVcR4LGbBKDHtwE3HPk0ikyS/SxRyNa9msvi3QQ==", - "dev": true, - "optional": true - }, - "@nrwl/nx-darwin-x64": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.4.tgz", - "integrity": "sha512-WKSfSlpVMLchpXkax0geeUNyhvNxwO7qUz/s0/HJWBekt8fizwKDwDj1gP7fOu+YWb/tHiSscbR1km8PtdjhQw==", - "dev": true, - "optional": true - }, - "@nrwl/nx-linux-arm-gnueabihf": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.4.tgz", - "integrity": "sha512-a/b4PP7lP/Cgrh0LjC4O2YTt5pyf4DQTGtuE8qlo8o486UiofCtk4QGJX72q80s23L0ejCaKY2ULKx/3zMLjuA==", - "dev": true, - "optional": true - }, - "@nrwl/nx-linux-arm64-gnu": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.4.tgz", - "integrity": "sha512-ibBV8fMhSfLVd/2WzcDuUm32BoZsattuKkvMmOoyU6Pzoznc3AqyDjJR4xCIoAn5Rf+Nu1oeQONr5FAtb1Ugow==", - "dev": true, - "optional": true - }, - "@nrwl/nx-linux-arm64-musl": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.4.tgz", - "integrity": "sha512-iIjvVYd7+uM4jVD461+PvU5XTALgSvJOODUaMRGOoDl0KlMuTe6pQZlw0eXjl5rcTd6paKaVFWT5j6awr8kj7w==", - "dev": true, - "optional": true - }, "@nrwl/nx-linux-x64-gnu": { "version": "15.9.4", "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.4.tgz", @@ -9435,20 +9291,6 @@ "dev": true, "optional": true }, - "@nrwl/nx-win32-arm64-msvc": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.4.tgz", - "integrity": "sha512-2rEsq3eOGVCYpYJn2tTJkOGNJm/U8rP/FmqtZXYa6VJv/00XP3Gl00IXFEDaYV6rZo7SWqLxtEPUbjK5LwPzZA==", - "dev": true, - "optional": true - }, - "@nrwl/nx-win32-x64-msvc": { - "version": "15.9.4", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.4.tgz", - "integrity": "sha512-bogVju4Z/hy1jbppqaTNbmV1R4Kg0R5fKxXAXC2LaL7FL0dup31wPumdV+mXttXBNOBDjV8V/Oz1ZqdmxpOJUw==", - "dev": true, - "optional": true - }, "@nrwl/tao": { "version": "15.9.4", "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.9.4.tgz", @@ -10999,9 +10841,9 @@ "dev": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "gauge": { @@ -11268,15 +11110,6 @@ "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -11289,6 +11122,15 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "hosted-git-info": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", @@ -11559,12 +11401,12 @@ } }, "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "is-docker": { @@ -14091,12 +13933,12 @@ "dev": true }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -14700,9 +14542,9 @@ } }, "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "tuf-js": { diff --git a/packages/backend-bundle/CHANGELOG.md b/packages/backend-bundle/CHANGELOG.md index c1ceee0771..67dcd1d34e 100644 --- a/packages/backend-bundle/CHANGELOG.md +++ b/packages/backend-bundle/CHANGELOG.md @@ -3,6 +3,54 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.0.1-alpha.4](https://github.com/TryQuiet/quiet/compare/backend-bundle@2.0.1-alpha.3...backend-bundle@2.0.1-alpha.4) (2023-10-12) + +**Note:** Version bump only for package backend-bundle + + + + + +## [2.0.1-alpha.3](https://github.com/TryQuiet/quiet/compare/backend-bundle@2.0.1-alpha.2...backend-bundle@2.0.1-alpha.3) (2023-10-10) + +**Note:** Version bump only for package backend-bundle + + + + + +## [2.0.1-alpha.2](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.6.1...backend-bundle@2.0.1-alpha.2) (2023-10-09) + +**Note:** Version bump only for package backend-bundle + + + + + +## [2.0.1-alpha.1](https://github.com/TryQuiet/quiet/compare/backend-bundle@2.0.1-alpha.0...backend-bundle@2.0.1-alpha.1) (2023-09-25) + +**Note:** Version bump only for package backend-bundle + + + + + +## [2.0.1-alpha.0](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.7.0-alpha.0...backend-bundle@2.0.1-alpha.0) (2023-09-25) + +**Note:** Version bump only for package backend-bundle + + + + + +# [2.0.0-alpha.18](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.6.1...backend-bundle@2.0.0-alpha.18) (2023-10-04) + +**Note:** Version bump only for package backend-bundle + + + + + # [1.7.0-alpha.0](/compare/backend-bundle@1.6.0...backend-bundle@1.7.0-alpha.0) (2023-08-29) **Note:** Version bump only for package backend-bundle @@ -11,6 +59,14 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline +## [1.6.1](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.6.0...backend-bundle@1.6.1) (2023-09-25) + +**Note:** Version bump only for package backend-bundle + + + + + # [1.6.0](/compare/backend-bundle@1.6.0-alpha.0...backend-bundle@1.6.0) (2023-08-28) **Note:** Version bump only for package backend-bundle @@ -35,7 +91,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.4.0](https://github.com/ZbayApp/monorepo/compare/backend-bundle@1.4.0-alpha.1...backend-bundle@1.4.0) (2023-07-28) +# [1.4.0](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.4.0-alpha.1...backend-bundle@1.4.0) (2023-07-28) **Note:** Version bump only for package backend-bundle @@ -51,7 +107,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.4.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/backend-bundle@1.3.1...backend-bundle@1.4.0-alpha.0) (2023-06-19) +# [1.4.0-alpha.0](https://github.com/TryQuiet/quiet/compare/backend-bundle@1.3.1...backend-bundle@1.4.0-alpha.0) (2023-06-19) **Note:** Version bump only for package backend-bundle @@ -227,7 +283,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.19.0-alpha.1](https://github.com/ZbayApp/monorepo/compare/backend-bundle@0.19.0-alpha.0...backend-bundle@0.19.0-alpha.1) (2023-01-27) +# [0.19.0-alpha.1](https://github.com/TryQuiet/quiet/compare/backend-bundle@0.19.0-alpha.0...backend-bundle@0.19.0-alpha.1) (2023-01-27) **Note:** Version bump only for package backend-bundle diff --git a/packages/backend-bundle/package-lock.json b/packages/backend-bundle/package-lock.json index 74379c9a02..e9ddad833f 100644 --- a/packages/backend-bundle/package-lock.json +++ b/packages/backend-bundle/package-lock.json @@ -1,12 +1,12 @@ { "name": "backend-bundle", - "version": "1.7.0-alpha.0", + "version": "2.0.1-alpha.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "backend-bundle", - "version": "1.7.0-alpha.0", + "version": "2.0.1-alpha.4", "license": "ISC" } } diff --git a/packages/backend-bundle/package.json b/packages/backend-bundle/package.json index 282b8fb1e2..567fbb7f47 100644 --- a/packages/backend-bundle/package.json +++ b/packages/backend-bundle/package.json @@ -1,10 +1,11 @@ { "name": "backend-bundle", - "version": "1.7.0-alpha.0", + "version": "2.0.1-alpha.4", "description": "", "main": "bundle.cjs", "scripts": {}, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "private": true } diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 220a1d7af3..0665f2f495 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -3,7 +3,77 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -# [2.0.0-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.10.0-alpha.0...@quiet/backend@2.0.0-alpha.0) (2023-09-01) +## [2.0.3-alpha.3](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.3-alpha.2...@quiet/backend@2.0.3-alpha.3) (2023-11-13) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.3-alpha.2](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.3-alpha.1...@quiet/backend@2.0.3-alpha.2) (2023-11-09) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.3-alpha.1](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.3-alpha.0...@quiet/backend@2.0.3-alpha.1) (2023-11-08) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.3-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.2-alpha.0...@quiet/backend@2.0.3-alpha.0) (2023-10-26) + + +### Features + +* version code 366 ([cebf886](https://github.com/TryQuiet/backend/commit/cebf886aee2c4ec6e0ef4e66219d9dc0afecb98c)) + + + + + +## [2.0.2-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.6...@quiet/backend@2.0.2-alpha.0) (2023-10-26) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.1-alpha.6](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.5...@quiet/backend@2.0.1-alpha.6) (2023-10-13) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.1-alpha.5](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.4...@quiet/backend@2.0.1-alpha.5) (2023-10-12) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.1-alpha.4](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.3...@quiet/backend@2.0.1-alpha.4) (2023-10-11) + + +### Reverts + +* Revert "Adjust project bootstrap scripts to be windows-friendly (#1870)" (#1937) ([0811ea3](https://github.com/TryQuiet/backend/commit/0811ea3ea3f682dd763be72b12f626fe416bc036)), closes [#1870](https://github.com/TryQuiet/backend/issues/1870) [#1937](https://github.com/TryQuiet/backend/issues/1937) [#1870](https://github.com/TryQuiet/backend/issues/1870) + + + + + +## [2.0.1-alpha.3](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.2...@quiet/backend@2.0.1-alpha.3) (2023-10-10) **Note:** Version bump only for package @quiet/backend @@ -11,6 +81,101 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline +## [2.0.1-alpha.2](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.4...@quiet/backend@2.0.1-alpha.2) (2023-10-09) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.1-alpha.1](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.1-alpha.0...@quiet/backend@2.0.1-alpha.1) (2023-09-25) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [2.0.1-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.5...@quiet/backend@2.0.1-alpha.0) (2023-09-25) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.20](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.19...@quiet/backend@2.0.0-alpha.20) (2023-10-09) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.19](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.18...@quiet/backend@2.0.0-alpha.19) (2023-10-04) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.18](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.4...@quiet/backend@2.0.0-alpha.18) (2023-10-04) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.5](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.4...@quiet/backend@2.0.0-alpha.5) (2023-09-19) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.4](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.1...@quiet/backend@2.0.0-alpha.4) (2023-09-18) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.3](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.2...@quiet/backend@2.0.0-alpha.3) (2023-09-14) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.2](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.1...@quiet/backend@2.0.0-alpha.2) (2023-09-06) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.1](https://github.com/TryQuiet/backend/compare/@quiet/backend@2.0.0-alpha.0...@quiet/backend@2.0.0-alpha.1) (2023-09-05) + +**Note:** Version bump only for package @quiet/backend + + + + + +# [2.0.0-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.10.0-alpha.0...@quiet/backend@2.0.0-alpha.0) (2023-09-01) + +**Note:** Version bump only for package @quiet/backend + + + + # [1.10.0-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.0...@quiet/backend@1.10.0-alpha.0) (2023-08-29) **Note:** Version bump only for package @quiet/backend @@ -19,6 +184,38 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline +## [1.9.4](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.3...@quiet/backend@1.9.4) (2023-10-03) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [1.9.3](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.2...@quiet/backend@1.9.3) (2023-10-03) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [1.9.2](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.1...@quiet/backend@1.9.2) (2023-09-25) + +**Note:** Version bump only for package @quiet/backend + + + + + +## [1.9.1](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.0...@quiet/backend@1.9.1) (2023-09-15) + +**Note:** Version bump only for package @quiet/backend + + + + + # [1.9.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.0-alpha.0...@quiet/backend@1.9.0) (2023-08-28) **Note:** Version bump only for package @quiet/backend diff --git a/packages/backend/package-lock.json b/packages/backend/package-lock.json index 86d5f15a33..15500283f1 100644 --- a/packages/backend/package-lock.json +++ b/packages/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/backend", - "version": "2.0.0-alpha.0", + "version": "2.0.3-alpha.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/backend", - "version": "2.0.0-alpha.0", + "version": "2.0.3-alpha.3", "license": "MIT", "dependencies": { "@chainsafe/libp2p-gossipsub": "6.1.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index cbe1c5543f..d167a3615d 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,12 +1,13 @@ { "name": "@quiet/backend", - "version": "2.0.0-alpha.0", + "version": "2.0.3-alpha.3", "description": "tlg-manager", "types": "lib/index.d.ts", "type": "module", "exports": "lib/index.js", "author": "", "license": "MIT", + "private": true, "scripts": { "build": "tsc -p tsconfig.build.json", "webpack": "webpack --env mode=development && cp ./lib/bundle.cjs ../backend-bundle/bundle.cjs", @@ -16,7 +17,6 @@ "version": "git add -A src", "lint": "eslint --ext .jsx,.js,.ts,.tsx ./src/ --fix", "lint-ci": "eslint --ext .jsx,.js,.ts,.tsx ./src/", - "test2": "cross-env NODE_OPTIONS=--experimental-vm-modules DEBUG=ipfs:*,backend:* node_modules/jest/bin/jest.js", "test-nest": "cross-env NODE_OPTIONS=--experimental-vm-modules DEBUG=ipfs:*,backend:* node_modules/jest/bin/jest.js --detectOpenHandles --forceExit ./src/nest/**/*.spec.ts", "test": "cross-env NODE_OPTIONS=--experimental-vm-modules DEBUG=ipfs:*,backend:* node_modules/jest/bin/jest.js ./src/**/* --runInBand --verbose --testPathIgnorePatterns=\".src/(!?nodeTest*)|(.node_modules*)\"", "test-ci": "cross-env NODE_OPTIONS=--experimental-vm-modules jest ./src/**/* --runInBand --colors --ci --silent --verbose --testPathIgnorePatterns=\".src/nest/(!?nodeTest*)|(.node_modules*)|src/nest/.*\\.tor.spec\\.(t|j)s|src/nest/ipfs-file-manager/big-files.long.spec.ts$\"", @@ -53,8 +53,8 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", - "@quiet/eslint-config": "^1.4.0-alpha.0", - "@quiet/state-manager": "^2.0.0-alpha.0", + "@quiet/eslint-config": "^2.0.2-alpha.0", + "@quiet/state-manager": "^2.0.2-alpha.1", "@types/crypto-js": "^4.0.2", "@types/express": "^4.17.9", "@types/jest": "28.1.8", @@ -89,10 +89,10 @@ "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@peculiar/webcrypto": "1.4.3", - "@quiet/common": "^2.0.0-alpha.0", - "@quiet/identity": "^2.0.0-alpha.0", - "@quiet/logger": "^1.7.0-alpha.0", - "@quiet/types": "^2.0.0-alpha.0", + "@quiet/common": "^2.0.2-alpha.0", + "@quiet/identity": "^2.0.2-alpha.0", + "@quiet/logger": "^2.0.2-alpha.0", + "@quiet/types": "^2.0.2-alpha.0", "abortable-iterator": "^3.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.13.1", diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 47fc4fcb84..f7e80b5e4e 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -5,6 +5,7 @@ import path from 'path' import getPort from 'get-port' import { AppModule } from './nest/app.module' import { ConnectionsManagerService } from './nest/connections-manager/connections-manager.service' +import { TorControl } from './nest/tor/tor-control.service' import { torBinForPlatform, torDirForPlatform } from './nest/common/utils' import initRnBridge from './rn-bridge' @@ -71,7 +72,7 @@ export const runBackendDesktop = async () => { try { await connectionsManager.closeAllServices() } catch (e) { - log.error('Error occured while closing backend services', e) + log.error('Error occurred while closing backend services', e) } if (process.send) process.send('closed-services') } @@ -79,7 +80,7 @@ export const runBackendDesktop = async () => { try { await connectionsManager.leaveCommunity() } catch (e) { - log.error('Error occured while leaving community', e) + log.error('Error occurred while leaving community', e) } if (process.send) process.send('leftCommunity') } @@ -93,8 +94,7 @@ export const runBackendMobile = async (): Promise => { const rn_bridge = initRnBridge() - let app: INestApplicationContext - app = await NestFactory.createApplicationContext( + const app: INestApplicationContext = await NestFactory.createApplicationContext( AppModule.forOptions({ socketIOPort: options.dataPort, httpTunnelPort: options.httpTunnelPort ? options.httpTunnelPort : null, @@ -113,27 +113,13 @@ export const runBackendMobile = async (): Promise => { rn_bridge.channel.on('close', async () => { const connectionsManager = app.get(ConnectionsManagerService) - await connectionsManager.closeAllServices() - await app.close() + connectionsManager.closeSocket() }) rn_bridge.channel.on('open', async (msg: OpenServices) => { - app = await NestFactory.createApplicationContext( - AppModule.forOptions({ - socketIOPort: msg.socketIOPort, - httpTunnelPort: msg.httpTunnelPort ? msg.httpTunnelPort : null, - torAuthCookie: msg.authCookie ? msg.authCookie : null, - torControlPort: msg.torControlPort ? msg.torControlPort : await getPort(), - torBinaryPath: options.torBinary ? options.torBinary : null, - options: { - env: { - appDataPath: options.dataPath, - }, - createPaths: false, - }, - }), - { logger: ['warn', 'error', 'log', 'debug', 'verbose'] } - ) - console.log('started backend wiktor little bastard ') + const connectionsManager = app.get(ConnectionsManagerService) + const torControlParams = app.get(TorControl) + torControlParams.torControlParams.auth.value = msg.authCookie + await connectionsManager.openSocket() }) } diff --git a/packages/backend/src/nest/common/test.module.ts b/packages/backend/src/nest/common/test.module.ts index ba34c3c1e7..12e79c308c 100644 --- a/packages/backend/src/nest/common/test.module.ts +++ b/packages/backend/src/nest/common/test.module.ts @@ -14,6 +14,7 @@ import { SOCKS_PROXY_AGENT, DB_PATH, LEVEL_DB, + TEST_DATA_PORT, } from '../const' import { ConfigOptions } from '../types' import path from 'path' @@ -27,7 +28,7 @@ const libPath = torDirForPlatform() // torBinaryPath: '../../../../../3rd-party/tor/linux/tor', // torResourcesPath: '../../../../../3rd-party/tor/linux', export const defaultConfigForTest = { - socketIOPort: await getPort(), + socketIOPort: TEST_DATA_PORT, torBinaryPath: torBinForPlatform(), torResourcesPath: torPath, torControlPort: await getPort(), diff --git a/packages/backend/src/nest/common/utils.ts b/packages/backend/src/nest/common/utils.ts index 22e843a62c..23d0e875ed 100644 --- a/packages/backend/src/nest/common/utils.ts +++ b/packages/backend/src/nest/common/utils.ts @@ -2,7 +2,7 @@ import fs from 'fs' import getPort from 'get-port' import path from 'path' import { Server } from 'socket.io' -import { User } from '@quiet/types' +import { UserData } from '@quiet/types' import createHttpsProxyAgent from 'https-proxy-agent' import PeerId from 'peer-id' import tmp from 'tmp' @@ -12,6 +12,7 @@ import { TestConfig } from '../const' import logger from './logger' import { createCertificatesTestHelper } from './client-server' import { Libp2pNodeParams } from '../libp2p/libp2p.types' +import { createLibp2pAddress, createLibp2pListenAddress } from '@quiet/common' const log = logger('test') export interface Ports { @@ -143,16 +144,8 @@ export const torDirForPlatform = (basePath?: string): string => { return torPath } -export const createLibp2pAddress = (address: string, peerId: string) => { - return `/dns4/${address}/tcp/80/ws/p2p/${peerId}` -} - -export const createLibp2pListenAddress = (address: string) => { - return `/dns4/${address}/tcp/80/ws` -} - -export const getUsersAddresses = async (users: User[]): Promise => { - const peers = users.map(async (userData: User) => { +export const getUsersAddresses = async (users: UserData[]): Promise => { + const peers = users.map(async (userData: UserData) => { return createLibp2pAddress(userData.onionAddress, userData.peerId) }) @@ -208,9 +201,6 @@ export const libp2pInstanceParams = async (): Promise => { listenAddresses: [createLibp2pListenAddress('localhost')], agent: createHttpsProxyAgent({ port: 1234, host: 'localhost' }), localAddress: createLibp2pAddress('localhost', peerId.toString()), - cert: pems.userCert, - key: pems.userKey, - ca: [pems.ca], targetPort: port, peers: [remoteAddress], } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts index 2bd278647b..4952b669a6 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts @@ -1,34 +1,23 @@ +import { jest } from '@jest/globals' +import { LazyModuleLoader } from '@nestjs/core' import { Test, TestingModule } from '@nestjs/testing' +import { getFactory, prepareStore, type Store, type communities, type identity } from '@quiet/state-manager' +import { type Community, type Identity, type InitCommunityPayload } from '@quiet/types' +import { type FactoryGirl } from 'factory-girl' +import PeerId from 'peer-id' import { TestModule } from '../common/test.module' +import { libp2pInstanceParams, removeFilesFromDir } from '../common/utils' import { QUIET_DIR, TOR_PASSWORD_PROVIDER } from '../const' -import { ConnectionsManagerModule } from './connections-manager.module' -import { ConnectionsManagerService } from './connections-manager.service' -import PeerId from 'peer-id' -import { libp2pInstanceParams } from '../common/utils' -import { jest } from '@jest/globals' -import { CustomEvent } from '@libp2p/interfaces/events' -import { type communities, getFactory, prepareStore, type identity, type Store } from '@quiet/state-manager' -import { type FactoryGirl } from 'factory-girl' -import { DateTime } from 'luxon' -import waitForExpect from 'wait-for-expect' -import { - type Community, - type Identity, - type InitCommunityPayload, - type LaunchRegistrarPayload, - type NetworkStats, -} from '@quiet/types' -import { LocalDBKeys } from '../local-db/local-db.types' +import { Libp2pModule } from '../libp2p/libp2p.module' +import { Libp2pService } from '../libp2p/libp2p.service' import { LocalDbModule } from '../local-db/local-db.module' import { LocalDbService } from '../local-db/local-db.service' -import { RegistrationService } from '../registration/registration.service' +import { LocalDBKeys } from '../local-db/local-db.types' import { RegistrationModule } from '../registration/registration.module' -import { LazyModuleLoader } from '@nestjs/core' -import { Libp2pService } from '../libp2p/libp2p.service' -import { Libp2pModule } from '../libp2p/libp2p.module' +import { RegistrationService } from '../registration/registration.service' import { SocketModule } from '../socket/socket.module' -import { removeFilesFromDir } from '../common/utils' -import { Libp2pEvents } from '../libp2p/libp2p.types' +import { ConnectionsManagerModule } from './connections-manager.module' +import { ConnectionsManagerService } from './connections-manager.service' describe('ConnectionsManagerService', () => { let module: TestingModule @@ -119,14 +108,12 @@ describe('ConnectionsManagerService', () => { await connectionsManagerService.closeAllServices() const launchCommunitySpy = jest.spyOn(connectionsManagerService, 'launchCommunity').mockResolvedValue() - const launchRegistrarSpy = jest.spyOn(registrationService, 'launchRegistrar').mockResolvedValue() await connectionsManagerService.init() - expect(launchRegistrarSpy).not.toHaveBeenCalled() expect(launchCommunitySpy).toHaveBeenCalledWith(launchCommunityPayload) }) - it('launches community and registrar on init if their data exists in local db', async () => { + it('launches community on init if their data exists in local db', async () => { const launchCommunityPayload: InitCommunityPayload = { id: community.id, peerId: userIdentity.peerId, @@ -141,20 +128,9 @@ describe('ConnectionsManagerService', () => { peers: community.peerList, } - const launchRegistrarPayload: LaunchRegistrarPayload = { - id: community.id, - peerId: userIdentity.peerId.id, - // @ts-expect-error - rootCertString: community.CA?.rootCertString, - // @ts-expect-error - rootKeyString: community.CA?.rootKeyString, - privateKey: 'privateKey', - } - await localDbService.put(LocalDBKeys.COMMUNITY, launchCommunityPayload) - await localDbService.put(LocalDBKeys.REGISTRAR, launchRegistrarPayload) - const peerAddress = '/dns4/test.onion/tcp/443/wss/p2p/peerid' + const peerAddress = '/dns4/test.onion/tcp/80/ws/p2p/peerid' await localDbService.put(LocalDBKeys.PEERS, { [peerAddress]: { peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', @@ -166,21 +142,17 @@ describe('ConnectionsManagerService', () => { await connectionsManagerService.closeAllServices() const launchCommunitySpy = jest.spyOn(connectionsManagerService, 'launchCommunity').mockResolvedValue() - const launchRegistrarSpy = jest.spyOn(registrationService, 'launchRegistrar').mockResolvedValue() await connectionsManagerService.init() expect(launchCommunitySpy).toHaveBeenCalledWith(Object.assign(launchCommunityPayload, { peers: [peerAddress] })) - expect(launchRegistrarSpy).toHaveBeenCalledWith(launchRegistrarPayload) }) it('does not launch community on init if its data does not exist in local db', async () => { await connectionsManagerService.closeAllServices() await connectionsManagerService.init() const launchCommunitySpy = jest.spyOn(connectionsManagerService, 'launchCommunity') - const launchRegistrarSpy = jest.spyOn(registrationService, 'launchRegistrar') expect(launchCommunitySpy).not.toHaveBeenCalled() - expect(launchRegistrarSpy).not.toHaveBeenCalled() }) // At this moment, that test have to be skipped, because checking statues is called before launchCommunity method @@ -210,7 +182,7 @@ describe('ConnectionsManagerService', () => { expect(launchSpy).toBeCalledTimes(1) }) - it('Bug reproduction - Error on startup - Error: TOR: Connection already established - Trigger launchCommunity and launchRegistrar from backend and state manager', async () => { + it('Bug reproduction - Error on startup - Error: TOR: Connection already established - Trigger launchCommunity from backend and state manager', async () => { const launchCommunityPayload: InitCommunityPayload = { id: community.id, peerId: userIdentity.peerId, @@ -225,21 +197,10 @@ describe('ConnectionsManagerService', () => { peers: community.peerList, } - const launchRegistrarPayload: LaunchRegistrarPayload = { - id: community.id, - peerId: userIdentity.peerId.id, - // @ts-expect-error - rootCertString: community.CA?.rootCertString, - // @ts-expect-error - rootKeyString: community.CA?.rootKeyString, - privateKey: '', - } - // await connectionsManager.init() await localDbService.put(LocalDBKeys.COMMUNITY, launchCommunityPayload) - await localDbService.put(LocalDBKeys.REGISTRAR, launchRegistrarPayload) - const peerAddress = '/dns4/test.onion/tcp/443/wss/p2p/peerid' + const peerAddress = '/dns4/test.onion/tcp/80/ws/p2p/peerid' await localDbService.put(LocalDBKeys.PEERS, { [peerAddress]: { peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', @@ -251,11 +212,9 @@ describe('ConnectionsManagerService', () => { await connectionsManagerService.closeAllServices() const launchCommunitySpy = jest.spyOn(connectionsManagerService, 'launchCommunity').mockResolvedValue() - const launchRegistrarSpy = jest.spyOn(registrationService, 'launchRegistrar').mockResolvedValue() await connectionsManagerService.init() expect(launchCommunitySpy).toBeCalledTimes(1) - expect(launchRegistrarSpy).toBeCalledTimes(1) }) }) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index e88e7151b2..8c759f8571 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -4,14 +4,7 @@ import crypto from 'crypto' import { CustomEvent } from '@libp2p/interfaces/events' import { jest, beforeEach, describe, it, expect, afterEach } from '@jest/globals' import { communities, getFactory, identity, prepareStore, Store } from '@quiet/state-manager' -import { - createLibp2pAddress, - createPeerId, - createTmpDir, - libp2pInstanceParams, - removeFilesFromDir, - tmpQuietDirPath, -} from '../common/utils' +import { createPeerId, createTmpDir, libp2pInstanceParams, removeFilesFromDir, tmpQuietDirPath } from '../common/utils' import { NetworkStats, type Community, type Identity, type InitCommunityPayload } from '@quiet/types' import { LazyModuleLoader } from '@nestjs/core' @@ -37,6 +30,7 @@ import { DateTime } from 'luxon' import waitForExpect from 'wait-for-expect' import { Libp2pEvents } from '../libp2p/libp2p.types' import { sleep } from '../common/sleep' +import { createLibp2pAddress } from '@quiet/common' jest.setTimeout(100_000) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index c62fcc8911..dcef3bdc41 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -30,11 +30,8 @@ import { NetworkStats, PushNotificationPayload, RegisterOwnerCertificatePayload, - RegisterUserCertificatePayload, RemoveDownloadStatus, ResponseCreateNetworkPayload, - SaveCertificatePayload, - SaveOwnerCertificatePayload, SendCertificatesResponse, SendMessagePayload, SetChannelSubscribedPayload, @@ -42,6 +39,9 @@ import { StorePeerListPayload, UploadFilePayload, PeerId as PeerIdType, + SaveCSRPayload, + CommunityMetadata, + CommunityMetadataPayload, } from '@quiet/types' import { CONFIG_OPTIONS, QUIET_DIR, SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const' import { ConfigOptions, GetPorts, ServerIoProviderTypes } from '../types' @@ -165,30 +165,20 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI if ([ServiceState.LAUNCHING, ServiceState.LAUNCHED].includes(this.communityState)) return this.communityState = ServiceState.LAUNCHING } - const registrarData: LaunchRegistrarPayload = await this.localDbService.get(LocalDBKeys.REGISTRAR) - if (registrarData) { - if ([ServiceState.LAUNCHING, ServiceState.LAUNCHED].includes(this.registrarState)) return - this.registrarState = ServiceState.LAUNCHING - } if (community) { await this.launchCommunity(community) } - if (registrarData) { - await this.registrationService.launchRegistrar(registrarData) - } } public async closeAllServices(options: { saveTor: boolean } = { saveTor: false }) { if (this.tor && !options.saveTor) { await this.tor.kill() } - if (this.registrationService) { - this.logger('Stopping registration service') - await this.registrationService.stop() - } if (this.storageService) { this.logger('Stopping orbitdb') await this.storageService?.stopOrbitDb() + this.logger('reset CsrReplicated map and id') + this.storageService.resetCsrReplicatedMapAndId() } // if (this.storageService.ipfs) { // this.storageService.ipfs = null @@ -207,6 +197,15 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } } + public closeSocket() { + this.serverIoProvider.io.close() + } + + // This method is only used on iOS through rn-bridge for reacting on lifecycle changes + public async openSocket() { + await this.socketService.init() + } + public async leaveCommunity() { this.tor.resetHiddenServices() this.serverIoProvider.io.close() @@ -273,7 +272,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI community: { ...community, privateKey: network2.hiddenService.privateKey, - registrarUrl: community.registrarUrl || network2.hiddenService.onionAddress.split('.')[0], + registrarUrl: community.registrarUrl || network2.hiddenService.onionAddress.split('.')[0], // TODO: remove }, network, } @@ -281,12 +280,14 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } public async createCommunity(payload: InitCommunityPayload) { + console.log('ConnectionsManager.createCommunity peers:', payload.peers) await this.launchCommunity(payload) this.logger(`Created and launched community ${payload.id}`) this.serverIoProvider.io.emit(SocketActionTypes.NEW_COMMUNITY, { id: payload.id }) } public async launchCommunity(payload: InitCommunityPayload) { + console.log('ConnectionsManager.launchCommunity peers:', payload.peers) this.communityState = ServiceState.LAUNCHING const communityData: InitCommunityPayload = await this.localDbService.get(LocalDBKeys.COMMUNITY) if (!communityData) { @@ -306,10 +307,17 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } this.logger(`Launched community ${payload.id}`) + this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.LAUNCHED_COMMUNITY) + this.communityId = payload.id this.communityState = ServiceState.LAUNCHED + console.log('Hunting for heisenbug: Backend initialized community and sent event to state manager') + + // Unblock websocket endpoints + this.socketService.resolveReadyness() + this.serverIoProvider.io.emit(SocketActionTypes.COMMUNITY, { id: payload.id }) } public async launch(payload: InitCommunityPayload) { @@ -327,7 +335,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI const { Libp2pModule } = await import('../libp2p/libp2p.module') const moduleRef = await this.lazyModuleLoader.load(() => Libp2pModule) - this.logger('launchCommunityFromStorage') const { Libp2pService } = await import('../libp2p/libp2p.service') const lazyService = moduleRef.get(Libp2pService) this.libp2pService = lazyService @@ -336,6 +343,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI const _peerId = await peerIdFromKeys(restoredRsa.marshalPubKey(), restoredRsa.marshalPrivKey()) let peers = payload.peers + console.log(`Launching community ${payload.id}, payload peers: ${peers}`) if (!peers || peers.length === 0) { peers = [this.libp2pService.createLibp2pAddress(onionAddress, _peerId.toString())] } @@ -344,14 +352,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI peerId: _peerId, listenAddresses: [this.libp2pService.createLibp2pListenAddress(onionAddress)], agent: this.socksProxyAgent, - cert: payload.certs.certificate, - key: payload.certs.key, - ca: payload.certs.CA, localAddress: this.libp2pService.createLibp2pAddress(onionAddress, _peerId.toString()), targetPort: this.ports.libp2pHiddenService, peers, } - this.logger('libp2p params', params) await this.libp2pService.createInstance(params) // KACPER @@ -385,34 +389,23 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.socketService.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) - - this.registrationService.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { - this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) - }) } private attachRegistrationListeners() { - this.registrationService.on(RegistrationEvents.REGISTRAR_STATE, (payload: ServiceState) => { - this.registrarState = payload - }) this.registrationService.on(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload => { this.serverIoProvider.io.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload) }) - this.registrationService.on(RegistrationEvents.SPAWN_HS_FOR_REGISTRAR, async payload => { - await this.tor.spawnHiddenService({ - targetPort: payload.port, - privKey: payload.privateKey, - virtPort: payload.targetPort, - }) - }) this.registrationService.on(RegistrationEvents.ERROR, payload => { emitError(this.serverIoProvider.io, payload) }) - this.registrationService.on(SocketActionTypes.SEND_USER_CERTIFICATE, payload => { - this.serverIoProvider.io.emit(SocketActionTypes.SEND_USER_CERTIFICATE, payload) - }) this.registrationService.on(RegistrationEvents.NEW_USER, async payload => { await this.storageService?.saveCertificate(payload) }) + + this.registrationService.on(RegistrationEvents.FINISHED_ISSUING_CERTIFICATES_FOR_ID, payload => { + if (payload.id) { + this.storageService.resolveCsrReplicatedPromise(payload.id) + } + }) } private attachsocketServiceListeners() { // Community @@ -446,38 +439,21 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.communityState = ServiceState.LAUNCHING await this.launchCommunity(args) }) - // Registration this.socketService.on(SocketActionTypes.LAUNCH_REGISTRAR, async (args: LaunchRegistrarPayload) => { + // Event left for setting permsData purposes this.logger(`socketService - ${SocketActionTypes.LAUNCH_REGISTRAR}`) - - const communityData = await this.localDbService.get(LocalDBKeys.REGISTRAR) - if (!communityData) { - await this.localDbService.put(LocalDBKeys.REGISTRAR, args) + this.registrationService.permsData = { + certificate: args.rootCertString, + privKey: args.rootKeyString, } - console.log('this.registrarState', this.registrarState) - if ([ServiceState.LAUNCHING, ServiceState.LAUNCHED].includes(this.registrarState)) return - this.registrarState = ServiceState.LAUNCHING - await this.registrationService.launchRegistrar(args) - }) - this.socketService.on(SocketActionTypes.SAVED_OWNER_CERTIFICATE, async (args: SaveOwnerCertificatePayload) => { - const saveCertificatePayload: SaveCertificatePayload = { - certificate: args.certificate, - rootPermsData: args.permsData, - } - await this.storageService?.saveCertificate(saveCertificatePayload) - }) - this.socketService.on(SocketActionTypes.REGISTER_USER_CERTIFICATE, async (args: RegisterUserCertificatePayload) => { - // if (!this.socksProxyAgent) { - // this.createAgent() - // } - - await this.registrationService.sendCertificateRegistrationRequest( - args.serviceAddress, - args.userCsr, - args.communityId, - 120_000, - this.socksProxyAgent - ) + }) + this.socketService.on(SocketActionTypes.SEND_COMMUNITY_METADATA, async (payload: CommunityMetadata) => { + await this.storageService?.updateCommunityMetadata(payload) + }) + this.socketService.on(SocketActionTypes.SAVE_USER_CSR, async (payload: SaveCSRPayload) => { + console.log(`On ${SocketActionTypes.SAVE_USER_CSR}`) + await this.storageService?.saveCSR(payload) + this.serverIoProvider.io.emit(SocketActionTypes.SAVED_USER_CSR, payload) }) this.socketService.on( SocketActionTypes.REGISTER_OWNER_CERTIFICATE, @@ -537,7 +513,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI }) this.storageService.on(StorageEvents.LOAD_CERTIFICATES, (payload: SendCertificatesResponse) => { this.serverIoProvider.io.emit(SocketActionTypes.RESPONSE_GET_CERTIFICATES, payload) - this.registrationService.emit(RegistrationEvents.SET_CERTIFICATES, payload.certificates) }) this.storageService.on(StorageEvents.LOAD_PUBLIC_CHANNELS, (payload: ChannelsReplicatedPayload) => { this.serverIoProvider.io.emit(SocketActionTypes.CHANNELS_REPLICATED, payload) @@ -592,5 +567,21 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI console.log('emitting deleted channel event back to state manager') this.serverIoProvider.io.emit(SocketActionTypes.CHANNEL_DELETION_RESPONSE, payload) }) + this.storageService.on( + StorageEvents.REPLICATED_CSR, + async (payload: { csrs: string[]; certificates: string[]; id: string }) => { + console.log(`On ${StorageEvents.REPLICATED_CSR}`) + this.serverIoProvider.io.emit(SocketActionTypes.RESPONSE_GET_CSRS, { csrs: payload.csrs }) + this.registrationService.emit(RegistrationEvents.REGISTER_USER_CERTIFICATE, payload) + } + ) + this.storageService.on(StorageEvents.REPLICATED_COMMUNITY_METADATA, (payload: CommunityMetadata) => { + console.log(`On ${StorageEvents.REPLICATED_COMMUNITY_METADATA}: ${payload}`) + const communityMetadataPayload: CommunityMetadataPayload = { + rootCa: payload.rootCa, + ownerCertificate: payload.ownerCertificate, + } + this.serverIoProvider.io.emit(SocketActionTypes.SAVE_COMMUNITY_METADATA, communityMetadataPayload) + }) } } diff --git a/packages/backend/src/nest/const.ts b/packages/backend/src/nest/const.ts index ddb27b590c..f4c86c3a35 100644 --- a/packages/backend/src/nest/const.ts +++ b/packages/backend/src/nest/const.ts @@ -15,6 +15,8 @@ export enum TestConfig { IPFS_REPO_PATH = 'Ipfs-test-nest-backend', } +export const TEST_DATA_PORT = '9004' + export const QUIET_DIR_PATH = path.join(os.homedir(), Config.QUIET_DIR) export const TEST_QUIET_DIR_PATH = path.join(os.homedir(), TestConfig.QUIET_DIR) diff --git a/packages/backend/src/nest/ipfs-file-manager/ipfs-file-manager.service.ts b/packages/backend/src/nest/ipfs-file-manager/ipfs-file-manager.service.ts index 31c5a9a538..b8cb12a034 100644 --- a/packages/backend/src/nest/ipfs-file-manager/ipfs-file-manager.service.ts +++ b/packages/backend/src/nest/ipfs-file-manager/ipfs-file-manager.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common' import { EventEmitter, setMaxListeners } from 'events' import fs from 'fs' import path from 'path' +import crypto from 'crypto' import PQueue, { AbortError } from 'p-queue' import { decode, PBNode } from '@ipld/dag-pb' import * as base58 from 'multiformats/bases/base58' @@ -433,13 +434,20 @@ export class IpfsFileManagerService extends EventEmitter { private async assemblyFile(fileMetadata: FileMetadata) { const _CID = CID.parse(fileMetadata.cid) - const downloadDirectory = path.join(this.quietDir, 'downloads', fileMetadata.cid) + const downloadDirectory = path.join(this.quietDir, 'downloads') createPaths([downloadDirectory]) - const fileName = fileMetadata.name + fileMetadata.ext - const filePath = `${path.join(downloadDirectory, fileName)}` - - const writeStream = fs.createWriteStream(filePath) + // As a quick fix, using a UUID for filename ensures that we never + // save a file with a malicious filename. Perhaps it's also + // possible to use the CID, however let's verify that first. + let fileName: string + let filePath: string + do { + fileName = crypto.randomUUID() + filePath = `${path.join(downloadDirectory, fileName)}` + } while (fs.existsSync(filePath)) + + const writeStream = fs.createWriteStream(filePath, { flags: 'wx' }) const entries = this.ipfs.cat(_CID) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts index 9c7724eee4..6775f9a2e2 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts @@ -38,11 +38,11 @@ describe('Libp2pService', () => { it('creates libp2p address with proper ws type (%s)', async () => { const libp2pAddress = libp2pService.createLibp2pAddress(params.localAddress, params.peerId.toString()) - expect(libp2pAddress).toStrictEqual(`/dns4/${params.localAddress}.onion/tcp/80/wss/p2p/${params.peerId.toString()}`) + expect(libp2pAddress).toStrictEqual(`/dns4/${params.localAddress}.onion/tcp/80/ws/p2p/${params.peerId.toString()}`) }) it('creates libp2p listen address', async () => { const libp2pListenAddress = libp2pService.createLibp2pListenAddress('onionAddress') - expect(libp2pListenAddress).toStrictEqual(`/dns4/onionAddress.onion/tcp/80/wss`) + expect(libp2pListenAddress).toStrictEqual(`/dns4/onionAddress.onion/tcp/80/ws`) }) }) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index fa98820843..311b8f2b47 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -15,10 +15,10 @@ import { multiaddr } from '@multiformats/multiaddr' import { ConnectionProcessInfo, PeerId, SocketActionTypes } from '@quiet/types' import { SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const' import { ServerIoProviderTypes } from '../types' -import { createLibp2pListenAddress, createLibp2pAddress } from './libp2p.utils' import Logger from '../common/logger' import { webSockets } from '../websocketOverTor' import { all } from '../websocketOverTor/filters' +import { createLibp2pAddress, createLibp2pListenAddress } from '@quiet/common' @Injectable() export class Libp2pService extends EventEmitter { @@ -44,7 +44,7 @@ export class Libp2pService extends EventEmitter { return createLibp2pListenAddress(address) } - public async createInstance(params: Libp2pNodeParams): Promise { + public async createInstance(params: Libp2pNodeParams): Promise { if (this.libp2pInstance) { return this.libp2pInstance } @@ -118,6 +118,7 @@ export class Libp2pService extends EventEmitter { dialInChunks.stop() this.connectedPeers.set(remotePeerId, DateTime.utc().valueOf()) + this.logger(`${this.connectedPeers.size} connected peers`) this.emit(Libp2pEvents.PEER_CONNECTED, { peers: [remotePeerId], @@ -144,6 +145,7 @@ export class Libp2pService extends EventEmitter { const connectionDuration: number = connectionEndTime - connectionStartTime this.connectedPeers.delete(remotePeerId) + this.logger(`${this.connectedPeers.size} connected peers`) this.emit(Libp2pEvents.PEER_DISCONNECTED, { peer: remotePeerId, diff --git a/packages/backend/src/nest/libp2p/libp2p.types.ts b/packages/backend/src/nest/libp2p/libp2p.types.ts index a912015c23..d32cc0230f 100644 --- a/packages/backend/src/nest/libp2p/libp2p.types.ts +++ b/packages/backend/src/nest/libp2p/libp2p.types.ts @@ -11,9 +11,6 @@ export interface Libp2pNodeParams { peerId: any listenAddresses: string[] agent: Agent - cert: string - key: string - ca: string[] localAddress: string targetPort: number peers: string[] diff --git a/packages/backend/src/nest/local-db/local-db.service.spec.ts b/packages/backend/src/nest/local-db/local-db.service.spec.ts index 0ac06cba05..0cf4af79cf 100644 --- a/packages/backend/src/nest/local-db/local-db.service.spec.ts +++ b/packages/backend/src/nest/local-db/local-db.service.spec.ts @@ -73,7 +73,7 @@ describe('LocalDbService', () => { it('get sorted peers', async () => { const extraPeers = [ - '/dns4/zl37gnntp64dhnisddftypxbt5cqx6cum65vdv6oeaffrbqmemwc52ad.onion/tcp/443/wss/p2p/QmPGdGDUV1PXaJky4V53KSvFszdqEcM7KCoDpF2uFPf5w6', + '/dns4/zl37gnntp64dhnisddftypxbt5cqx6cum65vdv6oeaffrbqmemwc52ad.onion/tcp/443/ws/p2p/QmPGdGDUV1PXaJky4V53KSvFszdqEcM7KCoDpF2uFPf5w6', ] await localDbService.put(LocalDBKeys.PEERS, { ...peer1Stats, diff --git a/packages/backend/src/nest/registration/registration.functions.ts b/packages/backend/src/nest/registration/registration.functions.ts index 178682dd23..1363eae65e 100644 --- a/packages/backend/src/nest/registration/registration.functions.ts +++ b/packages/backend/src/nest/registration/registration.functions.ts @@ -1,31 +1,12 @@ -import { - createUserCert, - loadCSR, - CertFieldsTypes, - getReqFieldValue, - keyFromCertificate, - parseCertificate, - getCertFieldValue, -} from '@quiet/identity' +import { createUserCert, keyFromCertificate } from '@quiet/identity' import { IsBase64, IsNotEmpty, validate } from 'class-validator' -import { CertificationRequest } from 'pkijs' -import { Agent } from 'http' -import AbortController from 'abort-controller' -import fetch, { Response } from 'node-fetch' -import { getUsersAddresses } from '../common/utils' -import { - ErrorCodes, - ErrorMessages, - ErrorPayload, - PermsData, - SocketActionTypes, - SuccessfullRegistrarionResponse, - User, - UserCertificatePayload, -} from '@quiet/types' +import { ErrorPayload, PermsData, SocketActionTypes, SuccessfullRegistrarionResponse } from '@quiet/types' import { CsrContainsFields, IsCsr } from './registration.validators' import { RegistrationEvents } from './registration.types' +import { loadCSR, CertFieldsTypes, getCertFieldValue, getReqFieldValue, parseCertificate } from '@quiet/identity' +import { CertificationRequest } from 'pkijs' import Logger from '../common/logger' +import { load } from 'mock-fs' const logger = Logger('registration.functions') class UserCsrData { @@ -37,51 +18,8 @@ class UserCsrData { } export interface RegistrarResponse { - status: number - body: any -} - -// REFACTORING: Move this method to identity package -export const pubKeyMatch = (cert: string, parsedCsr: CertificationRequest): boolean => { - const parsedCertificate = parseCertificate(cert) - const pubKey = keyFromCertificate(parsedCertificate) - const pubKeyCsr = keyFromCertificate(parsedCsr) - - if (pubKey === pubKeyCsr) { - return true - } - return false -} - -export const registerOwner = async (userCsr: string, permsData: PermsData): Promise => { - const userData = new UserCsrData() - userData.csr = userCsr - const validationErrors = await validate(userData) - if (validationErrors.length > 0) { - throw new Error(`Validation errors: ${validationErrors}`) - } - const userCert = await createUserCert( - permsData.certificate, - permsData.privKey, - userCsr, - new Date(), - new Date(2030, 1, 1) - ) - return userCert.userCertString -} - -const certificateByUsername = (username: string, certificates: string[]): string | null => { - /** - * Check if given username is already in use - */ - for (const cert of certificates) { - const parsedCert = parseCertificate(cert) - const certUsername = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) - if (certUsername?.localeCompare(username, undefined, { sensitivity: 'base' }) === 0) { - return cert - } - } - return null + cert: string | null + error: any } export interface RegistrationResponse { @@ -89,194 +27,59 @@ export interface RegistrationResponse { data: ErrorPayload | SuccessfullRegistrarionResponse } -export const sendCertificateRegistrationRequest = async ( - serviceAddress: string, - userCsr: string, - communityId: string, - requestTimeout = 120000, - socksProxyAgent: Agent -): Promise => { - const controller = new AbortController() - const timeout = setTimeout(() => { - controller.abort() - }, requestTimeout) - - let options = { - method: 'POST', - body: JSON.stringify({ data: userCsr }), - headers: { 'Content-Type': 'application/json' }, - signal: controller.signal, - } - - options = Object.assign( - { - agent: socksProxyAgent, - }, - options - ) - - let response: Response | null = null +export const extractPendingCsrs = async (payload: { csrs: string[]; certificates: string[] }) => { + const certNames = new Set() + const pendingNames = new Set() + const parsedUniqueCsrs = new Map() + const pendingCsrs: string[] = [] - try { - const start = new Date() - response = await fetch(`${serviceAddress}/register`, options) - const end = new Date() - const fetchTime = (end.getTime() - start.getTime()) / 1000 - logger(`Fetched ${serviceAddress}, time: ${fetchTime}`) - } catch (e) { - logger.error(e) - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.NOT_FOUND, - message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId, - }, + payload.certificates.forEach(cert => { + const parsedCert = parseCertificate(cert) + const username = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) + if (username) { + certNames.add(username) } - } finally { - clearTimeout(timeout) - } + }) - switch (response?.status) { - case 200: - break - case 400: - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.BAD_REQUEST, - message: ErrorMessages.INVALID_USERNAME, - community: communityId, - }, - } - case 403: - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.FORBIDDEN, - message: ErrorMessages.USERNAME_TAKEN, - community: communityId, - }, - } - case 404: - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.NOT_FOUND, - message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId, - }, - } - default: - logger.error(`Registrar responded with ${response?.status} "${response?.statusText}" (${communityId})`) - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.SERVER_ERROR, - message: ErrorMessages.REGISTRATION_FAILED, - community: communityId, - }, - } + for (const csr of payload.csrs.reverse()) { + const parsedCsr = await loadCSR(csr) + const pubKey = keyFromCertificate(parsedCsr) + if (!parsedUniqueCsrs.has(pubKey)) { + parsedUniqueCsrs.set(pubKey, csr) + } } - const registrarResponse: UserCertificatePayload = await response.json() + const uniqueCsrsArray = Array.from(parsedUniqueCsrs.values()).reverse() - logger(`Sending user certificate (${communityId})`) - return { - eventType: SocketActionTypes.SEND_USER_CERTIFICATE, - data: { - communityId: communityId, - payload: registrarResponse, - }, + for (const csr of uniqueCsrsArray) { + const parsedCsr = await loadCSR(csr) + const username = getReqFieldValue(parsedCsr, CertFieldsTypes.nickName) + if (username && !certNames.has(username) && !pendingNames.has(username)) { + pendingNames.add(username) + pendingCsrs.push(csr) + } } + return pendingCsrs } -export const registerUser = async ( - csr: string, - permsData: PermsData, - certificates: string[], - ownerCertificate: string -): Promise => { - let cert: string +export const validateCsr = async (csr: string) => { const userData = new UserCsrData() userData.csr = csr const validationErrors = await validate(userData) - if (validationErrors.length > 0) { - logger.error(`Received data is not valid: ${validationErrors.toString()}`) - return { - status: 400, - body: JSON.stringify(validationErrors), - } - } + return validationErrors +} - const parsedCsr = await loadCSR(userData.csr) - const username = getReqFieldValue(parsedCsr, CertFieldsTypes.nickName) - if (!username) { - logger.error(`Could not parse certificate for field type ${CertFieldsTypes.nickName}`) +/** + * This function should only be called with pending CSRs (by calling extractPendingCsrs first which prevents signing CSRs for duplicate usernames). + */ +export const issueCertificate = async (userCsr: string, permsData: PermsData): Promise => { + const validationErrors = await validateCsr(userCsr) + if (validationErrors.length > 0) { return { - // Should be internal server error code 500 - status: 400, - body: null, - } - } - // Use map here - const usernameCert = certificateByUsername(username, certificates) - if (usernameCert) { - if (!pubKeyMatch(usernameCert, parsedCsr)) { - logger(`Username ${username} is taken`) - return { - // Should be conflict code 409 - status: 403, - body: null, - } - } else { - logger('Requesting same CSR again') - cert = usernameCert - } - } else { - logger('username doesnt have existing cert, creating new') - try { - cert = await registerCertificate(userData.csr, permsData) - } catch (e) { - logger.error(`Something went wrong with registering user: ${e.message as string}`) - return { - // Should be internal server error code 500 - status: 400, - body: null, - } + cert: null, + error: [validationErrors], } } - - const allUsers: User[] = [] - for (const cert of certificates) { - const parsedCert = parseCertificate(cert) - const onionAddress = getCertFieldValue(parsedCert, CertFieldsTypes.commonName) - const peerId = getCertFieldValue(parsedCert, CertFieldsTypes.peerId) - const username = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) - const dmPublicKey = getCertFieldValue(parsedCert, CertFieldsTypes.dmPublicKey) - if (!onionAddress || !peerId || !username || !dmPublicKey) continue - allUsers.push({ onionAddress, peerId, username, dmPublicKey }) - } - - const peerList = await getUsersAddresses(allUsers) - - return { - status: 200, - body: { - certificate: cert, - peers: peerList, - rootCa: permsData.certificate, - ownerCert: ownerCertificate, - }, - } -} - -export const registerCertificate = async (userCsr: string, permsData: PermsData): Promise => { const userCert = await createUserCert( permsData.certificate, permsData.privKey, @@ -284,5 +87,8 @@ export const registerCertificate = async (userCsr: string, permsData: PermsData) new Date(), new Date(2030, 1, 1) ) - return userCert.userCertString + return { + cert: userCert.userCertString, + error: null, + } } diff --git a/packages/backend/src/nest/registration/registration.service.spec.ts b/packages/backend/src/nest/registration/registration.service.spec.ts index 38719e3ec0..98abe55d4b 100644 --- a/packages/backend/src/nest/registration/registration.service.spec.ts +++ b/packages/backend/src/nest/registration/registration.service.spec.ts @@ -2,28 +2,14 @@ import { Test, TestingModule } from '@nestjs/testing' import { TestModule } from '../common/test.module' import { RegistrationModule } from './registration.module' import { RegistrationService } from './registration.service' -import { - configCrypto, - createRootCA, - createUserCert, - createUserCsr, - type RootCA, - verifyUserCert, - type UserCsr, -} from '@quiet/identity' +import { configCrypto, createRootCA, createUserCsr, type RootCA, verifyUserCert, type UserCsr } from '@quiet/identity' import { type DirResult } from 'tmp' -import { ErrorCodes, ErrorMessages, type PermsData, SocketActionTypes } from '@quiet/types' +import { type PermsData } from '@quiet/types' import { Time } from 'pkijs' -import { registerOwner, registerUser, sendCertificateRegistrationRequest } from './registration.functions' -import createHttpsProxyAgent from 'https-proxy-agent' -import { RegistrationEvents } from './registration.types' +import { issueCertificate, extractPendingCsrs } from './registration.functions' import { jest } from '@jest/globals' import { createTmpDir } from '../common/utils' - -// @ts-ignore -const { Response } = jest.requireActual('node-fetch') - -jest.mock('node-fetch', () => jest.fn()) +import { RegistrationEvents } from './registration.types' describe('RegistrationService', () => { let module: TestingModule @@ -34,7 +20,6 @@ describe('RegistrationService', () => { let permsData: PermsData let userCsr: UserCsr let invalidUserCsr: any - let fetch: any beforeEach(async () => { module = await Test.createTestingModule({ @@ -60,7 +45,6 @@ describe('RegistrationService', () => { hashAlg: configCrypto.hashAlg, }) invalidUserCsr = 'invalidUserCsr' - fetch = await import('node-fetch') }) afterEach(async () => { @@ -68,104 +52,132 @@ describe('RegistrationService', () => { await module.close() }) - it('registerOwner should return certificate if csr is valid', async () => { - const result = await registerOwner(userCsr.userCsr, permsData) - expect(result).toBeTruthy() + it('registerUser should return cert if csr is valid and cert should pass the verification', async () => { + const responseData = await issueCertificate(userCsr.userCsr, permsData) + expect(responseData.cert).toBeTruthy() + if (!responseData.cert) return null + const isProperUserCert = await verifyUserCert(certRoot.rootCertString, responseData.cert) + expect(isProperUserCert.result).toBe(true) }) - it('registerOwner should throw error if csr is invalid', async () => { - await expect(registerOwner(invalidUserCsr, permsData)).rejects.toThrow() + it('registrar should return errors array if csr is not valid and should not return any cert', async () => { + const responseData = await issueCertificate(invalidUserCsr, permsData) + expect(responseData.cert).toBeFalsy() + expect(responseData.error.length).toBeTruthy() }) - it('registerUser should return 200 status code', async () => { - const responseData = await registerUser(userCsr.userCsr, permsData, [], 'ownerCert') - const isProperUserCert = await verifyUserCert(certRoot.rootCertString, responseData.body.certificate) - expect(isProperUserCert.result).toBe(true) + it('extractPendingCsrs should return all csrs if there are no certificates and csrs do not contain duplicate usernames', async () => { + const certificates: string[] = [] + const csrs: string[] = [userCsr.userCsr] + const payload: { certificates: string[]; csrs: string[] } = { + certificates: certificates, + csrs: csrs, + } + const pendingCsrs = await extractPendingCsrs(payload) + expect(pendingCsrs).toEqual(csrs) }) - it('returns existing certificate if username is taken but csr and cert public keys match', async () => { - const user = await createUserCsr({ - nickname: 'userName', + it('extractPendingCsrs should return all csrs if there are certificates, and csrs do not contain any name that is in certificates already', async () => { + const aliceCsr = await createUserCsr({ + nickname: 'alice', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey1', + dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) - const userCert = await createUserCert( - certRoot.rootCertString, - certRoot.rootKeyString, - user.userCsr, - new Date(), - new Date(2030, 1, 1) - ) - const responseData = await registerUser(user.userCsr, permsData, [userCert.userCertString], 'ownerCert') - expect(responseData.status).toEqual(200) - const isProperUserCert = await verifyUserCert(certRoot.rootCertString, responseData.body.certificate) - expect(isProperUserCert.result).toBe(true) - expect(responseData.body.peers.length).toBe(1) - expect(responseData.body.rootCa).toBe(certRoot.rootCertString) + const aliceCert = await issueCertificate(aliceCsr.userCsr, permsData) + if (!aliceCert.cert) return + const certificates: string[] = [aliceCert.cert] + const csrs: string[] = [userCsr.userCsr] + const payload: { certificates: string[]; csrs: string[] } = { + certificates: certificates, + csrs: csrs, + } + const pendingCsrs = await extractPendingCsrs(payload) + expect(pendingCsrs).toEqual(csrs) }) - it('returns 403 if username already exists and csr and cert public keys dont match', async () => { - const user = await createUserCsr({ - nickname: 'userName', + it('extractPendingCsrs should return filtered csrs, excluding those that tries to claim username already present in certificate', async () => { + const userCert = await issueCertificate(userCsr.userCsr, permsData) + if (!userCert.cert) return + const certificates: string[] = [userCert.cert] + const csrs: string[] = [userCsr.userCsr] + const payload: { certificates: string[]; csrs: string[] } = { + certificates: certificates, + csrs: csrs, + } + const pendingCsrs = await extractPendingCsrs(payload) + expect(pendingCsrs.length).toEqual(0) + }) + + it('extractPendingCsrs should return all csrs if there are no duplicates in requested usernames', async () => { + const userCsr2 = await createUserCsr({ + nickname: 'userName2', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey1', + dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) - const userCert = await createUserCert( - certRoot.rootCertString, - certRoot.rootKeyString, - user.userCsr, - new Date(), - new Date(2030, 1, 1) - ) - const userNew = await createUserCsr({ - nickname: 'username', - commonName: 'abcd.onion', - peerId: 'QmS9vJkgbea9EgzHvVPqhj1u4tH7YKq7eteDN7gnG5zUmc', - dmPublicKey: 'testdmPublicKey2', + const csrs: string[] = [userCsr.userCsr, userCsr2.userCsr] + const pendingCsrs = await extractPendingCsrs({ certificates: [], csrs: csrs }) + expect(pendingCsrs.length).toEqual(csrs.length) + }) + + it('Extract pending csrs should return only csrs that have unique usernames', async () => { + const userCsr = await createUserCsr({ + nickname: 'karol', + commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', + peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', + dmPublicKey: 'testdmPublicKey', + signAlg: configCrypto.signAlg, + hashAlg: configCrypto.hashAlg, + }) + const userCsr2 = await createUserCsr({ + nickname: 'karol', + commonName: 'nnnnnnc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', + peerId: 'QmffffffqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', + dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) - const response = await registerUser(userNew.userCsr, permsData, [userCert.userCertString], 'ownerCert') - expect(response.status).toEqual(403) + const csrs: string[] = [userCsr.userCsr, userCsr2.userCsr] + const pendingCsrs = await extractPendingCsrs({ certificates: [], csrs: csrs }) + expect(pendingCsrs.length).toEqual(1) + expect(pendingCsrs[0]).toBe(userCsr.userCsr) }) - it('returns 400 if no csr in data or csr has wrong format', async () => { - for (const invalidCsr of ['', 'abcd']) { - const response = await registerUser(invalidCsr, permsData, [], 'ownerCert') - expect(response.status).toEqual(400) - } - }) + it('wait for all NEW_USER events until emitting FINISHED_ISSUING_CERTIFICATES_FOR_ID', async () => { + registrationService.permsData = permsData - it('returns 400 if csr is lacking a field', async () => { - const csr = - 'MIIBFTCBvAIBADAqMSgwFgYKKwYBBAGDjBsCARMIdGVzdE5hbWUwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGPGHpJzE/CvL7l/OmTSfYQrhhnWQrYw3GgWB1raCTSeFI/MDVztkBOlxwdUWSm10+1OtKVUWeMKaMtyIYFcPPqAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFLjaEh+cnNhsi5qDsiMB/ZTzZFfqMAoGCCqGSM49BAMCA0gAMEUCIFwlob/Igab05EozU0e/lsG7c9BxEy4M4c4Jzru2vasGAiEAqFTQuQr/mVqTHO5vybWm/iNDk8vh88K6aBCCGYqIfdw=' - const response = await registerUser(csr, permsData, [], 'ownerCert') - expect(response.status).toEqual(400) - }) + const eventSpy = jest.spyOn(registrationService, 'emit') - it('returns 404 if fetching registrar address throws error', async () => { - console.log(fetch) - fetch.default.mockRejectedValue('User aborted request') - const communityId = 'communityID' - const response = await sendCertificateRegistrationRequest( - 'QmS9vJkgbea9EgzHvVPqhj1u4tH7YKq7eteDN7gnG5zUmc', - userCsr.userCsr, - communityId, - 1000, - createHttpsProxyAgent({ port: '12311', host: 'localhost' }) - ) - expect(response.eventType).toBe(RegistrationEvents.ERROR) - expect(response.data).toEqual({ - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.NOT_FOUND, - message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId, + const userCsr = await createUserCsr({ + nickname: 'alice', + commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', + peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', + dmPublicKey: 'testdmPublicKey', + signAlg: configCrypto.signAlg, + hashAlg: configCrypto.hashAlg, }) + const userCsr2 = await createUserCsr({ + nickname: 'karol', + commonName: 'nnnnnnc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', + peerId: 'QmffffffqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', + dmPublicKey: 'testdmPublicKey', + signAlg: configCrypto.signAlg, + hashAlg: configCrypto.hashAlg, + }) + + const csrs: string[] = [userCsr.userCsr, userCsr2.userCsr] + // @ts-ignore - fn 'issueCertificates' is private + await registrationService.issueCertificates({ certificates: [], csrs, id: 1 }) + + expect(eventSpy).toHaveBeenLastCalledWith(RegistrationEvents.FINISHED_ISSUING_CERTIFICATES_FOR_ID, { + id: 1, + }) + + expect(eventSpy).toHaveBeenCalledTimes(3) }) }) diff --git a/packages/backend/src/nest/registration/registration.service.ts b/packages/backend/src/nest/registration/registration.service.ts index c3d8a9b172..2f6e0d90d9 100644 --- a/packages/backend/src/nest/registration/registration.service.ts +++ b/packages/backend/src/nest/registration/registration.service.ts @@ -1,163 +1,76 @@ -import { Inject, Injectable, OnModuleInit } from '@nestjs/common' -import express from 'express' -import getPort from 'get-port' -import { Agent, Server } from 'http' +import { Injectable, OnModuleInit } from '@nestjs/common' import { EventEmitter } from 'events' -import { - registerOwner, - registerUser, - RegistrarResponse, - RegistrationResponse, - sendCertificateRegistrationRequest, -} from './registration.functions' -import { - ConnectionProcessInfo, - ErrorCodes, - ErrorMessages, - LaunchRegistrarPayload, - PermsData, - RegisterOwnerCertificatePayload, - SocketActionTypes, -} from '@quiet/types' -import { EXPRESS_PROVIDER } from '../const' +import { extractPendingCsrs, issueCertificate } from './registration.functions' +import { ErrorCodes, ErrorMessages, PermsData, RegisterOwnerCertificatePayload, SocketActionTypes } from '@quiet/types' import { RegistrationEvents } from './registration.types' -import { ServiceState } from '../connections-manager/connections-manager.types' import Logger from '../common/logger' @Injectable() export class RegistrationService extends EventEmitter implements OnModuleInit { private readonly logger = Logger(RegistrationService.name) - private _server: Server - private _port: number - public registrationService: any - public certificates: string[] + public certificates: string[] = [] private _permsData: PermsData - private _ownerCertificate: string - constructor(@Inject(EXPRESS_PROVIDER) public readonly _app: express.Application) { + constructor() { super() } onModuleInit() { - this.on(RegistrationEvents.SET_CERTIFICATES, certs => { - this.setCertificates(certs) - }) - this.setRouting() - } - - public setCertificates(certs: string[]) { - this.certificates = certs - } - - private pendingPromise: Promise | null = null - - private setRouting() { - // @ts-ignore - this._app.use(express.json()) - this._app.post('/register', async (req, res): Promise => { - if (this.pendingPromise) return - this.pendingPromise = this.registerUser(req.body.data) - const result = await this.pendingPromise - if (result) { - res.status(result.status).send(result.body) + this.on( + RegistrationEvents.REGISTER_USER_CERTIFICATE, + async (payload: { csrs: string[]; certificates: string[]; id: string }) => { + // Lack of permsData means that we are not the owner of the community in the official model of the app, however anyone can modify the source code, put malicious permsData here, issue false certificates and try to trick other users. + await this.issueCertificates(payload) } - this.pendingPromise = null - }) + ) } - public async listen(): Promise { - return await new Promise(resolve => { - this._server = this._app.listen(this._port, () => { - this.logger(`Certificate registration service listening on port: ${this._port}`) - resolve() + private async issueCertificates(payload: { csrs: string[]; certificates: string[]; id?: string }) { + if (!this._permsData) { + if (payload.id) this.emit(RegistrationEvents.FINISHED_ISSUING_CERTIFICATES_FOR_ID, { id: payload.id }) + return + } + const pendingCsrs = await extractPendingCsrs(payload) + + await Promise.all( + pendingCsrs.map(async csr => { + await this.registerUserCertificate(csr) }) - }) + ) + + if (payload.id) this.emit(RegistrationEvents.FINISHED_ISSUING_CERTIFICATES_FOR_ID, { id: payload.id }) } - public async stop(): Promise { - return await new Promise(resolve => { - if (!this._server) resolve() - this._server.close(() => { - this.logger('Certificate registration service closed') - resolve() - }) - }) + public set permsData(perms: PermsData) { + this._permsData = { + certificate: perms.certificate, + privKey: perms.privKey, + } } public async registerOwnerCertificate(payload: RegisterOwnerCertificatePayload): Promise { - let cert: string - try { - cert = await registerOwner(payload.userCsr.userCsr, payload.permsData) - } catch (e) { - this.logger.error(`Registering owner failed: ${e.message}`) + // FIXME: We should resolve problems with events order and we should set permsData only on LAUNCH_REGISTRART socket event in connectionsManager. + this._permsData = payload.permsData + const result = await issueCertificate(payload.userCsr.userCsr, this._permsData) + if (result?.cert) { + this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, { + communityId: payload.communityId, + network: { certificate: result.cert }, + }) + } else { this.emit(SocketActionTypes.ERROR, { type: SocketActionTypes.REGISTRAR, code: ErrorCodes.SERVER_ERROR, message: ErrorMessages.REGISTRATION_FAILED, community: payload.communityId, }) - return - } - this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, { - communityId: payload.communityId, - network: { certificate: cert, peers: [] }, - }) - this._ownerCertificate = cert - } - - public async sendCertificateRegistrationRequest( - serviceAddress: string, - userCsr: string, - communityId: string, - requestTimeout = 120000, - socksProxyAgent: Agent - ): Promise { - const response: RegistrationResponse = await sendCertificateRegistrationRequest( - serviceAddress, - userCsr, - communityId, - requestTimeout, - socksProxyAgent - ) - this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CONNECTING_TO_COMMUNITY) - this.emit(response.eventType, response.data) - } - - private async registerUser(csr: string): Promise<{ status: number; body: any }> { - const result = await registerUser(csr, this._permsData, this.certificates, this._ownerCertificate) - if (result?.status === 200) { - this.emit(RegistrationEvents.NEW_USER, { certificate: result.body.certificate, rootPermsData: this._permsData }) } - return result } - public async launchRegistrar(payload: LaunchRegistrarPayload): Promise { - this.emit(RegistrationEvents.REGISTRAR_STATE, ServiceState.LAUNCHING) - this._permsData = { - certificate: payload.rootCertString, - privKey: payload.rootKeyString, - } - this.logger(`Initializing registration service for peer ${payload.peerId}...`) - try { - await this.init(payload.privateKey) - } catch (err) { - this.logger.error(`Couldn't initialize certificate registration service: ${err as string}`) - return + public async registerUserCertificate(csr: string): Promise { + const result = await issueCertificate(csr, this._permsData) + if (result?.cert) { + this.emit(RegistrationEvents.NEW_USER, { certificate: result.cert }) } - try { - await this.listen() - } catch (err) { - this.logger.error(`Certificate registration service couldn't start listening: ${err as string}`) - } - this.emit(RegistrationEvents.REGISTRAR_STATE, ServiceState.LAUNCHED) - } - - public async init(privKey: string): Promise { - this._port = await getPort() - this.emit(RegistrationEvents.SPAWN_HS_FOR_REGISTRAR, { - port: this._port, - privateKey: privKey, - targetPort: 80, - }) } } diff --git a/packages/backend/src/nest/registration/registration.types.ts b/packages/backend/src/nest/registration/registration.types.ts index 62e81b59c1..b9025ff870 100644 --- a/packages/backend/src/nest/registration/registration.types.ts +++ b/packages/backend/src/nest/registration/registration.types.ts @@ -1,7 +1,6 @@ export enum RegistrationEvents { ERROR = 'error', - SPAWN_HS_FOR_REGISTRAR = 'spawnHsForRegistrar', NEW_USER = 'newUser', - SET_CERTIFICATES = 'setCertificates', - REGISTRAR_STATE = 'registrarState', + REGISTER_USER_CERTIFICATE = 'registerUserCertificate', + FINISHED_ISSUING_CERTIFICATES_FOR_ID = 'FINISHED_ISSUING_CERTIFICATES_FOR_ID', } diff --git a/packages/backend/src/nest/socket/socket.service.spec.ts b/packages/backend/src/nest/socket/socket.service.spec.ts index 6fb8613281..f32ba21103 100644 --- a/packages/backend/src/nest/socket/socket.service.spec.ts +++ b/packages/backend/src/nest/socket/socket.service.spec.ts @@ -1,27 +1,74 @@ +import { jest } from '@jest/globals' import { Test, TestingModule } from '@nestjs/testing' import { TestModule } from '../common/test.module' import { SocketModule } from './socket.module' import { SocketService } from './socket.service' +import { io, Socket } from 'socket.io-client' +import waitForExpect from 'wait-for-expect' +import { SocketActionTypes } from '@quiet/types' +import { suspendableSocketEvents } from './suspendable.events' +import { TEST_DATA_PORT } from '../const' describe('SocketService', () => { let module: TestingModule let socketService: SocketService + let client: Socket + beforeAll(async () => { module = await Test.createTestingModule({ imports: [TestModule, SocketModule], }).compile() socketService = await module.resolve(SocketService) + + module.init() + + client = io(`http://127.0.0.1:${TEST_DATA_PORT}`) }) afterAll(async () => { + client.close() + socketService.close() + await module.close() }) - it('start and stop data server', async () => { + it('sets no default cors', async () => { expect(socketService.serverIoProvider.io.engine.opts.cors).toStrictEqual({}) // No cors should be set by default - await socketService.listen() - await socketService.close() + }) + + it('suspends events handling until backend is fully initialized', async () => { + const spy = jest.spyOn(socketService, 'emit') + + const event = suspendableSocketEvents[0] + + client.emit(event) + + expect(spy).not.toBeCalledWith(event, undefined) + + socketService.resolveReadyness() + + await waitForExpect(() => { + expect(spy).toHaveBeenCalledWith(event, undefined) + }) + }) + + it('there are no fragile endpoints in the collection of suspendables', async () => { + const fragile: string[] = [ + SocketActionTypes.CREATE_NETWORK.valueOf(), + SocketActionTypes.CREATE_COMMUNITY.valueOf(), + SocketActionTypes.LAUNCH_COMMUNITY.valueOf(), + SocketActionTypes.LAUNCH_REGISTRAR.valueOf(), + SocketActionTypes.REGISTER_OWNER_CERTIFICATE.valueOf(), + SocketActionTypes.REGISTER_USER_CERTIFICATE.valueOf(), + SocketActionTypes.SAVE_OWNER_CERTIFICATE.valueOf(), + SocketActionTypes.SAVE_USER_CSR.valueOf(), + SocketActionTypes.SEND_COMMUNITY_METADATA.valueOf(), + ] + + fragile.forEach(event => { + expect(suspendableSocketEvents).not.toContain(event) + }) }) }) diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index 4936e4a32a..5d687fe2a2 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -7,7 +7,6 @@ import { DownloadFilePayload, CancelDownloadPayload, AskForMessagesPayload, - RegisterUserCertificatePayload, ConnectionProcessInfo, RegisterOwnerCertificatePayload, SaveOwnerCertificatePayload, @@ -15,27 +14,39 @@ import { LaunchRegistrarPayload, Community, DeleteFilesFromChannelSocketPayload, + SaveCSRPayload, + CommunityMetadata, } from '@quiet/types' -import cors, { CorsOptions } from 'cors' import EventEmitter from 'events' import { CONFIG_OPTIONS, SERVER_IO_PROVIDER } from '../const' import { ConfigOptions, ServerIoProviderTypes } from '../types' +import { suspendableSocketEvents } from './suspendable.events' import Logger from '../common/logger' @Injectable() export class SocketService extends EventEmitter implements OnModuleInit { private readonly logger = Logger(SocketService.name) + + public resolveReadyness: (value: void | PromiseLike) => void + public readyness: Promise + constructor( @Inject(SERVER_IO_PROVIDER) public readonly serverIoProvider: ServerIoProviderTypes, @Inject(CONFIG_OPTIONS) public readonly configOptions: ConfigOptions ) { super() + + this.readyness = new Promise(resolve => { + this.resolveReadyness = resolve + }) } async onModuleInit() { this.logger('init:started') + this.attachListeners() await this.init() + this.logger('init:finished') } @@ -46,6 +57,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { resolve() }) }) + await this.listen() await connection @@ -55,102 +67,153 @@ export class SocketService extends EventEmitter implements OnModuleInit { // Attach listeners here this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { this.logger('socket connection') + // On websocket connection, update presentation service with network data this.emit(SocketActionTypes.CONNECTION) + socket.on(SocketActionTypes.CLOSE, async () => { this.emit(SocketActionTypes.CLOSE) }) + + socket.use(async (event, next) => { + const type = event[0] + if (suspendableSocketEvents.includes(type)) { + this.logger('Awaiting readyness before emitting: ', type) + await this.readyness + } + next() + }) + + // ====== Channels ===== socket.on(SocketActionTypes.CREATE_CHANNEL, async (payload: CreateChannelPayload) => { this.emit(SocketActionTypes.CREATE_CHANNEL, payload) }) + + socket.on(SocketActionTypes.DELETE_CHANNEL, async (payload: { channelId: string; ownerPeerId: string }) => { + this.emit(SocketActionTypes.DELETE_CHANNEL, payload) + }) + + // ====== Messages ====== socket.on(SocketActionTypes.SEND_MESSAGE, async (payload: SendMessagePayload) => { this.emit(SocketActionTypes.SEND_MESSAGE, payload) }) + + socket.on(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, async (peerId: string, conversations: string[]) => { + this.emit(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, { peerId, conversations }) + }) + + socket.on(SocketActionTypes.ASK_FOR_MESSAGES, async (payload: AskForMessagesPayload) => { + this.emit(SocketActionTypes.ASK_FOR_MESSAGES, payload) + }) + + // ====== Files ====== socket.on(SocketActionTypes.UPLOAD_FILE, async (payload: UploadFilePayload) => { this.emit(SocketActionTypes.UPLOAD_FILE, payload.file) }) + socket.on(SocketActionTypes.DOWNLOAD_FILE, async (payload: DownloadFilePayload) => { this.emit(SocketActionTypes.DOWNLOAD_FILE, payload.metadata) }) + socket.on(SocketActionTypes.CANCEL_DOWNLOAD, async (payload: CancelDownloadPayload) => { this.emit(SocketActionTypes.CANCEL_DOWNLOAD, payload.mid) }) + + socket.on(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, async (payload: DeleteFilesFromChannelSocketPayload) => { + this.emit(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, payload) + }) + + // ====== Direct Messages ====== socket.on( SocketActionTypes.INITIALIZE_CONVERSATION, async (peerId: string, { address, encryptedPhrase }: { address: string; encryptedPhrase: string }) => { this.emit(SocketActionTypes.INITIALIZE_CONVERSATION, { address, encryptedPhrase }) } ) + socket.on(SocketActionTypes.GET_PRIVATE_CONVERSATIONS, async (peerId: string) => { this.emit(SocketActionTypes.GET_PRIVATE_CONVERSATIONS, { peerId }) }) + socket.on( SocketActionTypes.SEND_DIRECT_MESSAGE, async (peerId: string, { channelId, message }: { channelId: string; message: string }) => { this.emit(SocketActionTypes.SEND_DIRECT_MESSAGE, { channelId, message }) } ) + socket.on(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, async (peerId: string, channelId: string) => { this.emit(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, { peerId, channelId }) }) - socket.on(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, async (peerId: string, conversations: string[]) => { - this.emit(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, { peerId, conversations }) - }) - socket.on(SocketActionTypes.ASK_FOR_MESSAGES, async (payload: AskForMessagesPayload) => { - this.emit(SocketActionTypes.ASK_FOR_MESSAGES, payload) - }) - socket.on(SocketActionTypes.REGISTER_USER_CERTIFICATE, async (payload: RegisterUserCertificatePayload) => { - this.logger(`Registering user certificate (${payload.communityId}) on ${payload.serviceAddress}`) - this.emit(SocketActionTypes.REGISTER_USER_CERTIFICATE, payload) + // ====== Certificates ====== + socket.on(SocketActionTypes.SAVE_USER_CSR, async (payload: SaveCSRPayload) => { + this.logger(`On ${SocketActionTypes.SAVE_USER_CSR}`) + + this.emit(SocketActionTypes.SAVE_USER_CSR, payload) + await new Promise(resolve => setTimeout(() => resolve(), 2000)) - this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_USER_CERTIFICATE) + + this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.WAITING_FOR_METADATA) }) + socket.on(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, async (payload: RegisterOwnerCertificatePayload) => { this.logger(`Registering owner certificate (${payload.communityId})`) + this.emit(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, payload) this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_OWNER_CERTIFICATE) }) + socket.on(SocketActionTypes.SAVE_OWNER_CERTIFICATE, async (payload: SaveOwnerCertificatePayload) => { this.logger(`Saving owner certificate (${payload.peerId}), community: ${payload.id}`) + this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload) + + const communityMetadataPayload: CommunityMetadata = { + id: payload.id, + ownerCertificate: payload.certificate, + rootCa: payload.permsData.certificate, + } + this.emit(SocketActionTypes.SEND_COMMUNITY_METADATA, communityMetadataPayload) + }) + + // ====== Community ====== + socket.on(SocketActionTypes.SEND_COMMUNITY_METADATA, (payload: CommunityMetadata) => { + this.emit(SocketActionTypes.SEND_COMMUNITY_METADATA, payload) }) + socket.on(SocketActionTypes.CREATE_COMMUNITY, async (payload: InitCommunityPayload) => { this.logger(`Creating community ${payload.id}`) this.emit(SocketActionTypes.CREATE_COMMUNITY, payload) }) + socket.on(SocketActionTypes.LAUNCH_COMMUNITY, async (payload: InitCommunityPayload) => { this.logger(`Launching community ${payload.id} for ${payload.peerId.id}`) this.emit(SocketActionTypes.LAUNCH_COMMUNITY, payload) this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.LAUNCHING_COMMUNITY) }) + socket.on(SocketActionTypes.LAUNCH_REGISTRAR, async (payload: LaunchRegistrarPayload) => { this.logger(`Launching registrar for community ${payload.id}, user ${payload.peerId}`) this.emit(SocketActionTypes.LAUNCH_REGISTRAR, payload) }) + socket.on(SocketActionTypes.CREATE_NETWORK, async (community: Community) => { this.logger(`Creating network for community ${community.id}`) this.emit(SocketActionTypes.CREATE_NETWORK, community) }) + socket.on(SocketActionTypes.LEAVE_COMMUNITY, async () => { - this.logger('leaving community') + this.logger('Leaving community') this.emit(SocketActionTypes.LEAVE_COMMUNITY) }) - socket.on(SocketActionTypes.DELETE_CHANNEL, async (payload: { channelId: string; ownerPeerId: string }) => { - this.logger('deleting channel ', payload.channelId) - this.emit(SocketActionTypes.DELETE_CHANNEL, payload) - }) - socket.on(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, async (payload: DeleteFilesFromChannelSocketPayload) => { - this.logger('DELETE_FILES_FROM_CHANNEL', payload) - this.emit(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, payload) - }) }) } public listen = async (port = this.configOptions.socketIOPort): Promise => { return await new Promise(resolve => { if (this.serverIoProvider.server.listening) resolve() - this.serverIoProvider.server.listen(this.configOptions.socketIOPort, () => { + this.serverIoProvider.server.listen(this.configOptions.socketIOPort, '127.0.0.1', () => { this.logger(`Data server running on port ${this.configOptions.socketIOPort}`) resolve() }) diff --git a/packages/backend/src/nest/socket/suspendable.events.ts b/packages/backend/src/nest/socket/suspendable.events.ts new file mode 100644 index 0000000000..f87bce2f45 --- /dev/null +++ b/packages/backend/src/nest/socket/suspendable.events.ts @@ -0,0 +1,26 @@ +import { SocketActionTypes } from '@quiet/types' + +export const suspendableSocketEvents: string[] = [ + // Channels + SocketActionTypes.CREATE_CHANNEL.valueOf(), + SocketActionTypes.DELETE_CHANNEL.valueOf(), + + // Files + SocketActionTypes.UPLOAD_FILE.valueOf(), + SocketActionTypes.DOWNLOAD_FILE.valueOf(), + SocketActionTypes.CANCEL_DOWNLOAD.valueOf(), + SocketActionTypes.CHECK_FOR_MISSING_FILES.valueOf(), + SocketActionTypes.DELETE_FILES_FROM_CHANNEL.valueOf(), + + // Messages + SocketActionTypes.SEND_MESSAGE.valueOf(), + SocketActionTypes.ASK_FOR_MESSAGES.valueOf(), + SocketActionTypes.UPDATE_MESSAGE_MEDIA.valueOf(), + + // Private Conversations + SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS.valueOf(), + SocketActionTypes.GET_PRIVATE_CONVERSATIONS.valueOf(), + SocketActionTypes.INITIALIZE_CONVERSATION.valueOf(), + SocketActionTypes.SEND_DIRECT_MESSAGE.valueOf(), + SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD.valueOf(), +] diff --git a/packages/backend/src/nest/storage/storage.service.spec.ts b/packages/backend/src/nest/storage/storage.service.spec.ts index 8e95303aad..c4cf8b9483 100644 --- a/packages/backend/src/nest/storage/storage.service.spec.ts +++ b/packages/backend/src/nest/storage/storage.service.spec.ts @@ -1,6 +1,6 @@ import { LazyModuleLoader } from '@nestjs/core' import { Test, TestingModule } from '@nestjs/testing' -import { createUserCert, keyFromCertificate, parseCertificate } from '@quiet/identity' +import { createUserCert, keyFromCertificate, loadCSR, parseCertificate } from '@quiet/identity' import { prepareStore, getFactory, @@ -40,6 +40,7 @@ import { LocalDbModule } from '../local-db/local-db.module' import { LocalDbService } from '../local-db/local-db.service' import { IPFS_REPO_PATCH, ORBIT_DB_DIR, QUIET_DIR } from '../const' import { LocalDBKeys } from '../local-db/local-db.types' +import { RegistrationEvents } from '../registration/registration.types' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -255,6 +256,10 @@ describe('StorageService', () => { const channelsDbAddress = storageService.channels?.address // @ts-expect-error 'certificates' is private const certificatesDbAddress = storageService.certificates.address + // @ts-expect-error 'certificatesRequests' is private + const certificatesRequestsDbAddress = storageService.certificatesRequests.address + // @ts-expect-error 'communityMetadata' is private + const communityMetadataDbAddress = storageService.communityMetadata.address expect(channelsDbAddress).not.toBeFalsy() expect(certificatesDbAddress).not.toBeFalsy() expect(subscribeToPubSubSpy).toBeCalledTimes(2) @@ -262,6 +267,8 @@ describe('StorageService', () => { expect(subscribeToPubSubSpy).toHaveBeenNthCalledWith(1, [ StorageService.dbAddress(channelsDbAddress), StorageService.dbAddress(certificatesDbAddress), + StorageService.dbAddress(certificatesRequestsDbAddress), + StorageService.dbAddress(communityMetadataDbAddress), ]) // Creating channel: expect(subscribeToPubSubSpy).toHaveBeenNthCalledWith(2, [StorageService.dbAddress(db.address)]) @@ -289,26 +296,6 @@ describe('StorageService', () => { expect(result).toBe(true) }) - it('is not saved to db if did not pass verification', async () => { - const oldUserCertificate = await createUserCert( - rootPermsData.certificate, - rootPermsData.privKey, - // @ts-expect-error userCsr can be undefined - alice.userCsr?.userCsr, - new Date(2021, 1, 1), - new Date(2021, 1, 2) - ) - - await storageService.init(peerId) - - const result = await storageService.saveCertificate({ - certificate: oldUserCertificate.userCertString, - rootPermsData, - }) - - expect(result).toBe(false) - }) - it('is not saved to db if empty', async () => { await storageService.init(peerId) @@ -540,7 +527,7 @@ describe('StorageService', () => { }) describe('Users', () => { - it('gets all users from db', async () => { + it('gets all registered users from db', async () => { await storageService.init(peerId) const mockGetCertificates = jest.fn() // @ts-ignore - Property 'getAllEventLogEntries' is protected @@ -549,7 +536,7 @@ describe('StorageService', () => { 'MIICWzCCAgGgAwIBAgIGAYKIVrmoMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBG1haW4wHhcNMjIwODEwMTUxOTIxWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz5wM29xZHI1M2RrZ2czbjVudWV6bHp5YXdoeHZpdDVlZnh6bHVudnpwN243bG12YTZmajNpNDNhZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCAjxbiV781WC8O5emEdavPaQfR0FD8CaqC+P3R3uRdL9xuzGeUu8f5NIplSJ6abBMnanGgcMs34u82buiFROHqjggENMIIBCTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLwYJKoZIhvcNAQkMBCIEICSr5xj+pjBSb+YOZ7TMPQJHYs4KASfnc9TugSpKJUG/MBUGCisGAQQBg4wbAgEEBxMFZGV2dnYwPQYJKwYBAgEPAwEBBDATLlFtVlRrVWFkMkdxM01rQ2E4Z2YxMlIxZ3NXRGZrMnlpVEVxYjZZR1hERzJpUTMwSQYDVR0RBEIwQII+cDNvcWRyNTNka2dnM241bnVlemx6eWF3aHh2aXQ1ZWZ4emx1bnZ6cDduN2xtdmE2ZmozaTQzYWQub25pb24wCgYIKoZIzj0EAwIDSAAwRQIhAIXhkkgs3H6GcZ1GYrSL2qJYDRQcpZlmcbq7YjpJHaORAiBMfkwP75v08R/ud6BPWvdS36corT+596+HzpqFt6bffw==', 'MIICYTCCAgegAwIBAgIGAYKIYnYuMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBG1haW4wHhcNMjIwODEwMTUzMjEwWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz52bnl3dWl5bDdwN2lnMm11cmNzY2R5emtza281M2U0azNkcGRtMnlvb3B2dnUyNXA2d3dqcWJhZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0cOt7jMJ6YhRvL9nhbDCh42QJPKDet/Zc2PJ9rm6CzYz1IXc5uRUCUNZSnNykVMZknogAavp0FjV+cFXzV8gGjggETMIIBDzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLwYJKoZIhvcNAQkMBCIEIIsBwPwIhLSltj9dnkgkMq3sOe3RVha9Mhukop6XOoISMBsGCisGAQQBg4wbAgEEDRMLZHNrZmpia3NmaWcwPQYJKwYBAgEPAwEBBDATLlFtZDJVbjlBeW5va1pyY1pHc011YXFndXBUdGlkSEdRblVrTlZmRkZBZWY5N0MwSQYDVR0RBEIwQII+dm55d3VpeWw3cDdpZzJtdXJjc2NkeXprc2tvNTNlNGszZHBkbTJ5b29wdnZ1MjVwNnd3anFiYWQub25pb24wCgYIKoZIzj0EAwIDSAAwRQIgAiCmGfUuSG010CxLEzu9mAQOgDq//SHI9LkXbmCxaAUCIQC9xzmkRBxq5HmNomYJ9ZAJXaY3J6+VqBYthaVnv0bhMw==', ]) - const allUsers = storageService.getAllUsers() + const allUsers = storageService.getAllRegisteredUsers() expect(allUsers).toStrictEqual([ { onionAddress: 'p3oqdr53dkgg3n5nuezlzyawhxvit5efxzlunvzp7n7lmva6fj3i43ad.onion', @@ -565,6 +552,65 @@ describe('StorageService', () => { }, ]) }) + it('gets all users from db', async () => { + await storageService.init(peerId) + const mockGetCsrs = jest.fn() + // @ts-ignore - Property 'getAllEventLogEntries' is protected + storageService.getAllEventLogEntries = mockGetCsrs + mockGetCsrs.mockReturnValue([ + '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', + ]) + const allUsers = storageService.getAllUsers() + + expect(allUsers).toStrictEqual([ + { + onionAddress: '6vu2bxki777it3cpayv6fq6vpl4ke3kzj7gxicfygm55dhhtphyfdvyd.onion', + peerId: 'QmXRY4rhAx8Muq8dMGkr9qknJdE6UHZDdGaDRTQEbwFN5b', + dmPublicKey: + '299abdb3a1c456245fa65560624ba583a5d5e35d5945ee14d548df5d37f5a516b281f71e9c66d3e9145a80fb59ce3baae611dd621a838d1cd98676e7f1901261380133ba9cb1e52ae54a52d69134f032368de3caa9ab69dcfce6e9afcc192ce21129d1131efce0994a7f028c8c9977dd403c9509bd9e149e0081f2071ddce0b7fc217756da7deb9e16d57e3a41300c7087a96df9ee5a82d197cbeabfc4011251e2cf7d481161672d94b82df0ea7e593a4bc81919b1516806f26b32dd9774cd11237d98cd346be7a19715ae15c90998e715f095048f8227403fb8cf915ca035ca1e5ef033d990a4dc84750a7de65fc9cc928e773250cd68c625754400d2e836f3f4bb0654a56aad820c3e0e977b304eaec2e0a193c4aac8dfaaee614671ecd11a40317957b56fa8f099d1f6684c826e4984d0ebb8', + username: 'o', + }, + { + onionAddress: 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd.onion', + peerId: 'QmT18UvnUBkseMc3SqnfPxpHwN8nzLrJeNSLZtc8rAFXhz', + dmPublicKey: + '050bf2be48a25c47ff3cb2a7b11d416bd02162c54ef28e7a6e77ed527a15cc19516605332c9bce4aba0f93f1266d5cfea2484cadc60d802587bf1ac0a87555ab42513fa6e2f7c06d094a2dc7a38f147a792b94022fad21a1dd6e02784aa1cc891bdb6ca0cf5a4be92b9ff93e83844206388f676e5c0bb406fd436beb0c8843c7a5061a3830a9bc88ea7b112852b19395d5feb1ae75d8082ff3c9473ce4612ec9c2f35489a0010bcf0136dd333638cd21a734b1f192f494371691f4b9c459ffa2d637aa70e8973af99dc94e417b766a8462afbc183fe6fc8b4220887de6a98d97a69cba851d0062b609439b6c6728be87cccc9f6680073d5ed8716611e2ca54f49bbc43c6aa49e02bcb1eecc79e5e491e063b95104e8e43ad09fac6d930399ff2b6c3a49c5b6117c8dc03db9300c0ca0364f29425', + username: 'o', + }, + ]) + }) + + it('getCsrs - remove old csrs and replace with new for each pubkey', async () => { + await storageService.init(peerId) + const allCsrs = [ + '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', + ] + + const mockGetAllEventLogEntries = jest.fn() + // @ts-ignore - Property 'getAllEventLogEntries' is protected + storageService.getAllEventLogEntries = mockGetAllEventLogEntries + mockGetAllEventLogEntries.mockReturnValue(allCsrs) + + const filteredCsrs = await storageService.getCsrs() + + expect(filteredCsrs.length).toEqual(allCsrs.length - 1) + + expect(filteredCsrs).toEqual([ + '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', + ]) + }) }) describe('Files deletion', () => { @@ -629,4 +675,63 @@ describe('StorageService', () => { await expect(storageService.deleteFilesFromChannel(messages)).resolves.not.toThrowError() }) }) + + describe('replicate certificatesRequests event', () => { + const replicatedEvent = async () => { + // @ts-ignore - Property 'certificates' is private + storageService.certificatesRequests.events.emit('replicated') + await new Promise(resolve => setTimeout(() => resolve(), 2000)) + } + + it('replicated event ', async () => { + await storageService.init(peerId) + const spyOnUpdatePeersList = jest.spyOn(storageService, 'updatePeersList') + await replicatedEvent() + expect(spyOnUpdatePeersList).toBeCalledTimes(1) + }) + + it('2 replicated events - first not resolved ', async () => { + await storageService.init(peerId) + const spyOnUpdatePeersList = jest.spyOn(storageService, 'updatePeersList') + await replicatedEvent() + await replicatedEvent() + expect(spyOnUpdatePeersList).toBeCalledTimes(1) + }) + + it('2 replicated events - first resolved ', async () => { + await storageService.init(peerId) + const spyOnUpdatePeersList = jest.spyOn(storageService, 'updatePeersList') + await replicatedEvent() + await replicatedEvent() + storageService.resolveCsrReplicatedPromise(1) + await new Promise(resolve => setTimeout(() => resolve(), 500)) + expect(spyOnUpdatePeersList).toBeCalledTimes(2) + }) + + it('3 replicated events - no resolved promises', async () => { + await storageService.init(peerId) + const spyOnUpdatePeersList = jest.spyOn(storageService, 'updatePeersList') + + await replicatedEvent() + await replicatedEvent() + await replicatedEvent() + + expect(spyOnUpdatePeersList).toBeCalledTimes(1) + }) + + it('3 replicated events - two resolved promises ', async () => { + await storageService.init(peerId) + const spyOnUpdatePeersList = jest.spyOn(storageService, 'updatePeersList') + + await replicatedEvent() + await replicatedEvent() + storageService.resolveCsrReplicatedPromise(1) + await new Promise(resolve => setTimeout(() => resolve(), 500)) + await replicatedEvent() + storageService.resolveCsrReplicatedPromise(2) + await new Promise(resolve => setTimeout(() => resolve(), 500)) + + expect(spyOnUpdatePeersList).toBeCalledTimes(3) + }) + }) }) diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index ea9cae3c47..f5f4c10a3b 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -7,6 +7,9 @@ import { parseCertificate, verifySignature, verifyUserCert, + parseCertificationRequest, + getReqFieldValue, + loadCSR, } from '@quiet/identity' import type { IPFS } from 'ipfs-core' import OrbitDB from 'orbit-db' @@ -15,21 +18,23 @@ import KeyValueStore from 'orbit-db-kvstore' import path from 'path' import { EventEmitter } from 'events' import PeerId from 'peer-id' -import { getCrypto } from 'pkijs' +import { CertificationRequest, getCrypto } from 'pkijs' import { stringToArrayBuffer } from 'pvutils' import validate from '../validation/validators' import { CID } from 'multiformats/cid' import { ChannelMessage, + CommunityMetadata, ConnectionProcessInfo, DeleteFilesFromChannelSocketPayload, FileMetadata, NoCryptoEngineError, PublicChannel, PushNotificationPayload, + SaveCSRPayload, SaveCertificatePayload, SocketActionTypes, - User, + UserData, } from '@quiet/types' import { isDefined } from '@quiet/common' import fs from 'fs' @@ -46,24 +51,34 @@ import Logger from '../common/logger' import { DirectMessagesRepo, PublicChannelsRepo } from '../common/types' import { removeFiles, removeDirs, createPaths, getUsersAddresses } from '../common/utils' import { StorageEvents } from './storage.types' +import { RegistrationEvents } from '../registration/registration.types' interface DBOptions { replicate: boolean } +interface CsrReplicatedPromiseValues { + promise: Promise + resolveFunction: any +} + @Injectable() export class StorageService extends EventEmitter { public channels: KeyValueStore private certificates: EventStore + private certificatesRequests: EventStore public publicChannelsRepos: Map = new Map() public directMessagesRepos: Map = new Map() private publicKeysMap: Map = new Map() private userNamesMap: Map = new Map() + private communityMetadata: KeyValueStore private ipfs: IPFS private orbitDb: OrbitDB private filesManager: IpfsFileManagerService private peerId: PeerId | null = null private ipfsStarted: boolean + public csrReplicatedPromiseMap: Map = new Map() + private csrReplicatedPromiseId: number = 0 private readonly logger = Logger(StorageService.name) constructor( @@ -141,6 +156,11 @@ export class StorageService extends EventEmitter { }) } + public resetCsrReplicatedMapAndId() { + this.csrReplicatedPromiseMap = new Map() + this.csrReplicatedPromiseId = 0 + } + private async startReplicate() { const dbs = [] @@ -150,6 +170,12 @@ export class StorageService extends EventEmitter { if (this.certificates?.address) { dbs.push(this.certificates.address) } + if (this.certificatesRequests?.address) { + dbs.push(this.certificatesRequests.address) + } + if (this.communityMetadata?.address) { + dbs.push(this.communityMetadata.address) + } const channels = this.publicChannelsRepos.values() @@ -172,7 +198,7 @@ export class StorageService extends EventEmitter { return } for (const a of addr) { - this.logger(`Pubsub - subscribe to ${addr}`) + this.logger(`Pubsub - subscribe to ${a}`) // @ts-ignore await this.orbitDb._pubsub.subscribe( a, @@ -203,16 +229,53 @@ export class StorageService extends EventEmitter { } public async initDatabases() { - this.logger('1/4') + this.logger('1/5') await this.createDbForChannels() - this.logger('2/4') + this.logger('2/5') await this.createDbForCertificates() - this.logger('3/4') + this.logger('3/5') + await this.createDbForCertificatesRequests() + this.logger('4/5') + await this.createDbForCommunityMetadata() + this.logger('5/5') await this.initAllChannels() - this.logger('4/4') this.logger('Initialized DBs') this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.INITIALIZED_DBS) } + private async createDbForCommunityMetadata() { + this.logger('createDbForCommunityMetadata init') + this.communityMetadata = await this.orbitDb.keyvalue('community-metadata', { + replicate: false, + accessController: { + write: ['*'], + }, + }) + + this.communityMetadata.events.on('write', async (_address, _entry) => { + this.logger('WRITE: communityMetadata') + }) + + this.communityMetadata.events.on('replicated', async () => { + this.logger('Replicated community metadata') + // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' + await this.communityMetadata.load({ fetchEntryTimeout: 15000 }) + this.emit(StorageEvents.REPLICATED_COMMUNITY_METADATA, Object.values(this.communityMetadata.all)[0]) + }) + // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' + await this.communityMetadata.load({ fetchEntryTimeout: 15000 }) + } + + public async updateCommunityMetadata(communityMetadata: CommunityMetadata) { + this.logger(`About to update community metadata`) + if (!communityMetadata.id) return + const meta = this.communityMetadata.get(communityMetadata.id) + if (meta?.ownerCertificate && meta?.rootCa) return + this.logger(`Updating community metadata`) + await this.communityMetadata.put(communityMetadata.id, { + ...meta, + ...communityMetadata, + }) + } private async __stopOrbitDb() { if (this.orbitDb) { @@ -245,19 +308,25 @@ export class StorageService extends EventEmitter { public async stopOrbitDb() { try { - if (this.channels) { - await this.channels.close() - } + await this.channels?.close() } catch (e) { - this.logger.error('channels', e) + this.logger.error('Error closing channels db', e) } try { - if (this.certificates) { - await this.certificates.close() - } + await this.certificates?.close() + } catch (e) { + this.logger.error('Error closing certificates db', e) + } + try { + await this.certificatesRequests?.close() + } catch (e) { + this.logger.error('Error closing certificates db', e) + } + try { + await this.communityMetadata?.close() } catch (e) { - this.logger.error('certificates', e) + this.logger.error('Error closing community metadata db', e) } await this.__stopOrbitDb() await this.__stopIPFS() @@ -265,7 +334,9 @@ export class StorageService extends EventEmitter { public async updatePeersList() { const allUsers = this.getAllUsers() - const peers = await getUsersAddresses(allUsers) + const registeredUsers = this.getAllRegisteredUsers() + const peers = [...new Set(await getUsersAddresses(allUsers.concat(registeredUsers)))] + console.log('updatePeersList, peers count:', peers.length) const community = await this.localDbService.get(LocalDBKeys.COMMUNITY) this.emit(StorageEvents.UPDATE_PEERS_LIST, { communityId: community.id, peerList: peers }) } @@ -332,6 +403,100 @@ export class StorageService extends EventEmitter { this.logger('STORAGE: Finished createDbForCertificates') } + private createCsrReplicatedPromise(id: number) { + let resolveFunction + const promise = new Promise(resolve => { + resolveFunction = resolve + }) + + this.csrReplicatedPromiseMap.set(id, { promise, resolveFunction }) + } + + public resolveCsrReplicatedPromise(id: number) { + const csrReplicatedPromiseMapId = this.csrReplicatedPromiseMap.get(id) + if (csrReplicatedPromiseMapId) { + csrReplicatedPromiseMapId?.resolveFunction(id) + this.csrReplicatedPromiseMap.delete(id) + } else { + console.log(`No promise with ID ${id} found.`) + return + } + } + + public async createDbForCertificatesRequests() { + this.logger('certificatesRequests db init') + this.certificatesRequests = await this.orbitDb.log('csrs', { + replicate: false, + accessController: { + write: ['*'], + }, + }) + + // DOCS -> handleCsrReplicationEvent.md + this.certificatesRequests.events.on('replicated', async () => { + this.logger('REPLICATED: CSRs') + + this.csrReplicatedPromiseId++ + + const filteredCsrs = await this.getCsrs() + + const allCertificates = this.getAllEventLogEntries(this.certificates) + + this.createCsrReplicatedPromise(this.csrReplicatedPromiseId) + + if (this.csrReplicatedPromiseId > 1) { + const csrReplicatedPromiseMapId = this.csrReplicatedPromiseMap.get(this.csrReplicatedPromiseId - 1) + + if (csrReplicatedPromiseMapId?.promise) { + await csrReplicatedPromiseMapId.promise + } + } + + this.emit(StorageEvents.REPLICATED_CSR, { + csrs: filteredCsrs, + certificates: allCertificates, + id: this.csrReplicatedPromiseId, + }) + + await this.updatePeersList() + }) + this.certificatesRequests.events.on('write', async (_address, entry) => { + const csr: string = entry.payload.value + this.logger('Saved CSR locally') + const allCertificates = this.getAllEventLogEntries(this.certificates) + this.emit(StorageEvents.REPLICATED_CSR, { csrs: [csr], certificates: allCertificates }) + await this.updatePeersList() + }) + + // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' + await this.certificatesRequests.load({ fetchEntryTimeout: 15000 }) + const allcsrs = this.getAllEventLogEntries(this.certificatesRequests) + this.logger('ALL Certificates COUNT:', allcsrs.length) + this.logger('STORAGE: Finished creating certificatesRequests db') + } + + public async getCsrs(): Promise { + // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' + await this.certificatesRequests.load({ fetchEntryTimeout: 15000 }) + const allCsrs = this.getAllEventLogEntries(this.certificatesRequests) + const filteredCsrsMap: Map = new Map() + + await Promise.all( + allCsrs.map(async csr => { + const parsedCsr = await loadCSR(csr) + const pubKey = keyFromCertificate(parsedCsr) + + if (filteredCsrsMap.has(pubKey)) { + filteredCsrsMap.delete(pubKey) + } + + filteredCsrsMap.set(pubKey, csr) + }) + ) + + return [...filteredCsrsMap.values()] + } + public async loadAllChannels() { this.logger('Getting all channels') // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' @@ -706,20 +871,39 @@ export class StorageService extends EventEmitter { this.logger('Certificate is either null or undefined, not saving to db') return false } - const verification = await verifyUserCert(payload.rootPermsData.certificate, payload.certificate) - if (verification.resultCode !== 0) { - this.logger.error('Certificate is not valid') - this.logger.error(verification.resultMessage) - return false - } this.logger('Saving certificate...') await this.certificates.add(payload.certificate) return true } - public getAllUsers(): User[] { + public async saveCSR(payload: SaveCSRPayload): Promise { + this.logger('About to save csr...') + if (!payload.csr) { + this.logger('CSR is either null or undefined, not saving to db') + return false + } + // TODO: Verify CSR + try { + parseCertificationRequest(payload.csr) + } catch (e) { + this.logger.error(`Cannot save csr ${payload.csr}. Reason: ${e.message}`) + return false + } + + await this.certificatesRequests.load() + + const csrs = this.getAllEventLogEntries(this.certificatesRequests) + + if (csrs.includes(payload.csr)) return false + + this.logger('Saving csr...') + await this.certificatesRequests.add(payload.csr) + return true + } + + public getAllRegisteredUsers(): UserData[] { const certs = this.getAllEventLogEntries(this.certificates) - const allUsers: User[] = [] + const allUsers: UserData[] = [] for (const cert of certs) { const parsedCert = parseCertificate(cert) const onionAddress = getCertFieldValue(parsedCert, CertFieldsTypes.commonName) @@ -732,6 +916,22 @@ export class StorageService extends EventEmitter { return allUsers } + public getAllUsers(): UserData[] { + const csrs = this.getAllEventLogEntries(this.certificatesRequests) + console.log('csrs count:', csrs.length) + const allUsers: UserData[] = [] + for (const csr of csrs) { + const parsedCert = parseCertificationRequest(csr) + const onionAddress = getReqFieldValue(parsedCert, CertFieldsTypes.commonName) + const peerId = getReqFieldValue(parsedCert, CertFieldsTypes.peerId) + const username = getReqFieldValue(parsedCert, CertFieldsTypes.nickName) + const dmPublicKey = getReqFieldValue(parsedCert, CertFieldsTypes.dmPublicKey) + if (!onionAddress || !peerId || !username || !dmPublicKey) continue + allUsers.push({ onionAddress, peerId, username, dmPublicKey }) + } + return allUsers + } + public usernameCert(username: string): string | null { /** * Check if given username is already in use @@ -817,5 +1017,6 @@ export class StorageService extends EventEmitter { // @ts-ignore this.filesManager = null this.peerId = null + this.resetCsrReplicatedMapAndId() } } diff --git a/packages/backend/src/nest/storage/storage.types.ts b/packages/backend/src/nest/storage/storage.types.ts index 617b944333..84b3286b5d 100644 --- a/packages/backend/src/nest/storage/storage.types.ts +++ b/packages/backend/src/nest/storage/storage.types.ts @@ -4,6 +4,7 @@ export enum StorageEvents { // Peers UPDATE_PEERS_LIST = 'updatePeersList', LOAD_CERTIFICATES = 'loadCertificates', + REPLICATED_CSR = 'replicatedCsr', // Public Channels LOAD_PUBLIC_CHANNELS = 'loadPublicChannels', LOAD_ALL_PRIVATE_CONVERSATIONS = 'loadAllPrivateConversations', @@ -22,6 +23,8 @@ export enum StorageEvents { LOAD_ALL_DIRECT_MESSAGES = 'loadAllDirectMessages', // Misc SEND_PUSH_NOTIFICATION = 'sendPushNotification', + // Community + REPLICATED_COMMUNITY_METADATA = 'replicatedCommunityMetadata', } export interface InitStorageParams { communityId: string diff --git a/packages/backend/src/nest/tor/tor-control.service.ts b/packages/backend/src/nest/tor/tor-control.service.ts index 1c2e6f509b..5fa1426bea 100644 --- a/packages/backend/src/nest/tor/tor-control.service.ts +++ b/packages/backend/src/nest/tor/tor-control.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, OnModuleInit } from '@nestjs/common' +import { Inject, Injectable } from '@nestjs/common' import net from 'net' import { CONFIG_OPTIONS, TOR_CONTROL_PARAMS } from '../const' import { ConfigOptions } from '../types' @@ -6,7 +6,7 @@ import { TorControlAuthType, TorControlParams } from './tor.types' import Logger from '../common/logger' @Injectable() -export class TorControl implements OnModuleInit { +export class TorControl { connection: net.Socket | null authString: string private readonly logger = Logger(TorControl.name) @@ -15,7 +15,7 @@ export class TorControl implements OnModuleInit { @Inject(CONFIG_OPTIONS) public configOptions: ConfigOptions ) {} - onModuleInit() { + private updateAuthString() { if (this.torControlParams.auth.type === TorControlAuthType.PASSWORD) { this.authString = 'AUTHENTICATE "' + this.torControlParams.auth.value + '"\r\n' } @@ -46,11 +46,12 @@ export class TorControl implements OnModuleInit { reject(new Error(`TOR: Control port error: ${data.toString() as string}`)) } }) + this.updateAuthString() this.connection.write(this.authString) }) } - private async disconnect() { + private disconnect() { try { this.connection?.end() } catch (e) { @@ -62,11 +63,12 @@ export class TorControl implements OnModuleInit { // eslint-disable-next-line @typescript-eslint/ban-types private async _sendCommand(command: string, resolve: Function, reject: Function) { await this.connect() + const connectionTimeout = setTimeout(() => { reject('TOR: Send command timeout') }, 5000) this.connection?.on('data', async data => { - await this.disconnect() + this.disconnect() const dataArray = data.toString().split(/\r?\n/) if (dataArray[0].startsWith('250')) { resolve({ code: 250, messages: dataArray }) diff --git a/packages/backend/src/nest/websocketOverTor/websocketOverTor.tor.spec.ts b/packages/backend/src/nest/websocketOverTor/websocketOverTor.tor.spec.ts index 0ac6482471..f8fc150f63 100644 --- a/packages/backend/src/nest/websocketOverTor/websocketOverTor.tor.spec.ts +++ b/packages/backend/src/nest/websocketOverTor/websocketOverTor.tor.spec.ts @@ -4,13 +4,7 @@ import { multiaddr } from '@multiformats/multiaddr' import getPort from 'get-port' import { type DirResult } from 'tmp' import { jest, describe, it, expect, afterEach, beforeAll, afterAll } from '@jest/globals' -import { - createLibp2pAddress, - torBinForPlatform, - torDirForPlatform, - createTmpDir, - tmpQuietDirPath, -} from '../common/utils' +import { torBinForPlatform, torDirForPlatform, createTmpDir, tmpQuietDirPath } from '../common/utils' import { type CreateListenerOptions } from '@libp2p/interface-transport' import { createServer } from 'it-ws/server' import { createCertificatesTestHelper } from '../common/client-server' @@ -22,6 +16,7 @@ import { Tor } from '../tor/tor.service' import crypto from 'crypto' import { TorControl } from '../tor/tor-control.service' import { TorControlAuthType } from '../tor/tor.types' +import { createLibp2pAddress } from '@quiet/common' jest.setTimeout(120000) describe('websocketOverTor', () => { @@ -123,7 +118,8 @@ describe('websocketOverTor', () => { await listener?.close() }) - it.each([ + // Those are randomly failing and we do not use wss atm anyway. + it.skip.each([ ['string', String], ['array', Array], ])('connects successfully with CA passed as %s', async (_name: string, caType: (ca: string) => any) => { @@ -283,73 +279,4 @@ describe('websocketOverTor', () => { }) ).rejects.toBeTruthy() }) - - it.skip('rejects connection if server cert is invalid', async () => { - const pems = await createCertificatesTestHelper(`${service1.onionAddress}`, `${service2.onionAddress}`) - const anotherPems = await createCertificatesTestHelper(`${service1.onionAddress}`, `${service2.onionAddress}`) - - const prepareListenerArg: CreateListenerOptions = { - handler: x => x, - upgrader: { - // @ts-expect-error - upgradeOutbound, - // @ts-expect-error - upgradeInbound, - }, - } - - const signal: AbortSignal = { - ...abortSignalOpts, - addEventListener, - removeEventListener, - } - - const peerId1 = 'Qme5NiSQ6V3cc3nyfYVtkkXDPGBSYEVUNCN5sM4DbyYc7s' - const peerId2 = 'QmeCWxba5Yk1ZAKogQJsaHXoAermE7PgFZqpqyKNg65cSN' - - const websocketsOverTorData1 = { - filter: all, - websocket: { - agent, - cert: anotherPems.servCert, - key: anotherPems.servKey, - ca: [pems.ca], - }, - localAddress: createLibp2pAddress(service1.onionAddress, peerId1), - targetPort: port1Target, - createServer, - } - - const websocketsOverTorData2 = { - filter: all, - websocket: { - agent, - cert: pems.servCert, - key: pems.servKey, - ca: [pems.ca], - }, - localAddress: createLibp2pAddress(service2.onionAddress, peerId2), - targetPort: port2Target, - createServer, - } - - const multiAddress = multiaddr(createLibp2pAddress(service1.onionAddress, peerId1)) - - const ws1 = webSockets(websocketsOverTorData1)() - const ws2 = webSockets(websocketsOverTorData2)() - - listener = await ws1.prepareListener(prepareListenerArg) - - await listener.listen(multiAddress) - - const onConnection = jest.fn() - listener.on('connection', onConnection) - - await expect( - ws2.dial(multiAddress, { - signal, - upgrader: prepareListenerArg.upgrader, - }) - ).rejects.toBeTruthy() - }) }) diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 5fa0e70225..de505e8aa5 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -3,6 +3,96 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.0.2-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.4...@quiet/common@2.0.2-alpha.0) (2023-10-26) + +**Note:** Version bump only for package @quiet/common + + + + + +## [2.0.1-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.3...@quiet/common@2.0.1-alpha.4) (2023-10-12) + +**Note:** Version bump only for package @quiet/common + + + + + +## [2.0.1-alpha.3](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.2...@quiet/common@2.0.1-alpha.3) (2023-10-10) + +**Note:** Version bump only for package @quiet/common + + + + + +## [2.0.1-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.1...@quiet/common@2.0.1-alpha.2) (2023-10-09) + +**Note:** Version bump only for package @quiet/common + + + + + +## [2.0.1-alpha.1](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.0...@quiet/common@2.0.1-alpha.1) (2023-09-25) + +**Note:** Version bump only for package @quiet/common + + + + + +## [2.0.1-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.0-alpha.3...@quiet/common@2.0.1-alpha.0) (2023-09-25) + +**Note:** Version bump only for package @quiet/common + + + + + +# [2.0.0-alpha.18](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.0-alpha.3...@quiet/common@2.0.0-alpha.18) (2023-10-04) + +**Note:** Version bump only for package @quiet/common + + + + + +# [2.0.0-alpha.3](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.0-alpha.2...@quiet/common@2.0.0-alpha.3) (2023-09-19) + +**Note:** Version bump only for package @quiet/common + + + + + +# [2.0.0-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.8.1...@quiet/common@2.0.0-alpha.2) (2023-09-18) + + +### Bug Fixes + +* common: capitalize should return null if no string provided ([47269fd](https://github.com/TryQuiet/quiet/commit/47269fd48150c93cb6ede2bf833be05d5f893ab8)) +* typo ([62adb7e](https://github.com/TryQuiet/quiet/commit/62adb7e3a11cee2da9418f11a79d38c871fb684e)), closes [/github.com/TryQuiet/quiet/pull/1727#discussion_r1302471153](https://github.com//github.com/TryQuiet/quiet/pull/1727/issues/discussion_r1302471153) +* typo in function name ([7e00db1](https://github.com/TryQuiet/quiet/commit/7e00db1eb8d868cfe6682a1fa75f7e90b3a496bb)) + + + + + +# [2.0.0-alpha.1](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.0-alpha.0...@quiet/common@2.0.0-alpha.1) (2023-09-14) + + +### Bug Fixes + +* common: capitalize should return null if no string provided ([47269fd](https://github.com/TryQuiet/quiet/commit/47269fd48150c93cb6ede2bf833be05d5f893ab8)) +* typo ([62adb7e](https://github.com/TryQuiet/quiet/commit/62adb7e3a11cee2da9418f11a79d38c871fb684e)), closes [/github.com/TryQuiet/quiet/pull/1727#discussion_r1302471153](https://github.com//github.com/TryQuiet/quiet/pull/1727/issues/discussion_r1302471153) +* typo in function name ([7e00db1](https://github.com/TryQuiet/quiet/commit/7e00db1eb8d868cfe6682a1fa75f7e90b3a496bb)) + + + + + # [2.0.0-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.9.0-alpha.0...@quiet/common@2.0.0-alpha.0) (2023-09-01) **Note:** Version bump only for package @quiet/common @@ -12,6 +102,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.9.0-alpha.0](/compare/@quiet/common@1.8.0...@quiet/common@1.9.0-alpha.0) (2023-08-29) +## [1.8.1](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.8.0...@quiet/common@1.8.1) (2023-09-15) **Note:** Version bump only for package @quiet/common @@ -43,7 +134,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.6.0](https://github.com/ZbayApp/monorepo/compare/@quiet/common@1.5.1-alpha.0...@quiet/common@1.6.0) (2023-07-28) +# [1.6.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.5.1-alpha.0...@quiet/common@1.6.0) (2023-07-28) **Note:** Version bump only for package @quiet/common @@ -51,7 +142,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [1.5.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/@quiet/common@1.5.0-alpha.0...@quiet/common@1.5.1-alpha.0) (2023-07-28) +## [1.5.1-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.5.0-alpha.0...@quiet/common@1.5.1-alpha.0) (2023-07-28) **Note:** Version bump only for package @quiet/common @@ -59,7 +150,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.5.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/@quiet/common@1.4.0-alpha.1...@quiet/common@1.5.0-alpha.0) (2023-07-26) +# [1.5.0-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.4.0-alpha.1...@quiet/common@1.5.0-alpha.0) (2023-07-26) **Note:** Version bump only for package @quiet/common @@ -75,7 +166,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.4.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/@quiet/common@1.3.1...@quiet/common@1.4.0-alpha.0) (2023-06-19) +# [1.4.0-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.3.1...@quiet/common@1.4.0-alpha.0) (2023-06-19) **Note:** Version bump only for package @quiet/common @@ -171,7 +262,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.2.0-alpha.21](https://github.com/ZbayApp/monorepo/compare/@quiet/common@1.2.0-alpha.20...@quiet/common@1.2.0-alpha.21) (2023-04-07) +# [1.2.0-alpha.21](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.2.0-alpha.20...@quiet/common@1.2.0-alpha.21) (2023-04-07) **Note:** Version bump only for package @quiet/common diff --git a/packages/common/package-lock.json b/packages/common/package-lock.json index 425621773a..67dbf3e87e 100644 --- a/packages/common/package-lock.json +++ b/packages/common/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/common", - "version": "2.0.0-alpha.0", + "version": "2.0.2-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/common", - "version": "2.0.0-alpha.0", + "version": "2.0.2-alpha.0", "license": "ISC", "dependencies": { "cross-env": "^5.2.0", diff --git a/packages/common/package.json b/packages/common/package.json index 667c565984..1bd4e547ac 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,12 +1,13 @@ { "name": "@quiet/common", - "version": "2.0.0-alpha.0", + "version": "2.0.2-alpha.0", "description": "Common monorepo utils", "main": "lib/index.js", "types": "lib/index.d.ts", "files": [ "lib/**/*" ], + "private": true, "scripts": { "build": "tsc -p tsconfig.build.json", "prepare": "npm run build", @@ -17,7 +18,7 @@ "rmDist": "rimraf lib/" }, "devDependencies": { - "@quiet/eslint-config": "^1.4.0-alpha.0", + "@quiet/eslint-config": "^2.0.2-alpha.0", "@types/jest": "^26.0.23", "@types/node": "^17.0.21", "jest": "^26.6.3", @@ -25,7 +26,7 @@ "typescript": "^4.9.3" }, "dependencies": { - "@quiet/types": "^2.0.0-alpha.0", + "@quiet/types": "^2.0.2-alpha.0", "cross-env": "^5.2.0", "debug": "^4.3.1" }, diff --git a/packages/common/src/capitalize.test.ts b/packages/common/src/capitalize.test.ts index 96f7f19c03..dff9932699 100644 --- a/packages/common/src/capitalize.test.ts +++ b/packages/common/src/capitalize.test.ts @@ -6,10 +6,10 @@ describe('Capitalize first letter', () => { }) it("doesn't break if provided empty string", () => { - expect(capitalizeFirstLetter('')).toEqual(null) + expect(capitalizeFirstLetter('')).toEqual('') }) it("doesn't break if string is undefined", () => { - expect(capitalizeFirstLetter(undefined)).toEqual(null) + expect(capitalizeFirstLetter(undefined)).toEqual('') }) }) diff --git a/packages/common/src/capitalize.ts b/packages/common/src/capitalize.ts index 304d55b3fd..45ddb73aa7 100644 --- a/packages/common/src/capitalize.ts +++ b/packages/common/src/capitalize.ts @@ -1,5 +1,5 @@ export const capitalizeFirstLetter = (text: string): string => { - if (text?.length > 0) { + if (text) { return text.charAt(0).toUpperCase() + text.slice(1) } else { return '' diff --git a/packages/common/src/channelAddress.test.ts b/packages/common/src/channelAddress.test.ts new file mode 100644 index 0000000000..be47efdeb6 --- /dev/null +++ b/packages/common/src/channelAddress.test.ts @@ -0,0 +1,29 @@ +import { generateChannelId, getChannelNameFromChannelId } from './channelAddress' + +describe('Generate Channel Id', () => { + it('name "rockets" is the channel name', () => { + expect(generateChannelId('rockets')).toContain('rockets') + }) + + it('Should include hexadecimals characters in a determined structure (name + _ + 16 hex)', () => { + const channelName = 'rockets' + const randomBytesLength = 32 // 16 chars in hex + const underscoreLength = 1 + const expectedLength = channelName.length + underscoreLength + randomBytesLength + expect(generateChannelId('rockets')).toHaveLength(expectedLength) + }) +}) + +describe('Get Channel Name From Channel Id', () => { + it('Should return the channel name', () => { + const channelId = 'rockets_1faff74afc8daff3256275ce89d30528' + const channelName = 'rockets' + expect(getChannelNameFromChannelId(channelId)).toEqual(channelName) + }) + it('Should return the channel id if does not match the structure', () => { + const channelName = 'rockets' + const invalidChannelId = 'rockets+1faff74afc8daff3256275ce89d30528' + expect(getChannelNameFromChannelId(channelName)).toEqual(channelName) + expect(getChannelNameFromChannelId(invalidChannelId)).toEqual(invalidChannelId) + }) +}) diff --git a/packages/common/src/channelAddress.ts b/packages/common/src/channelAddress.ts index 1a61aad440..cc67c5a893 100644 --- a/packages/common/src/channelAddress.ts +++ b/packages/common/src/channelAddress.ts @@ -2,7 +2,7 @@ import crypto from 'crypto' export const generateChannelId = (channelName: string) => `${channelName}_${crypto.randomBytes(16).toString('hex')}` -export const getChannelNameFormChannelId = (channelId: string) => { +export const getChannelNameFromChannelId = (channelId: string) => { const index = channelId.indexOf('_') if (index === -1) { return channelId diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 914f45c769..67ce8a7b5b 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -7,3 +7,4 @@ export * from './sortPeers' export * from './channelAddress' export * from './naming' export * from './fileData' +export * from './libp2p' diff --git a/packages/common/src/invitationCode.test.ts b/packages/common/src/invitationCode.test.ts index 78bd1420c1..28254563bc 100644 --- a/packages/common/src/invitationCode.test.ts +++ b/packages/common/src/invitationCode.test.ts @@ -1,24 +1,75 @@ -import { argvInvitationCode, invitationDeepUrl, invitationShareUrl } from './invitationCode' -import { Site } from './static' +import { + argvInvitationCode, + invitationDeepUrl, + invitationShareUrl, + pairsToInvitationShareUrl, + retrieveInvitationCode, +} from './invitationCode' +import { QUIET_JOIN_PAGE } from './static' describe('Invitation code helper', () => { + const peerId1 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' + const address1 = 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad' + const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE' + const address2 = 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd' + it('retrieves invitation code from argv', () => { + const expectedCodes = [ + { peerId: peerId1, onionAddress: address1 }, + { peerId: peerId2, onionAddress: address2 }, + ] const result = argvInvitationCode([ 'something', 'quiet:/invalid', 'zbay://invalid', 'quiet://invalid', 'quiet://?param=invalid', - invitationDeepUrl('validCode'), + invitationDeepUrl(expectedCodes), ]) - expect(result).toBe('validCode') + expect(result).toEqual(expectedCodes) }) it('builds proper invitation deep url', () => { - expect(invitationDeepUrl('validCode')).toEqual('quiet://?code=validCode') + expect( + invitationDeepUrl([ + { peerId: 'peerID1', onionAddress: 'address1' }, + { peerId: 'peerID2', onionAddress: 'address2' }, + ]) + ).toEqual('quiet://?peerID1=address1&peerID2=address2') + }) + + it('creates invitation share url based on invitation pairs', () => { + const pairs = [ + { peerId: 'peerID1', onionAddress: 'address1' }, + { peerId: 'peerID2', onionAddress: 'address2' }, + ] + const expected = `${QUIET_JOIN_PAGE}#peerID1=address1&peerID2=address2` + expect(pairsToInvitationShareUrl(pairs)).toEqual(expected) }) it('builds proper invitation share url', () => { - expect(invitationShareUrl('validCode')).toEqual(`https://${Site.DOMAIN}/${Site.JOIN_PAGE}#validCode`) + const peerList = [ + '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/443/wss/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE', + 'invalidAddress', + '/dns4/somethingElse.onion/tcp/443/wss/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA', + ] + expect(invitationShareUrl(peerList)).toEqual( + `${QUIET_JOIN_PAGE}#QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE=gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad&QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA=somethingElse` + ) + }) + + it('retrieves invitation codes from deep url', () => { + const codes = retrieveInvitationCode(`quiet://?${peerId1}=${address1}&${peerId2}=${address2}`) + expect(codes).toEqual([ + { peerId: peerId1, onionAddress: address1 }, + { peerId: peerId2, onionAddress: address2 }, + ]) + }) + + it('retrieves invitation codes from deep url with partly invalid codes', () => { + const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLs' + const address2 = 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd' + const codes = retrieveInvitationCode(`quiet://?${peerId1}=${address1}&${peerId2}=${address2}}`) + expect(codes).toEqual([{ peerId: peerId1, onionAddress: address1 }]) }) }) diff --git a/packages/common/src/invitationCode.ts b/packages/common/src/invitationCode.ts index 6d17e38251..e5e6af26f7 100644 --- a/packages/common/src/invitationCode.ts +++ b/packages/common/src/invitationCode.ts @@ -1,46 +1,132 @@ -import { InvitationParams, Site } from './static' - -export const retrieveInvitationCode = (url: string): string => { +import { InvitationPair } from '@quiet/types' +import { ONION_ADDRESS_REGEX, Site } from './static' +import { createLibp2pAddress } from './libp2p' +export const retrieveInvitationCode = (url: string): InvitationPair[] => { /** - * Extract invitation code from deep url. - * Valid format: quiet://?code= + * Extract invitation codes from deep url. + * Valid format: quiet://?=&= */ let data: URL try { data = new URL(url) } catch (e) { - return '' + return [] } - if (!data || data.protocol !== 'quiet:') return '' - const code = data.searchParams.get(InvitationParams.CODE) - if (code) { - console.log('Retrieved code:', code) - return code + if (!data || data.protocol !== 'quiet:') return [] + const params = data.searchParams + const codes: InvitationPair[] = [] + for (const [peerId, onionAddress] of params.entries()) { + if (!invitationCodeValid(peerId, onionAddress)) continue + codes.push({ + peerId, + onionAddress, + }) } - return '' + console.log('Retrieved codes:', codes) + return codes } -export const argvInvitationCode = (argv: string[]): string => { +export const invitationShareUrl = (peers: string[] = []): string => { /** - * Extract invitation code from deep url if url is present in argv + * @arg {string[]} peers - List of peer's p2p addresses + * @returns {string} - Complete shareable invitation link, e.g. https://tryquiet.org/join/#=&= */ - let invitationCode = '' - for (const arg of argv) { - invitationCode = retrieveInvitationCode(arg) - if (invitationCode) { - break + console.log('Invitation share url, peers:', peers) + const pairs = [] + for (const peerAddress of peers) { + let peerId: string + let onionAddress: string + try { + peerId = peerAddress.split('/p2p/')[1] + } catch (e) { + console.info(`Could not add peer address '${peerAddress}' to invitation url. Reason: ${e.message}`) + continue + } + try { + onionAddress = peerAddress.split('/tcp/')[0].split('/dns4/')[1] + } catch (e) { + console.info(`Could not add peer address '${peerAddress}' to invitation url. Reason: ${e.message}`) + continue } + + if (!peerId || !onionAddress) { + console.error(`No peerId or address in ${peerAddress}`) + continue + } + const rawAddress = onionAddress.endsWith('.onion') ? onionAddress.split('.')[0] : onionAddress + pairs.push(`${peerId}=${rawAddress}`) + } + + console.log('invitationShareUrl', pairs.join('&')) + const url = new URL(`${Site.MAIN_PAGE}${Site.JOIN_PAGE}#${pairs.join('&')}`) + return url.href +} + +export const pairsToP2pAddresses = (pairs: InvitationPair[]): string[] => { + const addresses: string[] = [] + for (const pair of pairs) { + addresses.push(createLibp2pAddress(pair.onionAddress, pair.peerId)) + } + return addresses +} + +export const pairsToInvitationShareUrl = (pairs: InvitationPair[]) => { + const url = new URL(`${Site.MAIN_PAGE}${Site.JOIN_PAGE}`) + for (const pair of pairs) { + url.searchParams.append(pair.peerId, pair.onionAddress) } - return invitationCode + return url.href.replace('?', '#') } -export const invitationDeepUrl = (code = ''): string => { +export const invitationDeepUrl = (pairs: InvitationPair[] = []): string => { const url = new URL('quiet://') - url.searchParams.append(InvitationParams.CODE, code) + for (const pair of pairs) { + url.searchParams.append(pair.peerId, pair.onionAddress) + } return url.href } -export const invitationShareUrl = (code = ''): string => { - const url = new URL(`https://${Site.DOMAIN}/${Site.JOIN_PAGE}#${code}`) - return url.href +export const argvInvitationCode = (argv: string[]): InvitationPair[] => { + /** + * Extract invitation codes from deep url if url is present in argv + */ + let invitationCodes: InvitationPair[] = [] + for (const arg of argv) { + invitationCodes = retrieveInvitationCode(arg) + if (invitationCodes.length > 0) { + break + } + } + return invitationCodes +} + +export const invitationCodeValid = (peerId: string, onionAddress: string): boolean => { + if (!peerId.match(/^[a-zA-Z0-9]{46}$/g)) { + // TODO: test it more properly e.g with PeerId.createFromB58String(peerId.trim()) + console.log(`PeerId ${peerId} is not valid`) + return false + } + if (!onionAddress.trim().match(ONION_ADDRESS_REGEX)) { + console.log(`Onion address ${onionAddress} is not valid`) + return false + } + return true +} + +export const getInvitationPairs = (code: string) => { + /** + * @param code =&= + */ + const pairs = code.split('&') + const codes: InvitationPair[] = [] + for (const pair of pairs) { + const [peerId, address] = pair.split('=') + if (!peerId || !address) continue + if (!invitationCodeValid(peerId, address)) continue + codes.push({ + peerId: peerId, + onionAddress: address, + }) + } + return codes } diff --git a/packages/backend/src/nest/libp2p/libp2p.utils.ts b/packages/common/src/libp2p.ts similarity index 74% rename from packages/backend/src/nest/libp2p/libp2p.utils.ts rename to packages/common/src/libp2p.ts index 447ffa9267..e86ee508e2 100644 --- a/packages/backend/src/nest/libp2p/libp2p.utils.ts +++ b/packages/common/src/libp2p.ts @@ -2,10 +2,10 @@ const ONION = '.onion' export const createLibp2pAddress = (address: string, peerId: string) => { if (!address.endsWith(ONION)) address += ONION - return `/dns4/${address}/tcp/80/wss/p2p/${peerId}` + return `/dns4/${address}/tcp/80/ws/p2p/${peerId}` } export const createLibp2pListenAddress = (address: string) => { if (!address.endsWith(ONION)) address += ONION - return `/dns4/${address}/tcp/80/wss` + return `/dns4/${address}/tcp/80/ws` } diff --git a/packages/common/src/static.ts b/packages/common/src/static.ts index 057018d0a7..05b1f35f0a 100644 --- a/packages/common/src/static.ts +++ b/packages/common/src/static.ts @@ -1,11 +1,9 @@ export const ONION_ADDRESS_REGEX = /^[a-z0-9]{56}$/g -export enum InvitationParams { - CODE = 'code', -} - export enum Site { DOMAIN = 'tryquiet.org', MAIN_PAGE = 'https://tryquiet.org/', JOIN_PAGE = 'join', } + +export const QUIET_JOIN_PAGE = `${Site.MAIN_PAGE}${Site.JOIN_PAGE}` diff --git a/packages/common/tsconfig.build.json b/packages/common/tsconfig.build.json index 4c2fe1bc29..b889234c8b 100644 --- a/packages/common/tsconfig.build.json +++ b/packages/common/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { + "target": "ES2020", "rootDir": "./src", "outDir": "./lib", "typeRoots": [ diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index afd02831cc..6adb0ef8de 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "target": "es2017", + "target": "ES2020", "outDir": "./lib", "typeRoots": [ "../@types", diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 6a8411f56a..7508693170 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -3,6 +3,218 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.0.3-alpha.3](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.3-alpha.2...@quiet/desktop@2.0.3-alpha.3) (2023-11-13) + + +### Features + +* add debug logs ([#2057](https://github.com/TryQuiet/quiet/issues/2057)) ([aa3e777](https://github.com/TryQuiet/quiet/commit/aa3e777778b0861d5f96e6116bfc70031ed67929)) + + + + + +## [2.0.3-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.3-alpha.1...@quiet/desktop@2.0.3-alpha.2) (2023-11-09) + + +### Bug Fixes + +* trigger desktop ([2898bee](https://github.com/TryQuiet/quiet/commit/2898bee80bbf2f16cbda67281a29e47716faa77c)) + + + + + +## [2.0.3-alpha.1](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.3-alpha.0...@quiet/desktop@2.0.3-alpha.1) (2023-11-08) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## [2.0.3-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.2-alpha.0...@quiet/desktop@2.0.3-alpha.0) (2023-10-26) + + +### Features + +* version code 366 ([cebf886](https://github.com/TryQuiet/quiet/commit/cebf886aee2c4ec6e0ef4e66219d9dc0afecb98c)) + + + + + +## [2.0.2-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.7...@quiet/desktop@2.0.2-alpha.0) (2023-10-26) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## [2.0.1-alpha.7](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.6...@quiet/desktop@2.0.1-alpha.7) (2023-10-16) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## [2.0.1-alpha.6](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.5...@quiet/desktop@2.0.1-alpha.6) (2023-10-13) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## [2.0.1-alpha.5](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.4...@quiet/desktop@2.0.1-alpha.5) (2023-10-12) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## [2.0.1-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.3...@quiet/desktop@2.0.1-alpha.4) (2023-10-11) + + +### Reverts + +* Revert "Adjust project bootstrap scripts to be windows-friendly (#1870)" (#1937) ([0811ea3](https://github.com/TryQuiet/quiet/commit/0811ea3ea3f682dd763be72b12f626fe416bc036)), closes [#1870](https://github.com/TryQuiet/quiet/issues/1870) [#1937](https://github.com/TryQuiet/quiet/issues/1937) [#1870](https://github.com/TryQuiet/quiet/issues/1870) + + + + + +## [2.0.1-alpha.3](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.1-alpha.2...@quiet/desktop@2.0.1-alpha.3) (2023-10-10) + +**Note:** Version bump only for package @quiet/desktop + + + + + +## 2.0.1-alpha.2 (2023-10-09) + + +### Bug Fixes + +* add conditional checksum path ([8164b50](https://github.com/TryQuiet/quiet/commit/8164b50774f32856d9886f75d1bb25788f1e03c4)) + + +### Reverts + +* Revert "Remove afterAllArtifactBuild for linux" ([975d0df](https://github.com/TryQuiet/quiet/commit/975d0df58494bdfba1270f6845152af4969e77ea)) + + + + + +## [2.0.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@2.0.1-alpha.0...quiet@2.0.1-alpha.1) (2023-09-25) + +**Note:** Version bump only for package quiet + + + + + +## [2.0.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.12...quiet@2.0.1-alpha.0) (2023-09-25) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.12](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.11...quiet@2.0.0-alpha.12) (2023-09-20) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.11](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.10...quiet@2.0.0-alpha.11) (2023-09-19) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.10](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.9...quiet@2.0.0-alpha.10) (2023-09-19) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.9](https://github.com/TryQuiet/quiet/compare/quiet@1.9.1...quiet@2.0.0-alpha.9) (2023-09-18) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.8](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.7...quiet@2.0.0-alpha.8) (2023-09-14) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.7](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.6...quiet@2.0.0-alpha.7) (2023-09-07) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.6](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.5...quiet@2.0.0-alpha.6) (2023-09-07) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.5](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.4...quiet@2.0.0-alpha.5) (2023-09-06) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.4](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.3...quiet@2.0.0-alpha.4) (2023-09-06) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.3](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.2...quiet@2.0.0-alpha.3) (2023-09-05) + +**Note:** Version bump only for package quiet + + + + + +# [2.0.0-alpha.2](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.1...quiet@2.0.0-alpha.2) (2023-09-05) + +**Note:** Version bump only for package quiet + + + + + # [2.0.0-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@2.0.0-alpha.0...quiet@2.0.0-alpha.1) (2023-09-01) **Note:** Version bump only for package quiet @@ -20,6 +232,39 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.10.0-alpha.0](/compare/quiet@1.9.0...quiet@1.10.0-alpha.0) (2023-08-29) +## [1.9.5](https://github.com/TryQuiet/quiet/compare/quiet@1.9.4...quiet@1.9.5) (2023-10-12) + +**Note:** Version bump only for package quiet + + + + + +## [1.9.4](https://github.com/TryQuiet/quiet/compare/quiet@1.9.3...quiet@1.9.4) (2023-10-03) + +**Note:** Version bump only for package quiet + + + + + +## [1.9.3](https://github.com/TryQuiet/quiet/compare/quiet@1.9.2...quiet@1.9.3) (2023-10-03) + +**Note:** Version bump only for package quiet + + + + + +## [1.9.2](https://github.com/TryQuiet/quiet/compare/quiet@1.9.1...quiet@1.9.2) (2023-09-25) + +**Note:** Version bump only for package quiet + + + + + +## [1.9.1](https://github.com/TryQuiet/quiet/compare/quiet@1.9.0...quiet@1.9.1) (2023-09-15) **Note:** Version bump only for package quiet @@ -83,7 +328,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.7.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.6.1-alpha.0...quiet@1.7.0) (2023-07-28) +# [1.7.0](https://github.com/TryQuiet/quiet/compare/quiet@1.6.1-alpha.0...quiet@1.7.0) (2023-07-28) **Note:** Version bump only for package quiet @@ -91,7 +336,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [1.6.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.6.0-alpha.0...quiet@1.6.1-alpha.0) (2023-07-28) +## [1.6.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@1.6.0-alpha.0...quiet@1.6.1-alpha.0) (2023-07-28) **Note:** Version bump only for package quiet @@ -99,7 +344,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.6.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.5.0-alpha.0...quiet@1.6.0-alpha.0) (2023-07-26) +# [1.6.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@1.5.0-alpha.0...quiet@1.6.0-alpha.0) (2023-07-26) **Note:** Version bump only for package quiet @@ -107,7 +352,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.5.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.4.2-alpha.1...quiet@1.5.0-alpha.0) (2023-07-18) +# [1.5.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@1.4.2-alpha.1...quiet@1.5.0-alpha.0) (2023-07-18) **Note:** Version bump only for package quiet @@ -115,7 +360,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [1.4.2-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@1.4.2-alpha.0...quiet@1.4.2-alpha.1) (2023-07-17) +## [1.4.2-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@1.4.2-alpha.0...quiet@1.4.2-alpha.1) (2023-07-17) **Note:** Version bump only for package quiet @@ -123,7 +368,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [1.4.2-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.4.1-alpha.0...quiet@1.4.2-alpha.0) (2023-07-17) +## [1.4.2-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@1.4.1-alpha.0...quiet@1.4.2-alpha.0) (2023-07-17) **Note:** Version bump only for package quiet @@ -147,7 +392,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.4.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@1.3.1...quiet@1.4.0-alpha.0) (2023-06-19) +# [1.4.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@1.3.1...quiet@1.4.0-alpha.0) (2023-06-19) **Note:** Version bump only for package quiet @@ -315,7 +560,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.2.0-alpha.24](https://github.com/ZbayApp/monorepo/compare/quiet@1.2.0-alpha.23...quiet@1.2.0-alpha.24) (2023-04-07) +# [1.2.0-alpha.24](https://github.com/TryQuiet/quiet/compare/quiet@1.2.0-alpha.23...quiet@1.2.0-alpha.24) (2023-04-07) **Note:** Version bump only for package quiet @@ -623,7 +868,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [1.0.0-alpha.16](https://github.com/ZbayApp/monorepo/compare/quiet@1.0.0-alpha.15...quiet@1.0.0-alpha.16) (2023-02-20) +# [1.0.0-alpha.16](https://github.com/TryQuiet/quiet/compare/quiet@1.0.0-alpha.15...quiet@1.0.0-alpha.16) (2023-02-20) **Note:** Version bump only for package quiet @@ -986,7 +1231,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.19.0-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.18.7-alpha.1...quiet@0.19.0-alpha.1) (2023-01-27) +# [0.19.0-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.18.7-alpha.1...quiet@0.19.0-alpha.1) (2023-01-27) **Note:** Version bump only for package quiet @@ -1050,7 +1295,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.18.1-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.18.1-alpha.0...quiet@0.18.1-alpha.1) (2023-01-14) +## [0.18.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.18.1-alpha.0...quiet@0.18.1-alpha.1) (2023-01-14) **Note:** Version bump only for package quiet @@ -1074,7 +1319,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.18.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.17.0-alpha.4...quiet@0.18.0-alpha.0) (2023-01-13) +# [0.18.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.17.0-alpha.4...quiet@0.18.0-alpha.0) (2023-01-13) **Note:** Version bump only for package quiet @@ -1106,7 +1351,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.17.0-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.16.0...quiet@0.17.0-alpha.1) (2023-01-11) +# [0.17.0-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.16.0...quiet@0.17.0-alpha.1) (2023-01-11) **Note:** Version bump only for package quiet @@ -1114,7 +1359,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.17.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.16.0...quiet@0.17.0-alpha.0) (2023-01-11) +# [0.17.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.16.0...quiet@0.17.0-alpha.0) (2023-01-11) **Note:** Version bump only for package quiet @@ -1130,7 +1375,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.16.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.14.4...quiet@0.16.0-alpha.0) (2022-12-14) +# [0.16.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.14.4...quiet@0.16.0-alpha.0) (2022-12-14) **Note:** Version bump only for package quiet @@ -1138,7 +1383,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.15.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.14.4...quiet@0.15.0) (2022-12-14) +# [0.15.0](https://github.com/TryQuiet/quiet/compare/quiet@0.14.4...quiet@0.15.0) (2022-12-14) **Note:** Version bump only for package quiet @@ -1146,7 +1391,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.15.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.14.4...quiet@0.15.0-alpha.0) (2022-12-14) +# [0.15.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.14.4...quiet@0.15.0-alpha.0) (2022-12-14) **Note:** Version bump only for package quiet @@ -1154,7 +1399,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.14.5-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.14.4...quiet@0.14.5-alpha.0) (2022-12-09) +## [0.14.5-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.14.4...quiet@0.14.5-alpha.0) (2022-12-09) **Note:** Version bump only for package quiet @@ -1274,7 +1519,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.13](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.13) (2022-09-22) +## [0.11.3-alpha.13](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.13) (2022-09-22) **Note:** Version bump only for package quiet @@ -1282,7 +1527,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.12](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.12) (2022-09-21) +## [0.11.3-alpha.12](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.12) (2022-09-21) **Note:** Version bump only for package quiet @@ -1290,7 +1535,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.11](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.11) (2022-09-21) +## [0.11.3-alpha.11](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.11) (2022-09-21) **Note:** Version bump only for package quiet @@ -1298,7 +1543,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.10](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.10) (2022-09-21) +## [0.11.3-alpha.10](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.10) (2022-09-21) **Note:** Version bump only for package quiet @@ -1306,7 +1551,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.9](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.9) (2022-09-21) +## [0.11.3-alpha.9](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.9) (2022-09-21) **Note:** Version bump only for package quiet @@ -1314,7 +1559,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.8](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.8) (2022-09-21) +## [0.11.3-alpha.8](https://github.com/TryQuiet/quiet/compare/quiet@0.11.3-alpha.7...quiet@0.11.3-alpha.8) (2022-09-21) **Note:** Version bump only for package quiet @@ -1322,7 +1567,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.7](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.7) (2022-09-21) +## [0.11.3-alpha.7](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.7) (2022-09-21) **Note:** Version bump only for package quiet @@ -1330,7 +1575,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.6](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.6) (2022-09-20) +## [0.11.3-alpha.6](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.6) (2022-09-20) **Note:** Version bump only for package quiet @@ -1338,7 +1583,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.5](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.5) (2022-09-17) +## [0.11.3-alpha.5](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.5) (2022-09-17) **Note:** Version bump only for package quiet @@ -1346,7 +1591,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.4](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.4) (2022-09-16) +## [0.11.3-alpha.4](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.4) (2022-09-16) **Note:** Version bump only for package quiet @@ -1354,7 +1599,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.3](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.3) (2022-09-16) +## [0.11.3-alpha.3](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.3) (2022-09-16) **Note:** Version bump only for package quiet @@ -1362,7 +1607,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.2](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.2) (2022-09-16) +## [0.11.3-alpha.2](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.2) (2022-09-16) **Note:** Version bump only for package quiet @@ -1370,7 +1615,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.1) (2022-09-16) +## [0.11.3-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.1) (2022-09-16) **Note:** Version bump only for package quiet @@ -1378,7 +1623,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.11.3-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.11.2...quiet@0.11.3-alpha.0) (2022-09-16) +## [0.11.3-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.11.2...quiet@0.11.3-alpha.0) (2022-09-16) **Note:** Version bump only for package quiet @@ -1450,7 +1695,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.10.2-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.10.1-alpha.1...quiet@0.10.2-alpha.0) (2022-08-26) +## [0.10.2-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.10.1-alpha.1...quiet@0.10.2-alpha.0) (2022-08-26) **Note:** Version bump only for package quiet @@ -1458,7 +1703,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.10.1-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.10.0...quiet@0.10.1-alpha.1) (2022-08-16) +## [0.10.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.10.0...quiet@0.10.1-alpha.1) (2022-08-16) **Note:** Version bump only for package quiet @@ -1466,7 +1711,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.10.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.10.0...quiet@0.10.1-alpha.0) (2022-08-11) +## [0.10.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.10.0...quiet@0.10.1-alpha.0) (2022-08-11) **Note:** Version bump only for package quiet @@ -1474,7 +1719,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.10.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.7-alpha.7...quiet@0.10.0) (2022-08-10) +# [0.10.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.7-alpha.7...quiet@0.10.0) (2022-08-10) **Note:** Version bump only for package quiet @@ -1490,7 +1735,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.7-alpha.6](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.7-alpha.5...quiet@0.9.7-alpha.6) (2022-08-04) +## [0.9.7-alpha.6](https://github.com/TryQuiet/quiet/compare/quiet@0.9.7-alpha.5...quiet@0.9.7-alpha.6) (2022-08-04) **Note:** Version bump only for package quiet @@ -1498,7 +1743,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.7-alpha.5](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.7-alpha.3...quiet@0.9.7-alpha.5) (2022-08-04) +## [0.9.7-alpha.5](https://github.com/TryQuiet/quiet/compare/quiet@0.9.7-alpha.3...quiet@0.9.7-alpha.5) (2022-08-04) **Note:** Version bump only for package quiet @@ -1506,7 +1751,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.7-alpha.4](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.7-alpha.3...quiet@0.9.7-alpha.4) (2022-08-04) +## [0.9.7-alpha.4](https://github.com/TryQuiet/quiet/compare/quiet@0.9.7-alpha.3...quiet@0.9.7-alpha.4) (2022-08-04) **Note:** Version bump only for package quiet @@ -1538,7 +1783,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.7-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.6...quiet@0.9.7-alpha.0) (2022-07-15) +## [0.9.7-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.8.6...quiet@0.9.7-alpha.0) (2022-07-15) **Note:** Version bump only for package quiet @@ -1546,7 +1791,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.6-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.5-alpha.1...quiet@0.9.6-alpha.0) (2022-07-15) +## [0.9.6-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.5-alpha.1...quiet@0.9.6-alpha.0) (2022-07-15) **Note:** Version bump only for package quiet @@ -1554,7 +1799,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.5-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.5-alpha.0...quiet@0.9.5-alpha.1) (2022-07-15) +## [0.9.5-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.9.5-alpha.0...quiet@0.9.5-alpha.1) (2022-07-15) **Note:** Version bump only for package quiet @@ -1562,7 +1807,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.5-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.4-alpha.0...quiet@0.9.5-alpha.0) (2022-07-07) +## [0.9.5-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.4-alpha.0...quiet@0.9.5-alpha.0) (2022-07-07) **Note:** Version bump only for package quiet @@ -1570,7 +1815,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.4-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.3-alpha.0...quiet@0.9.4-alpha.0) (2022-07-06) +## [0.9.4-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.3-alpha.0...quiet@0.9.4-alpha.0) (2022-07-06) **Note:** Version bump only for package quiet @@ -1578,7 +1823,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.3-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.2-alpha.0...quiet@0.9.3-alpha.0) (2022-07-06) +## [0.9.3-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.2-alpha.0...quiet@0.9.3-alpha.0) (2022-07-06) **Note:** Version bump only for package quiet @@ -1586,7 +1831,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.2-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.1-alpha.0...quiet@0.9.2-alpha.0) (2022-07-05) +## [0.9.2-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.1-alpha.0...quiet@0.9.2-alpha.0) (2022-07-05) **Note:** Version bump only for package quiet @@ -1594,7 +1839,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.9.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.9.0-alpha.0...quiet@0.9.1-alpha.0) (2022-07-05) +## [0.9.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.9.0-alpha.0...quiet@0.9.1-alpha.0) (2022-07-05) **Note:** Version bump only for package quiet @@ -1602,7 +1847,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.9.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.6...quiet@0.9.0-alpha.0) (2022-07-05) +# [0.9.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.8.6...quiet@0.9.0-alpha.0) (2022-07-05) **Note:** Version bump only for package quiet @@ -1645,7 +1890,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.8.3-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.2-alpha.0...quiet@0.8.3-alpha.0) (2022-06-21) +## [0.8.3-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.8.2-alpha.0...quiet@0.8.3-alpha.0) (2022-06-21) **Note:** Version bump only for package quiet @@ -1653,7 +1898,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.8.2-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.1-alpha.1...quiet@0.8.2-alpha.0) (2022-06-21) +## [0.8.2-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.8.1-alpha.1...quiet@0.8.2-alpha.0) (2022-06-21) **Note:** Version bump only for package quiet @@ -1661,7 +1906,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.8.1-alpha.2](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.1-alpha.1...quiet@0.8.1-alpha.2) (2022-06-21) +## [0.8.1-alpha.2](https://github.com/TryQuiet/quiet/compare/quiet@0.8.1-alpha.1...quiet@0.8.1-alpha.2) (2022-06-21) **Note:** Version bump only for package quiet @@ -1669,7 +1914,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.8.1-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.1-alpha.0...quiet@0.8.1-alpha.1) (2022-06-16) +## [0.8.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.8.1-alpha.0...quiet@0.8.1-alpha.1) (2022-06-16) **Note:** Version bump only for package quiet @@ -1677,7 +1922,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.8.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.8.0-alpha.0...quiet@0.8.1-alpha.0) (2022-06-14) +## [0.8.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.8.0-alpha.0...quiet@0.8.1-alpha.0) (2022-06-14) **Note:** Version bump only for package quiet @@ -1685,7 +1930,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.8.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.7.1-alpha.2...quiet@0.8.0-alpha.0) (2022-06-14) +# [0.8.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.7.1-alpha.2...quiet@0.8.0-alpha.0) (2022-06-14) **Note:** Version bump only for package quiet @@ -1693,7 +1938,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.7.1-alpha.2](https://github.com/ZbayApp/monorepo/compare/quiet@0.7.1-alpha.1...quiet@0.7.1-alpha.2) (2022-06-03) +## [0.7.1-alpha.2](https://github.com/TryQuiet/quiet/compare/quiet@0.7.1-alpha.1...quiet@0.7.1-alpha.2) (2022-06-03) **Note:** Version bump only for package quiet @@ -1701,7 +1946,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.7.1-alpha.1](https://github.com/ZbayApp/monorepo/compare/quiet@0.7.1-alpha.0...quiet@0.7.1-alpha.1) (2022-06-03) +## [0.7.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@0.7.1-alpha.0...quiet@0.7.1-alpha.1) (2022-06-03) **Note:** Version bump only for package quiet @@ -1709,7 +1954,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [0.7.1-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.7.0-alpha.0...quiet@0.7.1-alpha.0) (2022-06-03) +## [0.7.1-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.7.0-alpha.0...quiet@0.7.1-alpha.0) (2022-06-03) **Note:** Version bump only for package quiet @@ -1717,7 +1962,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [0.7.0-alpha.0](https://github.com/ZbayApp/monorepo/compare/quiet@0.6.1-alpha.0...quiet@0.7.0-alpha.0) (2022-06-03) +# [0.7.0-alpha.0](https://github.com/TryQuiet/quiet/compare/quiet@0.6.1-alpha.0...quiet@0.7.0-alpha.0) (2022-06-03) **Note:** Version bump only for package quiet diff --git a/packages/desktop/README.md b/packages/desktop/README.md index 5dbf233bcf..2ad572c2fc 100644 --- a/packages/desktop/README.md +++ b/packages/desktop/README.md @@ -1,8 +1,9 @@ # Quiet Desktop -Running the desktop version of Quiet should be straightforward on Mac, Windows, and Linux. Here are the steps: +Running the desktop version of Quiet should be straightforward on Mac and Linux. On Windows we recommend using git-bash or just wsl. +Here are the steps: -0. Use `Node 18.12.1` and `npm 8.19.2`. We recommend [nvm](https://github.com/nvm-sh/nvm) for easily switching Node versions, and if this README gets out of date you can see the actual version used by CI [here](https://github.com/TryQuiet/quiet/blob/master/.github/actions/setup-env/action.yml). +0. Use `Node 18.12.1` and `npm 8.19.2`. We recommend [nvm](https://github.com/nvm-sh/nvm) for easily switching Node versions, and if this README gets out of date you can see the actual version used by CI [here](https://github.com/TryQuiet/quiet/blob/master/.github/actions/setup-env/action.yml). If you are using nvm, you can run `nvm use` in the project's root to switch to the correct version. 1. In `quiet/` (project's root) install monorepo's dependencies and bootstrap the project with lerna. It will take care of the package's dependencies and trigger a prepublish script which builds them. ``` @@ -22,7 +23,7 @@ npm run start ## Versioning packages -Before trying to release a new version, make sure you have GH_TOKEN env set. +Before trying to release a new version, make sure you have `GH_TOKEN` env set. The project uses independent versioning which means each package has its own version number. Only those packages in which something has changed since the last release will be bumped. @@ -60,7 +61,7 @@ npm run lerna add luxon packages/state-manager ---- -Lerna takes care of all the packages. You can execute scripts is every pakcage by simpy running: +Lerna takes care of all the packages. You can execute scripts is every package by simply running: ``` npm run lerna run