Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
5c100e7bca chore(deps): bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 12:19:19 +00:00
294 changed files with 2073 additions and 6528 deletions

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: analytics
workspace_name: "@standardnotes/analytics"
deploy_web: false
package_path: packages/analytics
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: api-gateway
workspace_name: "@standardnotes/api-gateway"
deploy_worker: false
package_path: packages/api-gateway
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: auth
workspace_name: "@standardnotes/auth-server"
package_path: packages/auth
secrets: inherit

View File

@@ -9,9 +9,6 @@ on:
package_path:
required: true
type: string
workspace_name:
required: true
type: string
secrets:
DOCKER_USERNAME:
required: true
@@ -33,14 +30,6 @@ jobs:
with:
python-version: '3.11'
- name: Cache build
id: cache-build
uses: actions/cache@v3
with:
path: |
packages/**/dist
key: ${{ runner.os }}-${{ inputs.service_name }}-build-${{ github.sha }}
- name: Set up Node
uses: actions/setup-node@v3
with:
@@ -50,10 +39,6 @@ jobs:
- name: Install dependencies
run: yarn install --immutable
- name: Build
if: steps.cache-build.outputs.cache-hit != 'true'
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
@@ -81,7 +66,7 @@ jobs:
uses: docker/setup-buildx-action@master
- name: Publish Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .

View File

@@ -7,10 +7,6 @@ on:
type: string
default: latest
description: The Docker image tag used for SNJS container
suite:
type: string
default: all
description: The test suite to run
secrets:
DOCKER_USERNAME:
required: true
@@ -22,16 +18,153 @@ on:
required: true
jobs:
e2e-self-hosted:
uses: standardnotes/server/.github/workflows/e2e-self-hosted.yml@main
with:
snjs_image_tag: ${{ inputs.snjs_image_tag }}
suite: ${{ inputs.suite }}
secrets: inherit
e2e:
name: (Self Hosting) E2E Test Suite
strategy:
fail-fast: false
matrix:
secondary_db_enabled: [true, false]
transition_mode_enabled: [true, false]
runs-on: ubuntu-latest
services:
snjs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Install
run: yarn install --immutable
- name: Run Server
run: docker compose -f docker-compose.ci.yml up -d
env:
DB_TYPE: mysql
CACHE_TYPE: redis
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
TRANSITION_MODE_ENABLED: ${{ matrix.transition_mode_enabled }}
- name: Wait for server to start
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
- name: Show logs on failure
if: ${{ failure() }}
run: |
echo "# Errors:"
tail -n 100 logs/*.err
echo "# Logs:"
tail -n 100 logs/*.log
e2e-home-server:
uses: standardnotes/server/.github/workflows/e2e-home-server.yml@main
with:
snjs_image_tag: ${{ inputs.snjs_image_tag }}
suite: ${{ inputs.suite }}
secrets: inherit
name: (Home Server) E2E Test Suite
strategy:
fail-fast: false
matrix:
db_type: [mysql, sqlite]
cache_type: [redis, memory]
secondary_db_enabled: [true, false]
transition_mode_enabled: [true, false]
runs-on: ubuntu-latest
services:
snjs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
cache:
image: redis
ports:
- 6379:6379
db:
image: mysql
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: standardnotes
MYSQL_USER: standardnotes
MYSQL_PASSWORD: standardnotes
secondary_db:
image: mongo:5.0
ports:
- 27017:27017
env:
MONGO_INITDB_ROOT_USERNAME: standardnotes
MONGO_INITDB_ROOT_PASSWORD: standardnotes
MONGO_INITDB_DATABASE: standardnotes
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Install Dependencies
run: yarn install --immutable
- name: Build
run: yarn build
- name: Copy dotenv file
run: cp packages/home-server/.env.sample packages/home-server/.env
- name: Fill in env variables
run: |
sed -i "s/JWT_SECRET=/JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/AUTH_JWT_SECRET=/AUTH_JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/ENCRYPTION_SERVER_KEY=/ENCRYPTION_SERVER_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/VALET_TOKEN_SECRET=/VALET_TOKEN_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
echo "DB_HOST=localhost" >> packages/home-server/.env
echo "DB_PORT=3306" >> packages/home-server/.env
echo "DB_DATABASE=standardnotes" >> packages/home-server/.env
echo "DB_SQLITE_DATABASE_PATH=homeserver.db" >> packages/home-server/.env
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
echo "DB_DEBUG_LEVEL=all" >> packages/home-server/.env
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
echo "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
echo "TRANSITION_MODE_ENABLED=${{ matrix.transition_mode_enabled }}" >> packages/home-server/.env
echo "MONGO_HOST=localhost" >> packages/home-server/.env
echo "MONGO_PORT=27017" >> packages/home-server/.env
echo "MONGO_DATABASE=standardnotes" >> packages/home-server/.env
echo "MONGO_USERNAME=standardnotes" >> packages/home-server/.env
echo "MONGO_PASSWORD=standardnotes" >> packages/home-server/.env
echo "FILES_SERVER_URL=http://localhost:3123" >> packages/home-server/.env
echo "E2E_TESTING=true" >> packages/home-server/.env
- name: Run Server
run: nohup yarn workspace @standardnotes/home-server start > logs/output.log 2>&1 &
env:
PORT: 3123
- name: Wait for server to start
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
- name: Show logs on failure
if: ${{ failure() }}
run: tail -n 500 logs/output.log

View File

@@ -35,7 +35,7 @@ jobs:
uses: docker/setup-buildx-action@master
- name: Publish Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .

View File

@@ -6,9 +6,6 @@ on:
service_name:
required: true
type: string
workspace_name:
required: true
type: string
deploy_web:
required: false
default: true
@@ -39,7 +36,6 @@ jobs:
with:
service_name: ${{ inputs.service_name }}
package_path: ${{ inputs.package_path }}
workspace_name: ${{ inputs.workspace_name }}
secrets: inherit
deploy-web:

View File

@@ -1,128 +0,0 @@
name: E2E Home Server Test Suite
on:
workflow_call:
inputs:
snjs_image_tag:
type: string
default: latest
description: The Docker image tag used for SNJS container
suite:
type: string
default: all
description: The test suite to run
secrets:
DOCKER_USERNAME:
required: true
DOCKER_PASSWORD:
required: true
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
jobs:
e2e-home-server:
name: (Home Server) E2E Test Suite
strategy:
fail-fast: false
matrix:
db_type: [mysql, sqlite]
cache_type: [redis, memory]
secondary_db_enabled: [true, false]
runs-on: ubuntu-latest
services:
snjs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
cache:
image: redis
ports:
- 6379:6379
db:
image: mysql
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: standardnotes
MYSQL_USER: standardnotes
MYSQL_PASSWORD: standardnotes
secondary_db:
image: mongo:5.0
ports:
- 27017:27017
env:
MONGO_INITDB_ROOT_USERNAME: standardnotes
MONGO_INITDB_ROOT_PASSWORD: standardnotes
MONGO_INITDB_DATABASE: standardnotes
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Install Dependencies
run: yarn install --immutable
- name: Build
run: yarn build
- name: Copy dotenv file
run: cp packages/home-server/.env.sample packages/home-server/.env
- name: Fill in env variables
run: |
sed -i "s/JWT_SECRET=/JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/AUTH_JWT_SECRET=/AUTH_JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/ENCRYPTION_SERVER_KEY=/ENCRYPTION_SERVER_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/VALET_TOKEN_SECRET=/VALET_TOKEN_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
echo "DB_HOST=localhost" >> packages/home-server/.env
echo "DB_PORT=3306" >> packages/home-server/.env
echo "DB_DATABASE=standardnotes" >> packages/home-server/.env
echo "DB_SQLITE_DATABASE_PATH=homeserver.db" >> packages/home-server/.env
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
echo "DB_DEBUG_LEVEL=all" >> packages/home-server/.env
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
echo "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
echo "MONGO_HOST=localhost" >> packages/home-server/.env
echo "MONGO_PORT=27017" >> packages/home-server/.env
echo "MONGO_DATABASE=standardnotes" >> packages/home-server/.env
echo "MONGO_USERNAME=standardnotes" >> packages/home-server/.env
echo "MONGO_PASSWORD=standardnotes" >> packages/home-server/.env
echo "FILES_SERVER_URL=http://localhost:3123" >> packages/home-server/.env
echo "E2E_TESTING=true" >> packages/home-server/.env
- name: Run Server
run: nohup yarn workspace @standardnotes/home-server start > logs/output.log 2>&1 &
env:
PORT: 3123
- name: Wait for server to start
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
- name: Archive failed run logs
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: home-server-failure-logs-${{ inputs.suite }}-${{ matrix.db_type }}-${{ matrix.cache_type }}-${{ matrix.secondary_db_enabled }}
retention-days: 5
path: |
logs/output.log

View File

@@ -1,72 +0,0 @@
name: E2E Self Hosted Test Suite
on:
workflow_call:
inputs:
snjs_image_tag:
type: string
default: latest
description: The Docker image tag used for SNJS container
suite:
type: string
default: all
description: The test suite to run
secrets:
DOCKER_USERNAME:
required: true
DOCKER_PASSWORD:
required: true
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
jobs:
e2e:
name: (Self Hosting) E2E Test Suite
strategy:
fail-fast: false
matrix:
secondary_db_enabled: [true, false]
runs-on: ubuntu-latest
services:
snjs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Install
run: yarn install --immutable
- name: Run Server
run: docker compose -f docker-compose.ci.yml up -d
env:
DB_TYPE: mysql
CACHE_TYPE: redis
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
- name: Wait for server to start
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
- name: Archive failed run logs
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: self-hosted-failure-logs-${{ inputs.suite }}-${{ matrix.secondary_db_enabled }}
retention-days: 5
path: |
logs/*.err
logs/*.log

View File

@@ -1,6 +1,6 @@
name: E2E Test Suite
name: E2E Test Suite On Self Hosted Server
run-name: E2E ${{ inputs.suite }} Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
run-name: E2E Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
on:
schedule:
@@ -11,10 +11,6 @@ on:
type: string
default: latest
description: The Docker image tag used for SNJS container
suite:
type: string
default: all
description: The test suite to run
author:
type: string
default: unknown
@@ -30,5 +26,4 @@ jobs:
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
with:
snjs_image_tag: ${{ inputs.snjs_image_tag || 'latest' }}
suite: ${{ inputs.suite || 'all' }}
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: files
workspace_name: "@standardnotes/files-server"
package_path: packages/files
secrets: inherit

View File

@@ -95,20 +95,9 @@ jobs:
- name: Test
run: yarn test
e2e-base:
e2e:
needs: build
name: E2E Base Suite
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
with:
snjs_image_tag: 'latest'
suite: 'base'
secrets: inherit
e2e-vaults:
needs: build
name: E2E Vaults Suite
name: E2E
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
with:
snjs_image_tag: 'latest'
suite: 'vaults'
secrets: inherit

View File

@@ -95,32 +95,20 @@ jobs:
- name: Test
run: yarn test
e2e-base:
e2e:
needs: build
name: E2E Base Suite
name: E2E
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
with:
snjs_image_tag: 'latest'
suite: 'base'
secrets: inherit
e2e-vaults:
needs: build
name: E2E Vaults Suite
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
with:
snjs_image_tag: 'latest'
suite: 'vaults'
secrets: inherit
publish-self-hosting:
needs: [ test, lint, e2e-base, e2e-vaults ]
needs: [ test, lint, e2e ]
name: Publish Self Hosting Docker Image
uses: standardnotes/server/.github/workflows/common-self-hosting.yml@main
secrets: inherit
publish-services:
needs: [ test, lint, e2e-base, e2e-vaults ]
needs: [ test, lint, e2e ]
runs-on: ubuntu-latest

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: revisions
workspace_name: "@standardnotes/revisions-server"
package_path: packages/revisions
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: scheduler
workspace_name: "@standardnotes/scheduler-server"
deploy_web: false
package_path: packages/scheduler
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: syncing-server-js
workspace_name: "@standardnotes/syncing-server"
package_path: packages/syncing-server
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: websockets
workspace_name: "@standardnotes/websockets-server"
package_path: packages/websockets
secrets: inherit

327
.pnp.cjs generated
View File

@@ -1605,13 +1605,6 @@ const RAW_RUNTIME_STATE =
["@aws-sdk/service-error-classification", "npm:3.342.0"]\
],\
"linkType": "HARD"\
}],\
["npm:3.370.0", {\
"packageLocation": "./.yarn/cache/@aws-sdk-service-error-classification-npm-3.370.0-0d5b615252-500f067ba1.zip/node_modules/@aws-sdk/service-error-classification/",\
"packageDependencies": [\
["@aws-sdk/service-error-classification", "npm:3.370.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["@aws-sdk/shared-ini-file-loader", [\
@@ -1795,15 +1788,6 @@ const RAW_RUNTIME_STATE =
["tslib", "npm:2.5.2"]\
],\
"linkType": "HARD"\
}],\
["npm:3.418.0", {\
"packageLocation": "./.yarn/cache/@aws-sdk-types-npm-3.418.0-451c0cadd0-627955c2c9.zip/node_modules/@aws-sdk/types/",\
"packageDependencies": [\
["@aws-sdk/types", "npm:3.418.0"],\
["@smithy/types", "npm:2.3.4"],\
["tslib", "npm:2.5.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["@aws-sdk/url-parser", [\
@@ -5504,14 +5488,6 @@ const RAW_RUNTIME_STATE =
["tslib", "npm:2.5.2"]\
],\
"linkType": "HARD"\
}],\
["npm:2.3.4", {\
"packageLocation": "./.yarn/cache/@smithy-types-npm-2.3.4-7d0b3a2a2f-8a5ad3b47e.zip/node_modules/@smithy/types/",\
"packageDependencies": [\
["@smithy/types", "npm:2.3.4"],\
["tslib", "npm:2.5.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["@smithy/url-parser", [\
@@ -5859,7 +5835,6 @@ const RAW_RUNTIME_STATE =
["@types/uuid", "npm:9.0.3"],\
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
["aws-xray-sdk", "npm:3.5.2"],\
["axios", "npm:1.4.0"],\
["bcryptjs", "npm:2.4.3"],\
["cors", "npm:2.8.5"],\
@@ -5963,7 +5938,6 @@ const RAW_RUNTIME_STATE =
["@types/newrelic", "npm:9.14.0"],\
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
["aws-xray-sdk", "npm:3.5.2"],\
["eslint", "npm:8.41.0"],\
["eslint-plugin-prettier", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.0.0"],\
["ioredis", "npm:5.3.2"],\
@@ -6182,7 +6156,6 @@ const RAW_RUNTIME_STATE =
["@types/cors", "npm:2.8.13"],\
["@types/dotenv", "npm:8.2.0"],\
["@types/express", "npm:4.17.17"],\
["@types/ioredis", "npm:5.0.0"],\
["@types/jest", "npm:29.5.2"],\
["@types/newrelic", "npm:9.14.0"],\
["@types/node", "npm:20.5.7"],\
@@ -6195,7 +6168,6 @@ const RAW_RUNTIME_STATE =
["express", "npm:4.18.2"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.3.2"],\
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
["mysql2", "npm:3.3.3"],\
@@ -6368,7 +6340,6 @@ const RAW_RUNTIME_STATE =
["@types/cors", "npm:2.8.13"],\
["@types/dotenv", "npm:8.2.0"],\
["@types/express", "npm:4.17.17"],\
["@types/ioredis", "npm:5.0.0"],\
["@types/jest", "npm:29.5.2"],\
["@types/jsonwebtoken", "npm:9.0.2"],\
["@types/newrelic", "npm:9.14.0"],\
@@ -6388,7 +6359,6 @@ const RAW_RUNTIME_STATE =
["helmet", "npm:7.0.0"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.3.2"],\
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
["jsonwebtoken", "npm:9.0.0"],\
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
@@ -6635,16 +6605,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@types/cls-hooked", [\
["npm:4.3.6", {\
"packageLocation": "./.yarn/cache/@types-cls-hooked-npm-4.3.6-8787b64e86-f5b9864348.zip/node_modules/@types/cls-hooked/",\
"packageDependencies": [\
["@types/cls-hooked", "npm:4.3.6"],\
["@types/node", "npm:20.2.5"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/concat-stream", [\
["npm:1.6.1", {\
"packageLocation": "./.yarn/cache/@types-concat-stream-npm-1.6.1-42cd06b019-7d211e7433.zip/node_modules/@types/concat-stream/",\
@@ -6904,16 +6864,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@types/mysql", [\
["npm:2.15.22", {\
"packageLocation": "./.yarn/cache/@types-mysql-npm-2.15.22-d643eb999e-6be0aac58f.zip/node_modules/@types/mysql/",\
"packageDependencies": [\
["@types/mysql", "npm:2.15.22"],\
["@types/node", "npm:20.2.5"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/newrelic", [\
["npm:9.14.0", {\
"packageLocation": "./.yarn/cache/@types-newrelic-npm-9.14.0-4668da51a1-3a54ea75a4.zip/node_modules/@types/newrelic/",\
@@ -6982,18 +6932,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@types/pg", [\
["npm:8.10.3", {\
"packageLocation": "./.yarn/cache/@types-pg-npm-8.10.3-3fc3365c7b-22d4836bd9.zip/node_modules/@types/pg/",\
"packageDependencies": [\
["@types/pg", "npm:8.10.3"],\
["@types/node", "npm:20.2.5"],\
["pg-protocol", "npm:1.6.0"],\
["pg-types", "npm:4.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/prettier", [\
["npm:2.7.3", {\
"packageLocation": "./.yarn/cache/@types-prettier-npm-2.7.3-497316f37c-cda84c19ac.zip/node_modules/@types/prettier/",\
@@ -8004,16 +7942,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["async-hook-jl", [\
["npm:1.7.6", {\
"packageLocation": "./.yarn/cache/async-hook-jl-npm-1.7.6-9999815029-f61a3bd4c3.zip/node_modules/async-hook-jl/",\
"packageDependencies": [\
["async-hook-jl", "npm:1.7.6"],\
["stack-chain", "npm:1.3.7"]\
],\
"linkType": "HARD"\
}]\
]],\
["asynckit", [\
["npm:0.4.0", {\
"packageLocation": "./.yarn/cache/asynckit-npm-0.4.0-c718858525-3ce727cbc7.zip/node_modules/asynckit/",\
@@ -8023,112 +7951,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["atomic-batcher", [\
["npm:1.0.2", {\
"packageLocation": "./.yarn/cache/atomic-batcher-npm-1.0.2-6fcd3a3097-025e334f1f.zip/node_modules/atomic-batcher/",\
"packageDependencies": [\
["atomic-batcher", "npm:1.0.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["aws-xray-sdk", [\
["npm:3.5.2", {\
"packageLocation": "./.yarn/cache/aws-xray-sdk-npm-3.5.2-15fc4e54ee-576d0d5ccb.zip/node_modules/aws-xray-sdk/",\
"packageDependencies": [\
["aws-xray-sdk", "npm:3.5.2"],\
["aws-xray-sdk-core", "npm:3.5.2"],\
["aws-xray-sdk-express", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"],\
["aws-xray-sdk-mysql", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"],\
["aws-xray-sdk-postgres", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["aws-xray-sdk-core", [\
["npm:3.5.2", {\
"packageLocation": "./.yarn/cache/aws-xray-sdk-core-npm-3.5.2-9083a0c00f-a643998187.zip/node_modules/aws-xray-sdk-core/",\
"packageDependencies": [\
["aws-xray-sdk-core", "npm:3.5.2"],\
["@aws-sdk/service-error-classification", "npm:3.370.0"],\
["@aws-sdk/types", "npm:3.418.0"],\
["@types/cls-hooked", "npm:4.3.6"],\
["atomic-batcher", "npm:1.0.2"],\
["cls-hooked", "npm:4.2.2"],\
["semver", "npm:7.5.4"]\
],\
"linkType": "HARD"\
}]\
]],\
["aws-xray-sdk-express", [\
["npm:3.5.2", {\
"packageLocation": "./.yarn/cache/aws-xray-sdk-express-npm-3.5.2-c4574a664b-62a07d0f3b.zip/node_modules/aws-xray-sdk-express/",\
"packageDependencies": [\
["aws-xray-sdk-express", "npm:3.5.2"]\
],\
"linkType": "SOFT"\
}],\
["virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2", {\
"packageLocation": "./.yarn/__virtual__/aws-xray-sdk-express-virtual-36027c3d91/0/cache/aws-xray-sdk-express-npm-3.5.2-c4574a664b-62a07d0f3b.zip/node_modules/aws-xray-sdk-express/",\
"packageDependencies": [\
["aws-xray-sdk-express", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"],\
["@types/aws-xray-sdk-core", null],\
["@types/express", "npm:4.17.17"],\
["aws-xray-sdk-core", "npm:3.5.2"]\
],\
"packagePeers": [\
"@types/aws-xray-sdk-core",\
"aws-xray-sdk-core"\
],\
"linkType": "HARD"\
}]\
]],\
["aws-xray-sdk-mysql", [\
["npm:3.5.2", {\
"packageLocation": "./.yarn/cache/aws-xray-sdk-mysql-npm-3.5.2-095483ab95-f910a96630.zip/node_modules/aws-xray-sdk-mysql/",\
"packageDependencies": [\
["aws-xray-sdk-mysql", "npm:3.5.2"]\
],\
"linkType": "SOFT"\
}],\
["virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2", {\
"packageLocation": "./.yarn/__virtual__/aws-xray-sdk-mysql-virtual-d8a5f29b75/0/cache/aws-xray-sdk-mysql-npm-3.5.2-095483ab95-f910a96630.zip/node_modules/aws-xray-sdk-mysql/",\
"packageDependencies": [\
["aws-xray-sdk-mysql", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"],\
["@types/aws-xray-sdk-core", null],\
["@types/mysql", "npm:2.15.22"],\
["aws-xray-sdk-core", "npm:3.5.2"]\
],\
"packagePeers": [\
"@types/aws-xray-sdk-core",\
"aws-xray-sdk-core"\
],\
"linkType": "HARD"\
}]\
]],\
["aws-xray-sdk-postgres", [\
["npm:3.5.2", {\
"packageLocation": "./.yarn/cache/aws-xray-sdk-postgres-npm-3.5.2-3a7e7bcc42-f2d6da22c7.zip/node_modules/aws-xray-sdk-postgres/",\
"packageDependencies": [\
["aws-xray-sdk-postgres", "npm:3.5.2"]\
],\
"linkType": "SOFT"\
}],\
["virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2", {\
"packageLocation": "./.yarn/__virtual__/aws-xray-sdk-postgres-virtual-c6edb063fc/0/cache/aws-xray-sdk-postgres-npm-3.5.2-3a7e7bcc42-f2d6da22c7.zip/node_modules/aws-xray-sdk-postgres/",\
"packageDependencies": [\
["aws-xray-sdk-postgres", "virtual:15fc4e54eec18d85ce3f22aa2405619072c35fbd500ad809cce2c9e4ead8a191fcc189cef6a5d76df3bea5576f09735fc4d32f086db561623afc56dd36794c8f#npm:3.5.2"],\
["@types/aws-xray-sdk-core", null],\
["@types/pg", "npm:8.10.3"],\
["aws-xray-sdk-core", "npm:3.5.2"]\
],\
"packagePeers": [\
"@types/aws-xray-sdk-core",\
"aws-xray-sdk-core"\
],\
"linkType": "HARD"\
}]\
]],\
["axios", [\
["npm:0.21.4", {\
"packageLocation": "./.yarn/cache/axios-npm-0.21.4-e278873748-da644592cb.zip/node_modules/axios/",\
@@ -8919,18 +8741,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["cls-hooked", [\
["npm:4.2.2", {\
"packageLocation": "./.yarn/cache/cls-hooked-npm-4.2.2-db33b9b95f-59081fcc0f.zip/node_modules/cls-hooked/",\
"packageDependencies": [\
["cls-hooked", "npm:4.2.2"],\
["async-hook-jl", "npm:1.7.6"],\
["emitter-listener", "npm:1.1.2"],\
["semver", "npm:5.7.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["cluster-key-slot", [\
["npm:1.1.2", {\
"packageLocation": "./.yarn/cache/cluster-key-slot-npm-1.1.2-0571a28825-516ed8b5e1.zip/node_modules/cluster-key-slot/",\
@@ -9821,16 +9631,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["emitter-listener", [\
["npm:1.1.2", {\
"packageLocation": "./.yarn/cache/emitter-listener-npm-1.1.2-0fe118d0b3-697f53c308.zip/node_modules/emitter-listener/",\
"packageDependencies": [\
["emitter-listener", "npm:1.1.2"],\
["shimmer", "npm:1.2.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["emittery", [\
["npm:0.13.1", {\
"packageLocation": "./.yarn/cache/emittery-npm-0.13.1-cb6cd1bb03-fbe214171d.zip/node_modules/emittery/",\
@@ -14203,15 +14003,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["obuf", [\
["npm:1.1.2", {\
"packageLocation": "./.yarn/cache/obuf-npm-1.1.2-8db5fae8dd-53ff4ab3a1.zip/node_modules/obuf/",\
"packageDependencies": [\
["obuf", "npm:1.1.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["on-finished", [\
["npm:2.4.1", {\
"packageLocation": "./.yarn/cache/on-finished-npm-2.4.1-907af70f88-8e81472c50.zip/node_modules/on-finished/",\
@@ -14672,49 +14463,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["pg-int8", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/pg-int8-npm-1.0.1-5cd67f3e22-a1e3a05a69.zip/node_modules/pg-int8/",\
"packageDependencies": [\
["pg-int8", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["pg-numeric", [\
["npm:1.0.2", {\
"packageLocation": "./.yarn/cache/pg-numeric-npm-1.0.2-9026ec3427-8899f8200c.zip/node_modules/pg-numeric/",\
"packageDependencies": [\
["pg-numeric", "npm:1.0.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["pg-protocol", [\
["npm:1.6.0", {\
"packageLocation": "./.yarn/cache/pg-protocol-npm-1.6.0-089a4b1d3c-995864cc2a.zip/node_modules/pg-protocol/",\
"packageDependencies": [\
["pg-protocol", "npm:1.6.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["pg-types", [\
["npm:4.0.1", {\
"packageLocation": "./.yarn/cache/pg-types-npm-4.0.1-8f922557d3-2c686ef361.zip/node_modules/pg-types/",\
"packageDependencies": [\
["pg-types", "npm:4.0.1"],\
["pg-int8", "npm:1.0.1"],\
["pg-numeric", "npm:1.0.2"],\
["postgres-array", "npm:3.0.2"],\
["postgres-bytea", "npm:3.0.0"],\
["postgres-date", "npm:2.0.1"],\
["postgres-interval", "npm:3.0.0"],\
["postgres-range", "npm:1.1.3"]\
],\
"linkType": "HARD"\
}]\
]],\
["picocolors", [\
["npm:1.0.0", {\
"packageLocation": "./.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip/node_modules/picocolors/",\
@@ -14786,52 +14534,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["postgres-array", [\
["npm:3.0.2", {\
"packageLocation": "./.yarn/cache/postgres-array-npm-3.0.2-da6a3f1fed-0159517e4e.zip/node_modules/postgres-array/",\
"packageDependencies": [\
["postgres-array", "npm:3.0.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["postgres-bytea", [\
["npm:3.0.0", {\
"packageLocation": "./.yarn/cache/postgres-bytea-npm-3.0.0-5de4c664f6-f5c01758fd.zip/node_modules/postgres-bytea/",\
"packageDependencies": [\
["postgres-bytea", "npm:3.0.0"],\
["obuf", "npm:1.1.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["postgres-date", [\
["npm:2.0.1", {\
"packageLocation": "./.yarn/cache/postgres-date-npm-2.0.1-00e0e0bc9e-908eacec35.zip/node_modules/postgres-date/",\
"packageDependencies": [\
["postgres-date", "npm:2.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["postgres-interval", [\
["npm:3.0.0", {\
"packageLocation": "./.yarn/cache/postgres-interval-npm-3.0.0-936c769b98-c7a1cf006d.zip/node_modules/postgres-interval/",\
"packageDependencies": [\
["postgres-interval", "npm:3.0.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["postgres-range", [\
["npm:1.1.3", {\
"packageLocation": "./.yarn/cache/postgres-range-npm-1.1.3-46f68e1a9e-356a46d97e.zip/node_modules/postgres-range/",\
"packageDependencies": [\
["postgres-range", "npm:1.1.3"]\
],\
"linkType": "HARD"\
}]\
]],\
["prelude-ls", [\
["npm:1.2.1", {\
"packageLocation": "./.yarn/cache/prelude-ls-npm-1.2.1-3e4d272a55-0b9d2c7680.zip/node_modules/prelude-ls/",\
@@ -15591,13 +15293,6 @@ const RAW_RUNTIME_STATE =
],\
"linkType": "HARD"\
}],\
["npm:5.7.2", {\
"packageLocation": "./.yarn/cache/semver-npm-5.7.2-938ee91eaa-fca14418a1.zip/node_modules/semver/",\
"packageDependencies": [\
["semver", "npm:5.7.2"]\
],\
"linkType": "HARD"\
}],\
["npm:6.3.0", {\
"packageLocation": "./.yarn/cache/semver-npm-6.3.0-b3eace8bfd-8dd72e7c7c.zip/node_modules/semver/",\
"packageDependencies": [\
@@ -15742,15 +15437,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["shimmer", [\
["npm:1.2.1", {\
"packageLocation": "./.yarn/cache/shimmer-npm-1.2.1-8b50bf3206-aa0d6252ad.zip/node_modules/shimmer/",\
"packageDependencies": [\
["shimmer", "npm:1.2.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["side-channel", [\
["npm:1.0.4", {\
"packageLocation": "./.yarn/cache/side-channel-npm-1.0.4-e1f38b9e06-c4998d9fc5.zip/node_modules/side-channel/",\
@@ -16075,15 +15761,6 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["stack-chain", [\
["npm:1.3.7", {\
"packageLocation": "./.yarn/cache/stack-chain-npm-1.3.7-c803ef2abb-6420637b76.zip/node_modules/stack-chain/",\
"packageDependencies": [\
["stack-chain", "npm:1.3.7"]\
],\
"linkType": "HARD"\
}]\
]],\
["stack-trace", [\
["npm:0.0.10", {\
"packageLocation": "./.yarn/cache/stack-trace-npm-0.0.10-9460b173e1-7bd633f0e9.zip/node_modules/stack-trace/",\
@@ -17095,7 +16772,7 @@ const RAW_RUNTIME_STATE =
["@types/better-sqlite3", null],\
["@types/google-cloud__spanner", null],\
["@types/hdb-pool", null],\
["@types/ioredis", "npm:5.0.0"],\
["@types/ioredis", null],\
["@types/mongodb", null],\
["@types/mssql", null],\
["@types/mysql2", null],\
@@ -17119,7 +16796,7 @@ const RAW_RUNTIME_STATE =
["dotenv", "npm:16.1.3"],\
["glob", "npm:8.1.0"],\
["hdb-pool", null],\
["ioredis", "npm:5.3.2"],\
["ioredis", null],\
["mkdirp", "npm:2.1.6"],\
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
["mssql", null],\

View File

@@ -24,6 +24,7 @@ services:
DB_TYPE: "${DB_TYPE}"
CACHE_TYPE: "${CACHE_TYPE}"
SECONDARY_DB_ENABLED: "${SECONDARY_DB_ENABLED}"
TRANSITION_MODE_ENABLED: "${TRANSITION_MODE_ENABLED}"
container_name: server-ci
ports:
- 3123:3000

View File

@@ -68,6 +68,9 @@ fi
if [ -z "$SECONDARY_DB_ENABLED" ]; then
export SECONDARY_DB_ENABLED=false
fi
if [ -z "$TRANSITION_MODE_ENABLED" ]; then
export TRANSITION_MODE_ENABLED=false
fi
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
#########

View File

@@ -139,11 +139,6 @@ LINKING_RESULT=$(link_queue_and_topic $AUTH_TOPIC_ARN $FILES_QUEUE_ARN)
echo "linking done:"
echo "$LINKING_RESULT"
echo "linking topic $SYNCING_SERVER_TOPIC_ARN to queue $FILES_QUEUE_ARN"
LINKING_RESULT=$(link_queue_and_topic $SYNCING_SERVER_TOPIC_ARN $FILES_QUEUE_ARN)
echo "linking done:"
echo "$LINKING_RESULT"
QUEUE_NAME="syncing-server-local-queue"
echo "creating queue $QUEUE_NAME"

View File

@@ -20,7 +20,7 @@
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
"postversion": "./scripts/push-tags-one-by-one.sh",
"e2e": "yarn build && PORT=3123 yarn workspace @standardnotes/home-server start",
"e2e": "yarn workspace @standardnotes/home-server run build && PORT=3123 yarn workspace @standardnotes/home-server start",
"start": "yarn workspace @standardnotes/home-server run build && yarn workspace @standardnotes/home-server start"
},
"devDependencies": {

View File

@@ -3,64 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.27.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.4...@standardnotes/analytics@2.27.5) (2023-10-04)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.3...@standardnotes/analytics@2.27.4) (2023-10-03)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.2...@standardnotes/analytics@2.27.3) (2023-10-03)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.1...@standardnotes/analytics@2.27.2) (2023-09-28)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.0...@standardnotes/analytics@2.27.1) (2023-09-27)
**Note:** Version bump only for package @standardnotes/analytics
# [2.27.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.24...@standardnotes/analytics@2.27.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [2.26.24](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.23...@standardnotes/analytics@2.26.24) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.23](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.22...@standardnotes/analytics@2.26.23) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.22](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.21...@standardnotes/analytics@2.26.22) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.20...@standardnotes/analytics@2.26.21) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.19...@standardnotes/analytics@2.26.20) (2023-09-21)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.19](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.18...@standardnotes/analytics@2.26.19) (2023-09-20)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.18](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.17...@standardnotes/analytics@2.26.18) (2023-09-20)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.16...@standardnotes/analytics@2.26.17) (2023-09-19)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.15...@standardnotes/analytics@2.26.16) (2023-09-18)
**Note:** Version bump only for package @standardnotes/analytics

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.27.5",
"version": "2.26.16",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -18,7 +18,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js",
"report": "yarn node dist/bin/report.js",
"setup:env": "cp .env.sample .env",

View File

@@ -3,64 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.75.10](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.9...@standardnotes/api-gateway@1.75.10) (2023-10-04)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.9](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.8...@standardnotes/api-gateway@1.75.9) (2023-10-03)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.8](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.7...@standardnotes/api-gateway@1.75.8) (2023-10-03)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.6...@standardnotes/api-gateway@1.75.7) (2023-09-28)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.5...@standardnotes/api-gateway@1.75.6) (2023-09-27)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.4...@standardnotes/api-gateway@1.75.5) (2023-09-26)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.3...@standardnotes/api-gateway@1.75.4) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.2...@standardnotes/api-gateway@1.75.3) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.1...@standardnotes/api-gateway@1.75.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.0...@standardnotes/api-gateway@1.75.1) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.75.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.17...@standardnotes/api-gateway@1.75.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/api-gateway/issues/841)) ([230c96d](https://github.com/standardnotes/api-gateway/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
## [1.74.17](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.16...@standardnotes/api-gateway@1.74.17) (2023-09-20)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.16](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.15...@standardnotes/api-gateway@1.74.16) (2023-09-20)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.15](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.14...@standardnotes/api-gateway@1.74.15) (2023-09-19)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.14](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.13...@standardnotes/api-gateway@1.74.14) (2023-09-18)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.75.10",
"version": "1.74.14",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@@ -42,19 +42,4 @@ export class SharedVaultUsersController extends BaseHttpController {
request.body,
)
}
@httpPost('/:userUuid/designate-survivor')
async designateSurvivor(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'POST',
'shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
request.params.sharedVaultUuid,
request.params.userUuid,
),
request.body,
)
}
}

View File

@@ -89,10 +89,6 @@ export class EndpointResolver implements EndpointResolverInterface {
// Shared Vault Users Controller
['[GET]:shared-vaults/:sharedVaultUuid/users', 'sync.shared-vault-users.get-users'],
['[DELETE]:shared-vaults/:sharedVaultUuid/users/:userUuid', 'sync.shared-vault-users.remove-user'],
[
'[POST]:shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
'sync.shared-vault-users.designate-survivor',
],
])
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {

View File

@@ -3,139 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.150.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.3...@standardnotes/auth-server@1.150.4) (2023-10-04)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.150.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.2...@standardnotes/auth-server@1.150.3) (2023-10-04)
### Bug Fixes
* **auth:** remove ec2 plugin from worker ([0281724](https://github.com/standardnotes/server/commit/02817241965db06cc02e9cca1d068446db0dee4e))
## [1.150.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.1...@standardnotes/auth-server@1.150.2) (2023-10-04)
### Bug Fixes
* **auth:** remove ec2 plugin ([373cfad](https://github.com/standardnotes/server/commit/373cfad1f7c26d3a0ec2a69e720bbce535170359))
## [1.150.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.0...@standardnotes/auth-server@1.150.1) (2023-10-03)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.150.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.149.1...@standardnotes/auth-server@1.150.0) (2023-10-03)
### Features
* add xray segment tracing on auth-worker ([b1b244a](https://github.com/standardnotes/server/commit/b1b244a2cf1e17ddf67fc9b238b4b25a1bc5a190))
## [1.149.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.149.0...@standardnotes/auth-server@1.149.1) (2023-10-02)
### Bug Fixes
* **auth:** enable xray only for server ([71e2a41](https://github.com/standardnotes/server/commit/71e2a4187e8f79b2a3055f502310ed0b6a6d82ea))
# [1.149.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.148.2...@standardnotes/auth-server@1.149.0) (2023-10-02)
### Bug Fixes
* **auth:** add aws xray ecs and ec2 plugins ([3838358](https://github.com/standardnotes/server/commit/383835808165aba58d15e79f03b5ba4e1c899f4c))
* **auth:** prevent from loop disabling of email settings ([#858](https://github.com/standardnotes/server/issues/858)) ([bd71422](https://github.com/standardnotes/server/commit/bd71422fabc4b5b47cca6dd071e3332943adc81d))
### Features
* **auth:** introduce AWS X-Ray SDK ([#859](https://github.com/standardnotes/server/issues/859)) ([1a388f0](https://github.com/standardnotes/server/commit/1a388f00c3897d1ebb1411793cfb23c3d305ac2e))
## [1.148.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.148.1...@standardnotes/auth-server@1.148.2) (2023-09-29)
### Bug Fixes
* **auth:** increase ttl for in progress duration of transitions ([681e037](https://github.com/standardnotes/server/commit/681e0378ae6b97c838b0b34ccc630194b304b81a))
## [1.148.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.148.0...@standardnotes/auth-server@1.148.1) (2023-09-29)
### Bug Fixes
* **auth:** disable transitions retriggering if they are in progress ([5ef6c5c](https://github.com/standardnotes/server/commit/5ef6c5c14a9f7a558de7ac9ff0ab99a5f831c127))
# [1.148.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.147.1...@standardnotes/auth-server@1.148.0) (2023-09-28)
### Features
* block file operations during transition ([#856](https://github.com/standardnotes/server/issues/856)) ([676cf36](https://github.com/standardnotes/server/commit/676cf36f8d769aa433880b2800f0457d06fbbf14))
## [1.147.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.147.0...@standardnotes/auth-server@1.147.1) (2023-09-27)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.147.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.4...@standardnotes/auth-server@1.147.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.146.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.3...@standardnotes/auth-server@1.146.4) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.146.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.2...@standardnotes/auth-server@1.146.3) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.146.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.1...@standardnotes/auth-server@1.146.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.146.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.0...@standardnotes/auth-server@1.146.1) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.146.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.145.0...@standardnotes/auth-server@1.146.0) (2023-09-22)
### Bug Fixes
* **auth:** register specs ([f9b1f40](https://github.com/standardnotes/server/commit/f9b1f40ddf2d733d106ea64b9a7c4b38c5ec43ce))
### Features
* remove transition mode from code ([5001496](https://github.com/standardnotes/server/commit/5001496c7bc1df9e20c2d88ebf52ed77f868110c))
# [1.145.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.144.0...@standardnotes/auth-server@1.145.0) (2023-09-22)
### Features
* remove user from all shared vaults upon account deletion ([#843](https://github.com/standardnotes/server/issues/843)) ([dc77ff3](https://github.com/standardnotes/server/commit/dc77ff3e45983d231bc9c132802428e77b4be431))
# [1.144.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.9...@standardnotes/auth-server@1.144.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
## [1.143.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.8...@standardnotes/auth-server@1.143.9) (2023-09-20)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.7...@standardnotes/auth-server@1.143.8) (2023-09-20)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.6...@standardnotes/auth-server@1.143.7) (2023-09-19)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.4...@standardnotes/auth-server@1.143.6) (2023-09-19)
### Bug Fixes
* **auth:** bump version ([b91cd7e](https://github.com/standardnotes/server/commit/b91cd7e2329d68026665f61d9bd17ba918c57563))
## [1.143.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.3...@standardnotes/auth-server@1.143.4) (2023-09-19)
### Bug Fixes
* **auth:** add debug logs to transition ([bf855bb](https://github.com/standardnotes/server/commit/bf855bb26e8a3618113bd2f5801c260782566815))
## [1.143.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.2...@standardnotes/auth-server@1.143.3) (2023-09-18)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -24,13 +24,11 @@ import { urlencoded, json, Request, Response, NextFunction } from 'express'
import * as winston from 'winston'
import * as dayjs from 'dayjs'
import * as utc from 'dayjs/plugin/utc'
import * as AWSXRay from 'aws-xray-sdk'
import { InversifyExpressServer } from 'inversify-express-utils'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { ServiceIdentifier } from '@standardnotes/domain-core'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
@@ -39,20 +37,9 @@ void container.load().then((container) => {
const env: Env = new Env()
env.load()
const isConfiguredForAWSProduction =
env.get('MODE', true) !== 'home-server' && env.get('MODE', true) !== 'self-hosted'
if (isConfiguredForAWSProduction) {
AWSXRay.config([AWSXRay.plugins.ECSPlugin])
}
const server = new InversifyExpressServer(container)
server.setConfig((app) => {
if (isConfiguredForAWSProduction) {
app.use(AWSXRay.express.openSegment(ServiceIdentifier.NAMES.Auth))
}
app.use((_request: Request, response: Response, next: NextFunction) => {
response.setHeader('X-Auth-Version', container.get(TYPES.Auth_VERSION))
next()
@@ -79,10 +66,6 @@ void container.load().then((container) => {
const serverInstance = server.build()
if (isConfiguredForAWSProduction) {
serverInstance.use(AWSXRay.express.closeSegment())
}
serverInstance.listen(env.get('PORT'))
logger.info(`Server started on port ${process.env.PORT}`)

View File

@@ -53,10 +53,7 @@ const requestTransition = async (
continue
}
let wasTransitionRequested = false
if (itemsTransitionStatus === null || itemsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
if (itemsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
await transitionStatusRepository.remove(user.uuid, 'items')
await domainEventPublisher.publish(
@@ -68,8 +65,7 @@ const requestTransition = async (
)
}
if (revisionsTransitionStatus === null || revisionsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
if (revisionsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
await transitionStatusRepository.remove(user.uuid, 'revisions')
await domainEventPublisher.publish(
@@ -82,12 +78,6 @@ const requestTransition = async (
}
usersTriggered += 1
if (wasTransitionRequested) {
logger.info(
`[TRANSITION ${timestamp}] Transition requested for user ${user.uuid} - items status: ${itemsTransitionStatus?.value}, revisions status: ${revisionsTransitionStatus?.value}, has transition role: ${userHasTransitionRole}`,
)
}
}
logger.info(

View File

@@ -8,7 +8,6 @@ import { Env } from '../src/Bootstrap/Env'
import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-events'
import * as dayjs from 'dayjs'
import * as utc from 'dayjs/plugin/utc'
import * as AWSXRay from 'aws-xray-sdk'
const container = new ContainerConfigLoader('worker')
void container.load().then((container) => {
@@ -17,14 +16,6 @@ void container.load().then((container) => {
const env: Env = new Env()
env.load()
const isConfiguredForAWSProduction =
env.get('MODE', true) !== 'home-server' && env.get('MODE', true) !== 'self-hosted'
if (isConfiguredForAWSProduction) {
AWSXRay.enableManualMode()
AWSXRay.config([AWSXRay.plugins.ECSPlugin])
}
const logger: Logger = container.get(TYPES.Auth_Logger)
logger.info('Starting worker...')

Binary file not shown.

View File

@@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695283870612 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695283870612'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'ALTER TABLE `auth_shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `auth_shared_vault_users` DROP COLUMN `is_designated_survivor`')
}
}

View File

@@ -1,43 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695283961201 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695283961201'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "temporary_auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "is_designated_survivor" boolean NOT NULL DEFAULT (0))',
)
await queryRunner.query(
'INSERT INTO "temporary_auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "auth_shared_vault_users"',
)
await queryRunner.query('DROP TABLE "auth_shared_vault_users"')
await queryRunner.query('ALTER TABLE "temporary_auth_shared_vault_users" RENAME TO "auth_shared_vault_users"')
await queryRunner.query(
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
)
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
await queryRunner.query('ALTER TABLE "auth_shared_vault_users" RENAME TO "temporary_auth_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
)
await queryRunner.query(
'INSERT INTO "auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_auth_shared_vault_users"',
)
await queryRunner.query('DROP TABLE "temporary_auth_shared_vault_users"')
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
)
await queryRunner.query(
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
)
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.150.4",
"version": "1.143.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -19,7 +19,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --fix --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"cleanup": "yarn node dist/bin/cleanup.js",
@@ -54,7 +54,6 @@
"@standardnotes/sncrypto-common": "^1.13.4",
"@standardnotes/sncrypto-node": "workspace:*",
"@standardnotes/time": "workspace:*",
"aws-xray-sdk": "^3.5.2",
"axios": "^1.1.3",
"bcryptjs": "2.4.3",
"cors": "2.8.5",

View File

@@ -1,6 +1,5 @@
import * as winston from 'winston'
import Redis from 'ioredis'
import { captureAWSv3Client } from 'aws-xray-sdk'
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
import { Container } from 'inversify'
@@ -93,7 +92,7 @@ import {
SNSDomainEventPublisher,
SQSDomainEventSubscriberFactory,
SQSEventMessageHandler,
SQSXRayEventMessageHandler,
SQSNewRelicEventMessageHandler,
} from '@standardnotes/domain-events-infra'
import { GetUserSubscription } from '../Domain/UseCase/GetUserSubscription/GetUserSubscription'
import { ChangeCredentials } from '../Domain/UseCase/ChangeCredentials/ChangeCredentials'
@@ -272,9 +271,6 @@ import { AddSharedVaultUser } from '../Domain/UseCase/AddSharedVaultUser/AddShar
import { RemoveSharedVaultUser } from '../Domain/UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedToSharedVaultEventHandler'
import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRemovedFromSharedVaultEventHandler'
import { DesignateSurvivor } from '../Domain/UseCase/DesignateSurvivor/DesignateSurvivor'
import { UserDesignatedAsSurvivorInSharedVaultEventHandler } from '../Domain/Handler/UserDesignatedAsSurvivorInSharedVaultEventHandler'
import { DisableEmailSettingBasedOnEmailSubscription } from '../Domain/UseCase/DisableEmailSettingBasedOnEmailSubscription/DisableEmailSettingBasedOnEmailSubscription'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -321,8 +317,6 @@ export class ContainerConfigLoader {
logger.debug('Database initialized')
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
const isConfiguredForAWSProduction = !isConfiguredForHomeServer && !isConfiguredForSelfHosting
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
if (!isConfiguredForInMemoryCache) {
@@ -353,11 +347,7 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
}
}
let snsClient = new SNSClient(snsConfig)
if (isConfiguredForAWSProduction) {
snsClient = captureAWSv3Client(snsClient)
}
container.bind<SNSClient>(TYPES.Auth_SNS).toConstantValue(snsClient)
container.bind<SNSClient>(TYPES.Auth_SNS).toConstantValue(new SNSClient(snsConfig))
const sqsConfig: SQSClientConfig = {
region: env.get('SQS_AWS_REGION', true),
@@ -371,17 +361,13 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
}
}
let sqsClient = new SQSClient(sqsConfig)
if (isConfiguredForAWSProduction) {
sqsClient = captureAWSv3Client(sqsClient)
}
container.bind<SQSClient>(TYPES.Auth_SQS).toConstantValue(sqsClient)
container.bind<SQSClient>(TYPES.Auth_SQS).toConstantValue(new SQSClient(sqsConfig))
}
// Mapping
container
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper)
.toConstantValue(new SessionTracePersistenceMapper(container.get<TimerInterface>(TYPES.Auth_Timer)))
.toConstantValue(new SessionTracePersistenceMapper())
container
.bind<MapperInterface<Authenticator, TypeORMAuthenticator>>(TYPES.Auth_AuthenticatorPersistenceMapper)
.toConstantValue(new AuthenticatorPersistenceMapper())
@@ -470,9 +456,8 @@ export class ContainerConfigLoader {
.bind<SessionTraceRepositoryInterface>(TYPES.Auth_SessionTraceRepository)
.toConstantValue(
new TypeORMSessionTraceRepository(
container.get<Repository<TypeORMSessionTrace>>(TYPES.Auth_ORMSessionTraceRepository),
container.get<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper),
container.get<TimerInterface>(TYPES.Auth_Timer),
container.get(TYPES.Auth_ORMSessionTraceRepository),
container.get(TYPES.Auth_SessionTracePersistenceMapper),
),
)
container
@@ -510,6 +495,20 @@ export class ContainerConfigLoader {
),
)
// Middleware
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
container.bind<LockMiddleware>(TYPES.Auth_LockMiddleware).to(LockMiddleware)
container
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.Auth_RequiredCrossServiceTokenMiddleware)
.to(RequiredCrossServiceTokenMiddleware)
container
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.Auth_OptionalCrossServiceTokenMiddleware)
.to(OptionalCrossServiceTokenMiddleware)
container
.bind<ApiGatewayOfflineAuthMiddleware>(TYPES.Auth_ApiGatewayOfflineAuthMiddleware)
.to(ApiGatewayOfflineAuthMiddleware)
container.bind<OfflineUserAuthMiddleware>(TYPES.Auth_OfflineUserAuthMiddleware).to(OfflineUserAuthMiddleware)
// Projectors
container.bind<SessionProjector>(TYPES.Auth_SessionProjector).to(SessionProjector)
container.bind<UserProjector>(TYPES.Auth_UserProjector).to(UserProjector)
@@ -594,6 +593,9 @@ export class ContainerConfigLoader {
container
.bind(TYPES.Auth_READONLY_USERS)
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
container
.bind(TYPES.Auth_TRANSITION_MODE_ENABLED)
.toConstantValue(env.get('TRANSITION_MODE_ENABLED', true) === 'true')
if (isConfiguredForInMemoryCache) {
container
@@ -738,32 +740,6 @@ export class ContainerConfigLoader {
: new SNSDomainEventPublisher(container.get(TYPES.Auth_SNS), container.get(TYPES.Auth_SNS_TOPIC_ARN)),
)
// Middleware
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
container.bind<LockMiddleware>(TYPES.Auth_LockMiddleware).to(LockMiddleware)
container
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.Auth_RequiredCrossServiceTokenMiddleware)
.toConstantValue(
new RequiredCrossServiceTokenMiddleware(
container.get<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.Auth_CrossServiceTokenDecoder),
isConfiguredForAWSProduction && this.mode === 'server',
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.Auth_OptionalCrossServiceTokenMiddleware)
.toConstantValue(
new OptionalCrossServiceTokenMiddleware(
container.get<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.Auth_CrossServiceTokenDecoder),
isConfiguredForAWSProduction && this.mode === 'server',
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<ApiGatewayOfflineAuthMiddleware>(TYPES.Auth_ApiGatewayOfflineAuthMiddleware)
.to(ApiGatewayOfflineAuthMiddleware)
container.bind<OfflineUserAuthMiddleware>(TYPES.Auth_OfflineUserAuthMiddleware).to(OfflineUserAuthMiddleware)
// use cases
container
.bind<TraceSession>(TYPES.Auth_TraceSession)
@@ -981,23 +957,6 @@ export class ContainerConfigLoader {
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
),
)
container
.bind<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor)
.toConstantValue(
new DesignateSurvivor(
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
container.get<TimerInterface>(TYPES.Auth_Timer),
),
)
container
.bind<DisableEmailSettingBasedOnEmailSubscription>(TYPES.Auth_DisableEmailSettingBasedOnEmailSubscription)
.toConstantValue(
new DisableEmailSettingBasedOnEmailSubscription(
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<SettingRepositoryInterface>(TYPES.Auth_SettingRepository),
container.get<SettingFactoryInterface>(TYPES.Auth_SettingFactory),
),
)
// Controller
container
@@ -1039,15 +998,7 @@ export class ContainerConfigLoader {
container.bind<UserRegisteredEventHandler>(TYPES.Auth_UserRegisteredEventHandler).to(UserRegisteredEventHandler)
container
.bind<AccountDeletionRequestedEventHandler>(TYPES.Auth_AccountDeletionRequestedEventHandler)
.toConstantValue(
new AccountDeletionRequestedEventHandler(
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
container.get<RevokedSessionRepositoryInterface>(TYPES.Auth_RevokedSessionRepository),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
.to(AccountDeletionRequestedEventHandler)
container
.bind<SubscriptionPurchasedEventHandler>(TYPES.Auth_SubscriptionPurchasedEventHandler)
.to(SubscriptionPurchasedEventHandler)
@@ -1135,10 +1086,8 @@ export class ContainerConfigLoader {
.bind<EmailSubscriptionUnsubscribedEventHandler>(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)
.toConstantValue(
new EmailSubscriptionUnsubscribedEventHandler(
container.get<DisableEmailSettingBasedOnEmailSubscription>(
TYPES.Auth_DisableEmailSettingBasedOnEmailSubscription,
),
container.get<winston.Logger>(TYPES.Auth_Logger),
container.get(TYPES.Auth_UserRepository),
container.get(TYPES.Auth_SettingService),
),
)
container
@@ -1173,16 +1122,6 @@ export class ContainerConfigLoader {
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<UserDesignatedAsSurvivorInSharedVaultEventHandler>(
TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler,
)
.toConstantValue(
new UserDesignatedAsSurvivorInSharedVaultEventHandler(
container.get<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
@@ -1217,10 +1156,6 @@ export class ContainerConfigLoader {
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
['USER_ADDED_TO_SHARED_VAULT', container.get(TYPES.Auth_UserAddedToSharedVaultEventHandler)],
['USER_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)],
[
'USER_DESIGNATED_AS_SURVIVOR_IN_SHARED_VAULT',
container.get(TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler),
],
])
if (isConfiguredForHomeServer) {
@@ -1236,8 +1171,8 @@ export class ContainerConfigLoader {
container
.bind<DomainEventMessageHandlerInterface>(TYPES.Auth_DomainEventMessageHandler)
.toConstantValue(
isConfiguredForAWSProduction
? new SQSXRayEventMessageHandler(eventHandlers, container.get(TYPES.Auth_Logger))
env.get('NEW_RELIC_ENABLED', true) === 'true'
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Auth_Logger))
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Auth_Logger)),
)

View File

@@ -105,6 +105,7 @@ const TYPES = {
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
Auth_TRANSITION_MODE_ENABLED: Symbol.for('Auth_TRANSITION_MODE_ENABLED'),
// use cases
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
@@ -160,8 +161,6 @@ const TYPES = {
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
Auth_DesignateSurvivor: Symbol.for('Auth_DesignateSurvivor'),
Auth_DisableEmailSettingBasedOnEmailSubscription: Symbol.for('Auth_DisableEmailSettingBasedOnEmailSubscription'),
// Handlers
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
@@ -193,9 +192,6 @@ const TYPES = {
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler: Symbol.for(
'Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler',
),
// Services
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
Auth_SessionService: Symbol.for('Auth_SessionService'),

View File

@@ -0,0 +1,114 @@
import 'reflect-metadata'
import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { EphemeralSession } from '../Session/EphemeralSession'
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
import { RevokedSession } from '../Session/RevokedSession'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { Session } from '../Session/Session'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { User } from '../User/User'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
describe('AccountDeletionRequestedEventHandler', () => {
let userRepository: UserRepositoryInterface
let sessionRepository: SessionRepositoryInterface
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
let revokedSessionRepository: RevokedSessionRepositoryInterface
let logger: Logger
let session: Session
let ephemeralSession: EphemeralSession
let revokedSession: RevokedSession
let user: User
let event: AccountDeletionRequestedEvent
const createHandler = () =>
new AccountDeletionRequestedEventHandler(
userRepository,
sessionRepository,
ephemeralSessionRepository,
revokedSessionRepository,
logger,
)
beforeEach(() => {
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
userRepository.remove = jest.fn()
session = {
uuid: '1-2-3',
} as jest.Mocked<Session>
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
sessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([session])
sessionRepository.remove = jest.fn()
ephemeralSession = {
uuid: '2-3-4',
userUuid: '00000000-0000-0000-0000-000000000000',
} as jest.Mocked<EphemeralSession>
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
ephemeralSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([ephemeralSession])
ephemeralSessionRepository.deleteOne = jest.fn()
revokedSession = {
uuid: '3-4-5',
} as jest.Mocked<RevokedSession>
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
revokedSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([revokedSession])
revokedSessionRepository.remove = jest.fn()
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
event.createdAt = new Date(1)
event.payload = {
userUuid: '00000000-0000-0000-0000-000000000000',
userCreatedAtTimestamp: 1,
regularSubscriptionUuid: '2-3-4',
roleNames: ['CORE_USER'],
}
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.warn = jest.fn()
})
it('should remove a user', async () => {
await createHandler().handle(event)
expect(userRepository.remove).toHaveBeenCalledWith(user)
})
it('should not remove a user with invalid uuid', async () => {
event.payload.userUuid = 'invalid'
await createHandler().handle(event)
expect(userRepository.remove).not.toHaveBeenCalled()
})
it('should not remove a user if one does not exist', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(userRepository.remove).not.toHaveBeenCalled()
expect(sessionRepository.remove).not.toHaveBeenCalled()
expect(revokedSessionRepository.remove).not.toHaveBeenCalled()
expect(ephemeralSessionRepository.deleteOne).not.toHaveBeenCalled()
})
it('should remove all user sessions', async () => {
await createHandler().handle(event)
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '00000000-0000-0000-0000-000000000000')
})
})

View File

@@ -1,19 +1,22 @@
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { Uuid } from '@standardnotes/domain-core'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
private userRepository: UserRepositoryInterface,
private sessionRepository: SessionRepositoryInterface,
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.Auth_SessionRepository) private sessionRepository: SessionRepositoryInterface,
@inject(TYPES.Auth_EphemeralSessionRepository)
private ephemeralSessionRepository: EphemeralSessionRepositoryInterface,
private revokedSessionRepository: RevokedSessionRepositoryInterface,
private logger: Logger,
@inject(TYPES.Auth_RevokedSessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
@inject(TYPES.Auth_Logger) private logger: Logger,
) {}
async handle(event: AccountDeletionRequestedEvent): Promise<void> {

View File

@@ -0,0 +1,117 @@
import { EmailLevel } from '@standardnotes/domain-core'
import { EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { User } from '../User/User'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { EmailSubscriptionUnsubscribedEventHandler } from './EmailSubscriptionUnsubscribedEventHandler'
describe('EmailSubscriptionUnsubscribedEventHandler', () => {
let userRepository: UserRepositoryInterface
let settingsService: SettingServiceInterface
let event: EmailSubscriptionUnsubscribedEvent
const createHandler = () => new EmailSubscriptionUnsubscribedEventHandler(userRepository, settingsService)
beforeEach(() => {
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue({} as jest.Mocked<User>)
settingsService = {} as jest.Mocked<SettingServiceInterface>
settingsService.createOrReplace = jest.fn()
event = {
payload: {
userEmail: 'test@test.te',
level: EmailLevel.LEVELS.Marketing,
},
} as jest.Mocked<EmailSubscriptionUnsubscribedEvent>
})
it('should not do anything if username is invalid', async () => {
event.payload.userEmail = ''
await createHandler().handle(event)
expect(settingsService.createOrReplace).not.toHaveBeenCalled()
})
it('should not do anything if user is not found', async () => {
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(settingsService.createOrReplace).not.toHaveBeenCalled()
})
it('should update user marketing email settings', async () => {
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_MARKETING_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user sign in email settings', async () => {
event.payload.level = EmailLevel.LEVELS.SignIn
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_SIGN_IN_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user email backup email settings', async () => {
event.payload.level = EmailLevel.LEVELS.FailedEmailBackup
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_FAILED_BACKUPS_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user email backup email settings', async () => {
event.payload.level = EmailLevel.LEVELS.FailedCloudBackup
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should throw error for unrecognized level', async () => {
event.payload.level = 'foobar'
let caughtError = null
try {
await createHandler().handle(event)
} catch (error) {
caughtError = error
}
expect(caughtError).not.toBeNull()
})
})

View File

@@ -1,21 +1,50 @@
import { EmailLevel, Username } from '@standardnotes/domain-core'
import { DomainEventHandlerInterface, EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { DisableEmailSettingBasedOnEmailSubscription } from '../UseCase/DisableEmailSettingBasedOnEmailSubscription/DisableEmailSettingBasedOnEmailSubscription'
import { SettingName } from '@standardnotes/settings'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
export class EmailSubscriptionUnsubscribedEventHandler implements DomainEventHandlerInterface {
constructor(
private disableEmailSettingBasedOnEmailSubscription: DisableEmailSettingBasedOnEmailSubscription,
private logger: Logger,
private userRepository: UserRepositoryInterface,
private settingsService: SettingServiceInterface,
) {}
async handle(event: EmailSubscriptionUnsubscribedEvent): Promise<void> {
const result = await this.disableEmailSettingBasedOnEmailSubscription.execute({
userEmail: event.payload.userEmail,
level: event.payload.level,
})
const usernameOrError = Username.create(event.payload.userEmail)
if (usernameOrError.isFailed()) {
return
}
const username = usernameOrError.getValue()
if (result.isFailed()) {
this.logger.error(`Failed to disable email setting for user ${event.payload.userEmail}: ${result.getError()}`)
const user = await this.userRepository.findOneByUsernameOrEmail(username)
if (user === null) {
return
}
await this.settingsService.createOrReplace({
user,
props: {
name: this.getSettingNameFromLevel(event.payload.level),
unencryptedValue: 'muted',
sensitive: false,
},
})
}
private getSettingNameFromLevel(level: string): string {
switch (level) {
case EmailLevel.LEVELS.FailedCloudBackup:
return SettingName.NAMES.MuteFailedCloudBackupsEmails
case EmailLevel.LEVELS.FailedEmailBackup:
return SettingName.NAMES.MuteFailedBackupsEmails
case EmailLevel.LEVELS.Marketing:
return SettingName.NAMES.MuteMarketingEmails
case EmailLevel.LEVELS.SignIn:
return SettingName.NAMES.MuteSignInEmails
default:
throw new Error(`Unknown level: ${level}`)
}
}
}

View File

@@ -1,26 +0,0 @@
import { DomainEventHandlerInterface, UserDesignatedAsSurvivorInSharedVaultEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { DesignateSurvivor } from '../UseCase/DesignateSurvivor/DesignateSurvivor'
export class UserDesignatedAsSurvivorInSharedVaultEventHandler implements DomainEventHandlerInterface {
constructor(
private designateSurvivorUseCase: DesignateSurvivor,
private logger: Logger,
) {}
async handle(event: UserDesignatedAsSurvivorInSharedVaultEvent): Promise<void> {
const result = await this.designateSurvivorUseCase.execute({
sharedVaultUuid: event.payload.sharedVaultUuid,
userUuid: event.payload.userUuid,
timestamp: event.payload.timestamp,
})
if (result.isFailed()) {
this.logger.error(
`Failed designate survivor for user ${event.payload.userUuid} and shared vault ${
event.payload.sharedVaultUuid
}: ${result.getError()}`,
)
}
}
}

View File

@@ -10,12 +10,6 @@ export class UserRemovedFromSharedVaultEventHandler implements DomainEventHandle
) {}
async handle(event: UserRemovedFromSharedVaultEvent): Promise<void> {
if (!event.payload.sharedVaultUuid) {
this.logger.error(`Shared vault uuid is missing from event: ${JSON.stringify(event)}`)
return
}
const result = await this.removeSharedVaultUser.execute({
userUuid: event.payload.userUuid,
sharedVaultUuid: event.payload.sharedVaultUuid,

View File

@@ -3,7 +3,7 @@ import { SubscriptionPlanName, Uuid } from '@standardnotes/domain-core'
import { SessionTrace } from './SessionTrace'
export interface SessionTraceRepositoryInterface {
insert(sessionTrace: SessionTrace): Promise<void>
save(sessionTrace: SessionTrace): Promise<void>
removeExpiredBefore(date: Date): Promise<void>
findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise<SessionTrace | null>
countByDate(date: Date): Promise<number>

View File

@@ -3,7 +3,6 @@ import { SharedVaultUser, Uuid } from '@standardnotes/domain-core'
export interface SharedVaultUserRepositoryInterface {
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null>
save(sharedVaultUser: SharedVaultUser): Promise<void>
remove(sharedVault: SharedVaultUser): Promise<void>
}

View File

@@ -43,7 +43,6 @@ export class AddSharedVaultUser implements UseCaseInterface<void> {
sharedVaultUuid,
permission,
timestamps,
isDesignatedSurvivor: false,
})
if (sharedVaultUserOrError.isFailed()) {
return Result.fail(sharedVaultUserOrError.getError())

View File

@@ -90,7 +90,6 @@ describe('CreateCrossServiceToken', () => {
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123456789, 123456789).getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
isDesignatedSurvivor: false,
}).getValue(),
])
})

View File

@@ -1,17 +1,14 @@
import 'reflect-metadata'
import { TransitionStatus } from '@standardnotes/domain-core'
import { TimerInterface } from '@standardnotes/time'
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
import { CreateValetToken } from './CreateValetToken'
import { TimerInterface } from '@standardnotes/time'
import { UserSubscription } from '../../Subscription/UserSubscription'
import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
import { User } from '../../User/User'
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
describe('CreateValetToken', () => {
let tokenEncoder: TokenEncoderInterface<ValetTokenData>
@@ -23,7 +20,6 @@ describe('CreateValetToken', () => {
let regularSubscription: UserSubscription
let sharedSubscription: UserSubscription
let user: User
let transitionStatusRepository: TransitionStatusRepositoryInterface
const createUseCase = () =>
new CreateValetToken(
@@ -33,7 +29,6 @@ describe('CreateValetToken', () => {
userSubscriptionService,
timer,
valetTokenTTL,
transitionStatusRepository,
)
beforeEach(() => {
@@ -71,11 +66,6 @@ describe('CreateValetToken', () => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(100)
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
transitionStatusRepository.getStatus = jest
.fn()
.mockReturnValue(TransitionStatus.create(TransitionStatus.STATUSES.Verified).getValue())
})
it('should create a read valet token', async () => {
@@ -176,7 +166,6 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: undefined,
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{
@@ -217,7 +206,6 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: '2-3-4',
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{
@@ -278,7 +266,6 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: undefined,
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{

View File

@@ -13,8 +13,6 @@ import { CreateValetTokenDTO } from './CreateValetTokenDTO'
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
import { CreateValetTokenPayload } from '../../ValetToken/CreateValetTokenPayload'
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
import { TransitionStatus } from '@standardnotes/domain-core'
@injectable()
export class CreateValetToken implements UseCaseInterface {
@@ -27,8 +25,6 @@ export class CreateValetToken implements UseCaseInterface {
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
@inject(TYPES.Auth_VALET_TOKEN_TTL) private valetTokenTTL: number,
@inject(TYPES.Auth_TransitionStatusRepository)
private transitionStatusRepository: TransitionStatusRepositoryInterface,
) {}
async execute(dto: CreateValetTokenDTO): Promise<CreateValetTokenResponseData> {
@@ -87,8 +83,6 @@ export class CreateValetToken implements UseCaseInterface {
sharedSubscriptionUuid = sharedSubscription.uuid
}
const transitionStatus = await this.transitionStatusRepository.getStatus(userUuid, 'items')
const tokenData: ValetTokenData = {
userUuid: dto.userUuid,
permittedOperation: dto.operation,
@@ -97,7 +91,6 @@ export class CreateValetToken implements UseCaseInterface {
uploadBytesLimit,
sharedSubscriptionUuid,
regularSubscriptionUuid: regularSubscription.uuid,
ongoingTransition: transitionStatus?.value === TransitionStatus.STATUSES.InProgress,
}
const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL)

View File

@@ -1,156 +0,0 @@
import { SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
import { DesignateSurvivor } from './DesignateSurvivor'
import { TimerInterface } from '@standardnotes/time'
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
describe('DesignateSurvivor', () => {
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
let sharedVaultUser: SharedVaultUser
let timer: TimerInterface
const createUseCase = () => new DesignateSurvivor(sharedVaultUserRepository, timer)
beforeEach(() => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
sharedVaultUser = SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: false,
}).getValue()
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(null)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
sharedVaultUserRepository.save = jest.fn()
})
it('should fail if shared vault uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: 'invalid',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should fail if user uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: 'invalid',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should fail if shared vault user is not found', async () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(null)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should designate a survivor if the user is a member', async () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(false)
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
expect(sharedVaultUserRepository.save).toBeCalledTimes(1)
})
it('should designate a survivor if the user is a member and there is already a survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(false)
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
expect(sharedVaultUserRepository.save).toBeCalledTimes(2)
})
it('should fail if the timestamp is older than the existing survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 122,
})
expect(result.isFailed()).toBe(true)
})
it('should do nothing if the user is already a survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 200,
})
expect(result.isFailed()).toBe(false)
})
})

View File

@@ -1,66 +0,0 @@
import { TimerInterface } from '@standardnotes/time'
import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { DesignateSurvivorDTO } from './DesignateSurvivorDTO'
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
export class DesignateSurvivor implements UseCaseInterface<void> {
constructor(
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
private timer: TimerInterface,
) {}
async execute(dto: DesignateSurvivorDTO): Promise<Result<void>> {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
}
const userUuid = userUuidOrError.getValue()
const existingSurvivor =
await this.sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid)
if (existingSurvivor) {
if (existingSurvivor.props.timestamps.updatedAt > dto.timestamp) {
return Result.fail(
'Cannot designate survivor to a previous version of the shared vault. Most probably a race condition.',
)
}
if (existingSurvivor.props.userUuid.value === userUuid.value) {
return Result.ok()
}
existingSurvivor.props.isDesignatedSurvivor = false
existingSurvivor.props.timestamps = Timestamps.create(
existingSurvivor.props.timestamps.createdAt,
this.timer.getTimestampInMicroseconds(),
).getValue()
await this.sharedVaultUserRepository.save(existingSurvivor)
}
const toBeDesignatedAsASurvivor = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
userUuid,
sharedVaultUuid,
})
if (!toBeDesignatedAsASurvivor) {
return Result.fail('User is not a member of the shared vault')
}
toBeDesignatedAsASurvivor.props.isDesignatedSurvivor = true
toBeDesignatedAsASurvivor.props.timestamps = Timestamps.create(
toBeDesignatedAsASurvivor.props.timestamps.createdAt,
this.timer.getTimestampInMicroseconds(),
).getValue()
await this.sharedVaultUserRepository.save(toBeDesignatedAsASurvivor)
return Result.ok()
}
}

View File

@@ -1,5 +0,0 @@
export interface DesignateSurvivorDTO {
sharedVaultUuid: string
userUuid: string
timestamp: number
}

View File

@@ -1,96 +0,0 @@
import { EmailLevel } from '@standardnotes/domain-core'
import { Setting } from '../../Setting/Setting'
import { SettingFactoryInterface } from '../../Setting/SettingFactoryInterface'
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
import { User } from '../../User/User'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { DisableEmailSettingBasedOnEmailSubscription } from './DisableEmailSettingBasedOnEmailSubscription'
describe('DisableEmailSettingBasedOnEmailSubscription', () => {
let userRepository: UserRepositoryInterface
let settingRepository: SettingRepositoryInterface
let factory: SettingFactoryInterface
let user: User
const createUseCase = () =>
new DisableEmailSettingBasedOnEmailSubscription(userRepository, settingRepository, factory)
beforeEach(() => {
user = {} as jest.Mocked<User>
user.uuid = 'userUuid'
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(user)
settingRepository = {} as jest.Mocked<SettingRepositoryInterface>
settingRepository.findLastByNameAndUserUuid = jest.fn().mockResolvedValue({} as jest.Mocked<Setting>)
settingRepository.save = jest.fn()
factory = {} as jest.Mocked<SettingFactoryInterface>
factory.create = jest.fn().mockResolvedValue({} as jest.Mocked<Setting>)
factory.createReplacement = jest.fn().mockResolvedValue({} as jest.Mocked<Setting>)
})
it('should fail if the username is empty', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userEmail: '',
level: EmailLevel.LEVELS.Marketing,
})
expect(result.isFailed()).toBeTruthy()
})
it('should fail if the user is not found', async () => {
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(null)
const useCase = createUseCase()
const result = await useCase.execute({
userEmail: 'test@test.te',
level: EmailLevel.LEVELS.Marketing,
})
expect(result.isFailed()).toBeTruthy()
})
it('should fail if the setting name cannot be determined', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userEmail: 'test@test.te',
level: 'invalid',
})
expect(result.isFailed()).toBeTruthy()
})
it('should create a new setting if it does not exist', async () => {
settingRepository.findLastByNameAndUserUuid = jest.fn().mockResolvedValue(null)
const useCase = createUseCase()
const result = await useCase.execute({
userEmail: 'test@test.te',
level: EmailLevel.LEVELS.Marketing,
})
expect(result.isFailed()).toBeFalsy()
expect(factory.create).toHaveBeenCalled()
expect(factory.createReplacement).not.toHaveBeenCalled()
})
it('should replace the setting if it exists', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userEmail: 'test@test.te',
level: EmailLevel.LEVELS.Marketing,
})
expect(result.isFailed()).toBeFalsy()
expect(factory.create).not.toHaveBeenCalled()
expect(factory.createReplacement).toHaveBeenCalled()
})
})

View File

@@ -1,72 +0,0 @@
import { EmailLevel, Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
import { SettingName } from '@standardnotes/settings'
import { DisableEmailSettingBasedOnEmailSubscriptionDTO } from './DisableEmailSettingBasedOnEmailSubscriptionDTO'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
import { SettingFactoryInterface } from '../../Setting/SettingFactoryInterface'
export class DisableEmailSettingBasedOnEmailSubscription implements UseCaseInterface<void> {
constructor(
private userRepository: UserRepositoryInterface,
private settingRepository: SettingRepositoryInterface,
private factory: SettingFactoryInterface,
) {}
async execute(dto: DisableEmailSettingBasedOnEmailSubscriptionDTO): Promise<Result<void>> {
const usernameOrError = Username.create(dto.userEmail)
if (usernameOrError.isFailed()) {
return Result.fail(usernameOrError.getError())
}
const username = usernameOrError.getValue()
const user = await this.userRepository.findOneByUsernameOrEmail(username)
if (user === null) {
return Result.fail(`User not found for email ${dto.userEmail}`)
}
const settingNameOrError = this.getSettingNameFromLevel(dto.level)
if (settingNameOrError.isFailed()) {
return Result.fail(settingNameOrError.getError())
}
const settingName = settingNameOrError.getValue()
let setting = await this.settingRepository.findLastByNameAndUserUuid(settingName, user.uuid)
if (!setting) {
setting = await this.factory.create(
{
name: settingName,
unencryptedValue: 'muted',
sensitive: false,
},
user,
)
} else {
setting = await this.factory.createReplacement(setting, {
name: settingName,
unencryptedValue: 'muted',
sensitive: false,
})
}
await this.settingRepository.save(setting)
return Result.ok()
}
private getSettingNameFromLevel(level: string): Result<string> {
/* istanbul ignore next */
switch (level) {
case EmailLevel.LEVELS.FailedCloudBackup:
return Result.ok(SettingName.NAMES.MuteFailedCloudBackupsEmails)
case EmailLevel.LEVELS.FailedEmailBackup:
return Result.ok(SettingName.NAMES.MuteFailedBackupsEmails)
case EmailLevel.LEVELS.Marketing:
return Result.ok(SettingName.NAMES.MuteMarketingEmails)
case EmailLevel.LEVELS.SignIn:
return Result.ok(SettingName.NAMES.MuteSignInEmails)
default:
return Result.fail(`Unknown level: ${level}`)
}
}
}

View File

@@ -1,4 +0,0 @@
export interface DisableEmailSettingBasedOnEmailSubscriptionDTO {
userEmail: string
level: string
}

View File

@@ -21,9 +21,19 @@ describe('Register', () => {
let user: User
let crypter: CrypterInterface
let timer: TimerInterface
let transitionModeEnabled = false
const createUseCase = () =>
new Register(userRepository, roleRepository, authResponseFactory, crypter, false, settingService, timer)
new Register(
userRepository,
roleRepository,
authResponseFactory,
crypter,
false,
settingService,
timer,
transitionModeEnabled,
)
beforeEach(() => {
userRepository = {} as jest.Mocked<UserRepositoryInterface>
@@ -84,7 +94,45 @@ describe('Register', () => {
expect(settingService.applyDefaultSettingsUponRegistration).toHaveBeenCalled()
})
it('should register a new user with default role', async () => {
const role = new Role()
role.name = 'role1'
roleRepository.findOneByName = jest.fn().mockReturnValue(role)
expect(
await createUseCase().execute({
email: 'test@test.te',
password: 'asdzxc',
updatedWithUserAgent: 'Mozilla',
apiVersion: '20200115',
ephemeralSession: false,
version: '004',
pwCost: 11,
pwSalt: 'qweqwe',
pwNonce: undefined,
}),
).toEqual({ success: true, authResponse: { foo: 'bar' } })
expect(userRepository.save).toHaveBeenCalledWith({
email: 'test@test.te',
encryptedPassword: expect.any(String),
encryptedServerKey: 'test',
serverEncryptionVersion: 1,
pwCost: 11,
pwNonce: undefined,
pwSalt: 'qweqwe',
updatedWithUserAgent: 'Mozilla',
uuid: expect.any(String),
version: '004',
createdAt: new Date(1),
updatedAt: new Date(1),
roles: Promise.resolve([role]),
})
})
it('should register a new user with default role and transition role', async () => {
transitionModeEnabled = true
const role = new Role()
role.name = RoleName.NAMES.CoreUser
@@ -201,6 +249,7 @@ describe('Register', () => {
true,
settingService,
timer,
transitionModeEnabled,
).execute({
email: 'test@test.te',
password: 'asdzxc',

View File

@@ -27,6 +27,7 @@ export class Register implements UseCaseInterface {
@inject(TYPES.Auth_DISABLE_USER_REGISTRATION) private disableUserRegistration: boolean,
@inject(TYPES.Auth_SettingService) private settingService: SettingServiceInterface,
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
@inject(TYPES.Auth_TRANSITION_MODE_ENABLED) private transitionModeEnabled: boolean,
) {}
async execute(dto: RegisterDTO): Promise<RegisterResponse> {
@@ -77,9 +78,11 @@ export class Register implements UseCaseInterface {
if (defaultRole) {
roles.push(defaultRole)
}
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
if (transitionRole) {
roles.push(transitionRole)
if (this.transitionModeEnabled) {
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
if (transitionRole) {
roles.push(transitionRole)
}
}
user.roles = Promise.resolve(roles)

View File

@@ -13,7 +13,6 @@ describe('RemoveSharedVaultUser', () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
.fn()
.mockReturnValue({} as jest.Mocked<SharedVaultUser>)
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([{} as jest.Mocked<SharedVaultUser>])
sharedVaultUserRepository.remove = jest.fn()
})
@@ -29,17 +28,6 @@ describe('RemoveSharedVaultUser', () => {
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
})
it('should remove all shared vault users', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
})
expect(result.isFailed()).toBeFalsy()
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
})
it('should fail when user uuid is invalid', async () => {
const useCase = createUseCase()

View File

@@ -13,31 +13,21 @@ export class RemoveSharedVaultUser implements UseCaseInterface<void> {
}
const userUuid = userUuidOrError.getValue()
let sharedVaultUuid: Uuid | undefined
if (dto.sharedVaultUuid !== undefined) {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
sharedVaultUuid = sharedVaultUuidOrError.getValue()
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
userUuid,
sharedVaultUuid,
})
if (!sharedVaultUser) {
return Result.fail('Shared vault user not found')
}
if (sharedVaultUuid) {
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
userUuid,
sharedVaultUuid,
})
if (!sharedVaultUser) {
return Result.fail('Shared vault user not found')
}
await this.sharedVaultUserRepository.remove(sharedVaultUser)
} else {
const sharedVaultUsers = await this.sharedVaultUserRepository.findByUserUuid(userUuid)
for (const sharedVaultUser of sharedVaultUsers) {
await this.sharedVaultUserRepository.remove(sharedVaultUser)
}
}
await this.sharedVaultUserRepository.remove(sharedVaultUser)
return Result.ok()
}

View File

@@ -1,4 +1,4 @@
export interface RemoveSharedVaultUserDTO {
sharedVaultUuid?: string
sharedVaultUuid: string
userUuid: string
}

View File

@@ -15,7 +15,7 @@ describe('TraceSession', () => {
beforeEach(() => {
sessionTraceRepository = {} as jest.Mocked<SessionTraceRepositoryInterface>
sessionTraceRepository.findOneByUserUuidAndDate = jest.fn().mockReturnValue(null)
sessionTraceRepository.insert = jest.fn()
sessionTraceRepository.save = jest.fn()
timer = {} as jest.Mocked<TimerInterface>
timer.getUTCDateNDaysAhead = jest.fn().mockReturnValue(new Date())
@@ -30,7 +30,7 @@ describe('TraceSession', () => {
expect(result.isFailed()).toBe(false)
expect(result.getValue().props.userUuid.value).toEqual('0702b137-4f5c-438a-915e-8f8b46572ce5')
expect(sessionTraceRepository.insert).toHaveBeenCalledTimes(1)
expect(sessionTraceRepository.save).toHaveBeenCalledTimes(1)
})
it('should not save a session trace if one already exists for the same user and date', async () => {
@@ -43,7 +43,7 @@ describe('TraceSession', () => {
})
expect(result.isFailed()).toBe(false)
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
})
it('should return an error if userUuid is invalid', async () => {
@@ -54,7 +54,7 @@ describe('TraceSession', () => {
})
expect(result.isFailed()).toBe(true)
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
})
it('should return an error if username is invalid', async () => {
@@ -65,7 +65,7 @@ describe('TraceSession', () => {
})
expect(result.isFailed()).toBe(true)
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
})
it('should return an error if subscriptionPlanName is invalid', async () => {
@@ -76,7 +76,7 @@ describe('TraceSession', () => {
})
expect(result.isFailed()).toBe(true)
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
})
it('should not save a session trace if creating of the session trace fails', async () => {
@@ -90,7 +90,7 @@ describe('TraceSession', () => {
})
expect(result.isFailed()).toBe(true)
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
mock.mockRestore()
})

View File

@@ -53,7 +53,7 @@ export class TraceSession implements UseCaseInterface<SessionTrace> {
}
const sessionTrace = sessionTraceOrError.getValue()
await this.sessionTraceRepository.insert(sessionTrace)
await this.sessionTraceRepository.save(sessionTrace)
return Result.ok<SessionTrace>(sessionTrace)
}

View File

@@ -2,12 +2,10 @@ import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/sec
import { NextFunction, Request, Response } from 'express'
import { BaseMiddleware } from 'inversify-express-utils'
import { Logger } from 'winston'
import { Segment, getSegment } from 'aws-xray-sdk'
export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
constructor(
private tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
private isConfiguredForAWSProduction: boolean,
private logger: Logger,
) {
super()
@@ -41,13 +39,6 @@ export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
response.locals.session = token.session
response.locals.readOnlyAccess = token.session?.readonly_access ?? false
if (this.isConfiguredForAWSProduction) {
const segment = getSegment()
if (segment instanceof Segment) {
segment.setUser(token.user.uuid)
}
}
return next()
} catch (error) {
return next(error)

View File

@@ -1,16 +1,18 @@
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../../Bootstrap/Types'
import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware'
@injectable()
export class OptionalCrossServiceTokenMiddleware extends ApiGatewayAuthMiddleware {
constructor(
tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
isConfiguredForAWSProduction: boolean,
logger: Logger,
@inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
@inject(TYPES.Auth_Logger) logger: Logger,
) {
super(tokenDecoder, isConfiguredForAWSProduction, logger)
super(tokenDecoder, logger)
}
protected override handleMissingToken(request: Request, _response: Response, next: NextFunction): boolean {

View File

@@ -1,16 +1,18 @@
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../../Bootstrap/Types'
import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware'
@injectable()
export class RequiredCrossServiceTokenMiddleware extends ApiGatewayAuthMiddleware {
constructor(
tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
isConfiguredForAWSProduction: boolean,
logger: Logger,
@inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
@inject(TYPES.Auth_Logger) logger: Logger,
) {
super(tokenDecoder, isConfiguredForAWSProduction, logger)
super(tokenDecoder, logger)
}
protected override handleMissingToken(request: Request, response: Response, _next: NextFunction): boolean {

View File

@@ -19,8 +19,8 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status.value)
break
case TransitionStatus.STATUSES.InProgress: {
const ttl24Hours = 86_400
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl24Hours, status.value)
const ttl2Hourse = 7_200
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl2Hourse, status.value)
break
}
}

View File

@@ -1,7 +1,5 @@
import { MapperInterface, SubscriptionPlanName, Uuid } from '@standardnotes/domain-core'
import { TimerInterface } from '@standardnotes/time'
import { Repository } from 'typeorm'
import { SessionTrace } from '../../Domain/Session/SessionTrace'
import { SessionTraceRepositoryInterface } from '../../Domain/Session/SessionTraceRepositoryInterface'
import { TypeORMSessionTrace } from './TypeORMSessionTrace'
@@ -10,14 +8,13 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
constructor(
private ormRepository: Repository<TypeORMSessionTrace>,
private mapper: MapperInterface<SessionTrace, TypeORMSessionTrace>,
private timer: TimerInterface,
) {}
async countByDateAndSubscriptionPlanName(date: Date, subscriptionPlanName: SubscriptionPlanName): Promise<number> {
return this.ormRepository
.createQueryBuilder('trace')
.where('trace.creation_date = :creationDate', {
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
})
.andWhere('trace.subscription_plan_name = :subscriptionPlanName', {
subscriptionPlanName: subscriptionPlanName.value,
@@ -29,7 +26,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
return this.ormRepository
.createQueryBuilder('trace')
.where('trace.creation_date = :creationDate', {
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
})
.getCount()
}
@@ -47,7 +44,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
.createQueryBuilder('trace')
.where('trace.user_uuid = :userUuid AND trace.creation_date = :creationDate', {
userUuid: userUuid.value,
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
})
.getOne()
@@ -58,9 +55,9 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
return this.mapper.toDomain(typeOrm)
}
async insert(sessionTrace: SessionTrace): Promise<void> {
async save(sessionTrace: SessionTrace): Promise<void> {
const persistence = this.mapper.toProjection(sessionTrace)
await this.ormRepository.insert(persistence)
await this.ormRepository.save(persistence)
}
}

View File

@@ -26,13 +26,6 @@ export class TypeORMSharedVaultUser {
})
declare permission: string
@Column({
name: 'is_designated_survivor',
type: 'boolean',
default: false,
})
declare isDesignatedSurvivor: boolean
@Column({
name: 'created_at_timestamp',
type: 'bigint',

View File

@@ -10,24 +10,6 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
) {}
async findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null> {
const persistence = await this.ormRepository
.createQueryBuilder('shared_vault_user')
.where('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
sharedVaultUuid: sharedVaultUuid.value,
})
.andWhere('shared_vault_user.is_designated_survivor = :isDesignatedSurvivor', {
isDesignatedSurvivor: true,
})
.getOne()
if (persistence === null) {
return null
}
return this.mapper.toDomain(persistence)
}
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
const persistence = await this.ormRepository
.createQueryBuilder('shared_vault_user')

View File

@@ -1,11 +1,8 @@
import { MapperInterface, SubscriptionPlanName, UniqueEntityId, Username, Uuid } from '@standardnotes/domain-core'
import { SessionTrace } from '../Domain/Session/SessionTrace'
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
import { TimerInterface } from '@standardnotes/time'
export class SessionTracePersistenceMapper implements MapperInterface<SessionTrace, TypeORMSessionTrace> {
constructor(private timer: TimerInterface) {}
toDomain(projection: TypeORMSessionTrace): SessionTrace {
const userUuidOrError = Uuid.create(projection.userUuid)
if (userUuidOrError.isFailed()) {
@@ -53,7 +50,11 @@ export class SessionTracePersistenceMapper implements MapperInterface<SessionTra
typeOrm.username = domain.props.username.value
typeOrm.subscriptionPlanName = domain.props.subscriptionPlanName ? domain.props.subscriptionPlanName.value : null
typeOrm.createdAt = domain.props.createdAt
typeOrm.creationDate = new Date(this.timer.convertDateToFormattedString(domain.props.createdAt, 'YYYY-MM-DD'))
typeOrm.creationDate = new Date(
domain.props.createdAt.getFullYear(),
domain.props.createdAt.getMonth(),
domain.props.createdAt.getDate(),
)
typeOrm.expiresAt = domain.props.expiresAt
return typeOrm

View File

@@ -41,7 +41,6 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
sharedVaultUuid,
permission,
timestamps,
isDesignatedSurvivor: !!projection.isDesignatedSurvivor,
},
new UniqueEntityId(projection.uuid),
)
@@ -62,7 +61,6 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
typeorm.permission = domain.props.permission.value
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
typeorm.isDesignatedSurvivor = !!domain.props.isDesignatedSurvivor
return typeorm
}

Some files were not shown because too many files have changed in this diff Show More