Compare commits

...

61 Commits

Author SHA1 Message Date
standardci
c4c3dfb823 chore(release): publish new version
- @standardnotes/analytics@2.27.9
 - @standardnotes/api-gateway@1.76.1
 - @standardnotes/auth-server@1.150.8
 - @standardnotes/domain-events-infra@1.14.6
 - @standardnotes/event-store@1.12.9
 - @standardnotes/files-server@1.26.1
 - @standardnotes/home-server@1.16.25
 - @standardnotes/revisions-server@1.39.2
 - @standardnotes/scheduler-server@1.21.9
 - @standardnotes/syncing-server@1.111.1
 - @standardnotes/websockets-server@1.11.9
2023-10-05 05:36:39 +00:00
Karol Sójko
c4b6f17ebc fix(domain-events-infra): setting user metadata on workers 2023-10-05 07:16:18 +02:00
standardci
71ce938cb1 chore(release): publish new version
- @standardnotes/api-gateway@1.76.0
 - @standardnotes/files-server@1.26.0
 - @standardnotes/home-server@1.16.24
 - @standardnotes/syncing-server@1.111.0
2023-10-04 18:03:38 +00:00
Karol Sójko
dc76113915 feat(api-gateaway): configure aws-xray-sdk 2023-10-04 19:44:25 +02:00
Karol Sójko
6583ff6cd9 feat: add xray to syncing server and files 2023-10-04 19:39:16 +02:00
standardci
69ea947857 chore(release): publish new version
- @standardnotes/analytics@2.27.8
 - @standardnotes/api-gateway@1.75.13
 - @standardnotes/auth-server@1.150.7
 - @standardnotes/domain-core@1.34.2
 - @standardnotes/domain-events-infra@1.14.5
 - @standardnotes/event-store@1.12.8
 - @standardnotes/files-server@1.25.6
 - @standardnotes/home-server@1.16.23
 - @standardnotes/revisions-server@1.39.1
 - @standardnotes/scheduler-server@1.21.8
 - @standardnotes/settings@1.21.42
 - @standardnotes/syncing-server@1.110.8
 - @standardnotes/websockets-server@1.11.8
2023-10-04 16:36:09 +00:00
Karol Sójko
eab78b3a95 fix: identifying services in workers 2023-10-04 18:13:41 +02:00
standardci
3097f7f063 chore(release): publish new version
- @standardnotes/home-server@1.16.22
 - @standardnotes/revisions-server@1.39.0
2023-10-04 15:42:53 +00:00
Karol Sójko
126989df82 feat(revisions): add aws-xray-sdk 2023-10-04 17:22:40 +02:00
Karol Sójko
a25eb178c5 chore: fix env var replacement 2023-10-04 16:43:48 +02:00
standardci
18dae26b8e chore(release): publish new version
- @standardnotes/analytics@2.27.7
 - @standardnotes/api-gateway@1.75.12
 - @standardnotes/auth-server@1.150.6
 - @standardnotes/domain-events-infra@1.14.4
 - @standardnotes/event-store@1.12.7
 - @standardnotes/files-server@1.25.5
 - @standardnotes/home-server@1.16.21
 - @standardnotes/revisions-server@1.38.10
 - @standardnotes/scheduler-server@1.21.7
 - @standardnotes/syncing-server@1.110.7
 - @standardnotes/websockets-server@1.11.7
2023-10-04 14:30:55 +00:00
Karol Sójko
5bc8157d4d chore: disable vault tests temporarily 2023-10-04 16:09:27 +02:00
Karol Sójko
f29826e917 chore: add filling in the package version 2023-10-04 16:09:15 +02:00
Karol Sójko
ba7cbb989b fix(domain-events-infra): subsegment name 2023-10-04 15:51:33 +02:00
Karol Sójko
d4579ce21e fix(auth): disable users from registering to MongoDB 2023-10-04 15:51:32 +02:00
standardci
ed14d2dfb7 chore(release): publish new version
- @standardnotes/analytics@2.27.6
 - @standardnotes/api-gateway@1.75.11
 - @standardnotes/auth-server@1.150.5
 - @standardnotes/domain-events-infra@1.14.3
 - @standardnotes/event-store@1.12.6
 - @standardnotes/files-server@1.25.4
 - @standardnotes/home-server@1.16.20
 - @standardnotes/revisions-server@1.38.9
 - @standardnotes/scheduler-server@1.21.6
 - @standardnotes/syncing-server@1.110.6
 - @standardnotes/websockets-server@1.11.6
2023-10-04 13:17:42 +00:00
Karol Sójko
64f1fe59c2 fix(domain-events-infra): remove redundant flush 2023-10-04 14:49:49 +02:00
Karol Sójko
207ef9f3e5 fix(domain-events-infra): handling segments 2023-10-04 14:47:45 +02:00
standardci
3ea0435459 chore(release): publish new version
- @standardnotes/analytics@2.27.5
 - @standardnotes/api-gateway@1.75.10
 - @standardnotes/auth-server@1.150.4
 - @standardnotes/domain-events-infra@1.14.2
 - @standardnotes/event-store@1.12.5
 - @standardnotes/files-server@1.25.3
 - @standardnotes/home-server@1.16.19
 - @standardnotes/revisions-server@1.38.8
 - @standardnotes/scheduler-server@1.21.5
 - @standardnotes/syncing-server@1.110.5
 - @standardnotes/websockets-server@1.11.5
2023-10-04 12:38:32 +00:00
Karol Sójko
7668713dd6 fix(domain-events-infra): imports 2023-10-04 14:06:47 +02:00
Karol Sójko
f6bc1c3084 fix(domain-events-infra): handling async functions 2023-10-04 14:01:46 +02:00
standardci
4203ff9a0c chore(release): publish new version
- @standardnotes/auth-server@1.150.3
 - @standardnotes/home-server@1.16.18
2023-10-04 11:48:12 +00:00
Karol Sójko
0281724196 fix(auth): remove ec2 plugin from worker 2023-10-04 13:21:14 +02:00
standardci
94bd319279 chore(release): publish new version
- @standardnotes/auth-server@1.150.2
 - @standardnotes/home-server@1.16.17
2023-10-04 11:09:36 +00:00
Karol Sójko
373cfad1f7 fix(auth): remove ec2 plugin 2023-10-04 12:42:49 +02:00
standardci
59107f8553 chore(release): publish new version
- @standardnotes/analytics@2.27.4
 - @standardnotes/api-gateway@1.75.9
 - @standardnotes/auth-server@1.150.1
 - @standardnotes/domain-events-infra@1.14.1
 - @standardnotes/event-store@1.12.4
 - @standardnotes/files-server@1.25.2
 - @standardnotes/home-server@1.16.16
 - @standardnotes/revisions-server@1.38.7
 - @standardnotes/scheduler-server@1.21.4
 - @standardnotes/syncing-server@1.110.4
 - @standardnotes/websockets-server@1.11.4
2023-10-03 13:32:00 +00:00
Karol Sójko
e066b6a126 fix(domain-events-infra): change segment closing 2023-10-03 14:57:59 +02:00
standardci
a0e4926135 chore(release): publish new version
- @standardnotes/analytics@2.27.3
 - @standardnotes/api-gateway@1.75.8
 - @standardnotes/auth-server@1.150.0
 - @standardnotes/domain-events-infra@1.14.0
 - @standardnotes/event-store@1.12.3
 - @standardnotes/files-server@1.25.1
 - @standardnotes/home-server@1.16.15
 - @standardnotes/revisions-server@1.38.6
 - @standardnotes/scheduler-server@1.21.3
 - @standardnotes/syncing-server@1.110.3
 - @standardnotes/websockets-server@1.11.3
2023-10-03 06:53:13 +00:00
Karol Sójko
b1b244a2cf feat: add xray segment tracing on auth-worker 2023-10-03 08:21:57 +02:00
standardci
b4a38d9dcc chore(release): publish new version
- @standardnotes/auth-server@1.149.1
 - @standardnotes/home-server@1.16.14
2023-10-02 15:45:45 +00:00
Karol Sójko
71e2a4187e fix(auth): enable xray only for server 2023-10-02 16:53:03 +02:00
standardci
c94f9c1a60 chore(release): publish new version
- @standardnotes/auth-server@1.149.0
 - @standardnotes/home-server@1.16.13
 - @standardnotes/revisions-server@1.38.5
 - @standardnotes/syncing-server@1.110.2
2023-10-02 13:56:17 +00:00
Karol Sójko
b9907924be chore: fix run name for e2e test suite to include suite 2023-10-02 14:37:10 +02:00
Karol Sójko
d4c188c096 feat: divide suites furthermore 2023-10-02 14:19:12 +02:00
Karol Sójko
2ccaf490df feat: divide test suites on a higher level 2023-10-02 13:47:32 +02:00
Karol Sójko
cd893b41d7 fix: temproarily disable transitions to empty overpopulated queues 2023-10-02 13:05:54 +02:00
Karol Sójko
3838358081 fix(auth): add aws xray ecs and ec2 plugins 2023-10-02 12:56:06 +02:00
Karol Sójko
1a388f00c3 feat(auth): introduce AWS X-Ray SDK (#859)
* feat(auth): configure aws-xray-sdk for express

* configured aws sdk clients

* configure setting user metadata on segment

* fix binding

* fix binding order
2023-10-02 12:52:49 +02:00
Karol Sójko
bd71422fab fix(auth): prevent from loop disabling of email settings (#858) 2023-10-02 10:56:35 +02:00
standardci
3e1697f47f chore(release): publish new version
- @standardnotes/home-server@1.16.12
 - @standardnotes/revisions-server@1.38.4
 - @standardnotes/syncing-server@1.110.1
2023-09-29 13:13:19 +00:00
Karol Sójko
032cde7723 fix: keep transition in-progress status alive 2023-09-29 14:08:31 +02:00
standardci
34b956b482 chore(release): publish new version
- @standardnotes/auth-server@1.148.2
 - @standardnotes/home-server@1.16.11
 - @standardnotes/revisions-server@1.38.3
 - @standardnotes/syncing-server@1.110.0
2023-09-29 10:23:26 +00:00
Karol Sójko
681e0378ae fix(auth): increase ttl for in progress duration of transitions 2023-09-29 11:05:54 +02:00
Karol Sójko
609e85f926 feat(syncing-server): allow surviving only upon account deletion (#857) 2023-09-29 10:51:26 +02:00
Karol Sójko
e4ca310707 fix: add paging memory to integrity check 2023-09-29 10:47:50 +02:00
standardci
d606493356 chore(release): publish new version
- @standardnotes/auth-server@1.148.1
 - @standardnotes/home-server@1.16.10
2023-09-29 08:14:08 +00:00
Karol Sójko
5ef6c5c14a fix(auth): disable transitions retriggering if they are in progress 2023-09-28 14:40:37 +02:00
standardci
0188f290f9 chore(release): publish new version
- @standardnotes/analytics@2.27.2
 - @standardnotes/api-gateway@1.75.7
 - @standardnotes/auth-server@1.148.0
 - @standardnotes/domain-events-infra@1.13.1
 - @standardnotes/domain-events@2.131.1
 - @standardnotes/event-store@1.12.2
 - @standardnotes/files-server@1.25.0
 - @standardnotes/home-server@1.16.9
 - @standardnotes/revisions-server@1.38.2
 - @standardnotes/scheduler-server@1.21.2
 - @standardnotes/security@1.15.0
 - @standardnotes/syncing-server@1.109.2
 - @standardnotes/time@1.17.0
 - @standardnotes/websockets-server@1.11.2
2023-09-28 11:39:49 +00:00
Karol Sójko
676cf36f8d feat: block file operations during transition (#856)
* feat: block file operations during transition

* fix: tracing sessions

* fix fs file removal

* fix: checking if directory exists before listing files

* fix: removing shared vault user on auth side
2023-09-28 13:10:58 +02:00
Karol Sójko
f8aef6c8ef feat: archiving failed run logs 2023-09-27 14:20:34 +02:00
Karol Sójko
5bf8cf49c1 feat: parametrize test suite to be run 2023-09-27 12:14:33 +02:00
standardci
51cd0a4dad chore(release): publish new version
- @standardnotes/analytics@2.27.1
 - @standardnotes/api-gateway@1.75.6
 - @standardnotes/auth-server@1.147.1
 - @standardnotes/domain-core@1.34.1
 - @standardnotes/event-store@1.12.1
 - @standardnotes/files-server@1.24.1
 - @standardnotes/home-server@1.16.8
 - @standardnotes/revisions-server@1.38.1
 - @standardnotes/scheduler-server@1.21.1
 - @standardnotes/settings@1.21.41
 - @standardnotes/syncing-server@1.109.1
 - @standardnotes/websockets-server@1.11.1
2023-09-27 09:45:40 +00:00
Karol Sójko
1d06ffe9d5 fix: removing items in a vault and notifying about designated survivor (#855)
* fix: removing items in a vault and notifying about designated survivor

* fix deleting shared vault items
2023-09-27 11:10:41 +02:00
standardci
dbf532f55e chore(release): publish new version
- @standardnotes/analytics@2.27.0
 - @standardnotes/api-gateway@1.75.5
 - @standardnotes/auth-server@1.147.0
 - @standardnotes/common@1.51.0
 - @standardnotes/domain-core@1.34.0
 - @standardnotes/domain-events-infra@1.13.0
 - @standardnotes/domain-events@2.131.0
 - @standardnotes/event-store@1.12.0
 - @standardnotes/files-server@1.24.0
 - @standardnotes/home-server@1.16.7
 - @standardnotes/predicates@1.7.0
 - @standardnotes/revisions-server@1.38.0
 - @standardnotes/scheduler-server@1.21.0
 - @standardnotes/security@1.14.0
 - @standardnotes/settings@1.21.40
 - @standardnotes/syncing-server@1.109.0
 - @standardnotes/time@1.16.0
 - @standardnotes/websockets-server@1.11.0
2023-09-26 10:05:20 +00:00
Karol Sójko
ca6dbc0053 feat: refactor handling revision creation from dump (#854)
* feat: refactor handling revision creation from dump

* fix: dump repository handling
2023-09-26 11:47:41 +02:00
standardci
1bb5980b45 chore(release): publish new version
- @standardnotes/analytics@2.26.24
 - @standardnotes/api-gateway@1.75.4
 - @standardnotes/auth-server@1.146.4
 - @standardnotes/domain-core@1.33.2
 - @standardnotes/event-store@1.11.52
 - @standardnotes/files-server@1.23.2
 - @standardnotes/home-server@1.16.6
 - @standardnotes/revisions-server@1.37.3
 - @standardnotes/scheduler-server@1.20.56
 - @standardnotes/settings@1.21.39
 - @standardnotes/syncing-server@1.108.2
 - @standardnotes/websockets-server@1.10.53
2023-09-25 16:42:43 +00:00
Karol Sójko
a02a28774b fix(domain-core): notification paylod to string casting 2023-09-25 18:24:38 +02:00
standardci
2d9b3578b6 chore(release): publish new version
- @standardnotes/analytics@2.26.23
 - @standardnotes/api-gateway@1.75.3
 - @standardnotes/auth-server@1.146.3
 - @standardnotes/domain-core@1.33.1
 - @standardnotes/event-store@1.11.51
 - @standardnotes/files-server@1.23.1
 - @standardnotes/home-server@1.16.5
 - @standardnotes/revisions-server@1.37.2
 - @standardnotes/scheduler-server@1.20.55
 - @standardnotes/settings@1.21.38
 - @standardnotes/syncing-server@1.108.1
 - @standardnotes/websockets-server@1.10.52
2023-09-25 15:28:21 +00:00
Karol Sójko
3d5e747590 fix(syncing-server): another spec 2023-09-25 17:10:30 +02:00
Karol Sójko
94467747ac fix(syncing-server): specs 2023-09-25 15:52:35 +02:00
Karol Sójko
cebab59a02 fix: refactor the structure of notifications (#853) 2023-09-25 15:40:51 +02:00
196 changed files with 3604 additions and 1229 deletions

View File

@@ -38,6 +38,10 @@ jobs:
run: |
jq '(.containerDefinitions[] | select(.name=="${{ inputs.service_name }}-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: Fill in the new package version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="${{ inputs.service_name }}-prod") | .environment[] | select(.name=="npm_package_version")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def-prod
uses: aws-actions/amazon-ecs-render-task-definition@v1

View File

@@ -7,6 +7,10 @@ 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
@@ -18,149 +22,16 @@ on:
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
- name: Show logs on failure
if: ${{ failure() }}
run: |
echo "# Errors:"
tail -n 100 logs/*.err
echo "# Logs:"
tail -n 100 logs/*.log
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-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
- name: Show logs on failure
if: ${{ failure() }}
run: tail -n 500 logs/output.log
uses: standardnotes/server/.github/workflows/e2e-home-server.yml@main
with:
snjs_image_tag: ${{ inputs.snjs_image_tag }}
suite: ${{ inputs.suite }}
secrets: inherit

128
.github/workflows/e2e-home-server.yml vendored Normal file
View File

@@ -0,0 +1,128 @@
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

72
.github/workflows/e2e-self-hosted.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
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 On Self Hosted Server
name: E2E Test Suite
run-name: E2E Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
run-name: E2E ${{ inputs.suite }} Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
on:
schedule:
@@ -11,6 +11,10 @@ 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
@@ -26,4 +30,5 @@ 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

@@ -95,9 +95,20 @@ jobs:
- name: Test
run: yarn test
e2e:
e2e-base:
needs: build
name: E2E
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
# uses: standardnotes/server/.github/workflows/common-e2e.yml@main
# with:
# snjs_image_tag: 'latest'
# suite: 'vaults'
# secrets: inherit

View File

@@ -95,20 +95,32 @@ jobs:
- name: Test
run: yarn test
e2e:
e2e-base:
needs: build
name: E2E
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
# 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 ]
needs: [ test, lint, e2e-base ]
name: Publish Self Hosting Docker Image
uses: standardnotes/server/.github/workflows/common-self-hosting.yml@main
secrets: inherit
publish-services:
needs: [ test, lint, e2e ]
needs: [ test, lint, e2e-base ]
runs-on: ubuntu-latest

323
.pnp.cjs generated
View File

@@ -1605,6 +1605,13 @@ 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", [\
@@ -1788,6 +1795,15 @@ 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", [\
@@ -5488,6 +5504,14 @@ 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", [\
@@ -5773,6 +5797,7 @@ const RAW_RUNTIME_STATE =
["@types/prettyjson", "npm:0.0.30"],\
["@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"],\
["cors", "npm:2.8.5"],\
["dotenv", "npm:16.1.3"],\
@@ -5835,6 +5860,7 @@ 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"],\
@@ -5938,6 +5964,7 @@ 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"],\
@@ -6027,6 +6054,7 @@ 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"],\
["connect-busboy", "npm:1.0.0"],\
["cors", "npm:2.8.5"],\
["dayjs", "npm:1.11.7"],\
@@ -6162,6 +6190,7 @@ const RAW_RUNTIME_STATE =
["@types/node", "npm:20.5.7"],\
["@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"],\
["cors", "npm:2.8.5"],\
["dotenv", "npm:16.1.3"],\
["eslint", "npm:8.41.0"],\
@@ -6353,6 +6382,7 @@ 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"],\
["cors", "npm:2.8.5"],\
["dotenv", "npm:16.1.3"],\
@@ -6609,6 +6639,16 @@ 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/",\
@@ -6868,6 +6908,16 @@ 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/",\
@@ -6936,6 +6986,18 @@ 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/",\
@@ -7946,6 +8008,16 @@ 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/",\
@@ -7955,6 +8027,112 @@ 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/",\
@@ -8745,6 +8923,18 @@ 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/",\
@@ -9635,6 +9825,16 @@ 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/",\
@@ -14007,6 +14207,15 @@ 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/",\
@@ -14467,6 +14676,49 @@ 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/",\
@@ -14538,6 +14790,52 @@ 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/",\
@@ -15297,6 +15595,13 @@ 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": [\
@@ -15441,6 +15746,15 @@ 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/",\
@@ -15765,6 +16079,15 @@ 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/",\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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 workspace @standardnotes/home-server run build && PORT=3123 yarn workspace @standardnotes/home-server start",
"e2e": "yarn 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,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.27.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.8...@standardnotes/analytics@2.27.9) (2023-10-05)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.7...@standardnotes/analytics@2.27.8) (2023-10-04)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.6...@standardnotes/analytics@2.27.7) (2023-10-04)
**Note:** Version bump only for package @standardnotes/analytics
## [2.27.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.5...@standardnotes/analytics@2.27.6) (2023-10-04)
**Note:** Version bump only for package @standardnotes/analytics
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.26.22",
"version": "2.27.9",
"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 --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --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,6 +3,60 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.76.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.76.0...@standardnotes/api-gateway@1.76.1) (2023-10-05)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.76.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.13...@standardnotes/api-gateway@1.76.0) (2023-10-04)
### Features
* **api-gateaway:** configure aws-xray-sdk ([dc76113](https://github.com/standardnotes/api-gateway/commit/dc7611391515dd49a8e9b7ce8ac5e128cd7af0a8))
## [1.75.13](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.12...@standardnotes/api-gateway@1.75.13) (2023-10-04)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.12](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.11...@standardnotes/api-gateway@1.75.12) (2023-10-04)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.11](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.10...@standardnotes/api-gateway@1.75.11) (2023-10-04)
**Note:** Version bump only for package @standardnotes/api-gateway
## [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

View File

@@ -29,6 +29,7 @@ import helmet from 'helmet'
import * as cors from 'cors'
import { text, json, Request, Response, NextFunction } from 'express'
import * as winston from 'winston'
import * as AWSXRay from 'aws-xray-sdk'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const robots = require('express-robots-txt')
@@ -36,15 +37,27 @@ 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) => {
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.ApiGateway))
}
app.use((_request: Request, response: Response, next: NextFunction) => {
response.setHeader('X-API-Gateway-Version', container.get(TYPES.ApiGateway_VERSION))
next()
@@ -104,6 +117,10 @@ 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

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.75.2",
"version": "1.76.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -31,6 +31,7 @@
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/security": "workspace:*",
"@standardnotes/time": "workspace:*",
"aws-xray-sdk": "^3.5.2",
"axios": "^1.1.3",
"cors": "2.8.5",
"dotenv": "^16.0.1",

View File

@@ -3,6 +3,105 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.150.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.7...@standardnotes/auth-server@1.150.8) (2023-10-05)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.150.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.6...@standardnotes/auth-server@1.150.7) (2023-10-04)
### Bug Fixes
* identifying services in workers ([eab78b3](https://github.com/standardnotes/server/commit/eab78b3a95ec82cb779d069d172169bfa92368c9))
## [1.150.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.5...@standardnotes/auth-server@1.150.6) (2023-10-04)
### Bug Fixes
* **auth:** disable users from registering to MongoDB ([d4579ce](https://github.com/standardnotes/server/commit/d4579ce21ed34ba433deb1fe2ff25c073fbe9622))
## [1.150.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.150.4...@standardnotes/auth-server@1.150.5) (2023-10-04)
**Note:** Version bump only for package @standardnotes/auth-server
## [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

View File

@@ -24,11 +24,13 @@ 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) => {
@@ -37,9 +39,20 @@ 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()
@@ -66,6 +79,10 @@ 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

@@ -55,7 +55,7 @@ const requestTransition = async (
let wasTransitionRequested = false
if (itemsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
if (itemsTransitionStatus === null || itemsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'items')
@@ -68,7 +68,7 @@ const requestTransition = async (
)
}
if (revisionsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
if (revisionsTransitionStatus === null || revisionsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'revisions')

View File

@@ -8,6 +8,7 @@ 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) => {
@@ -16,6 +17,14 @@ 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...')

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.146.2",
"version": "1.150.8",
"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 --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --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,6 +54,7 @@
"@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,5 +1,6 @@
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'
@@ -92,7 +93,7 @@ import {
SNSDomainEventPublisher,
SQSDomainEventSubscriberFactory,
SQSEventMessageHandler,
SQSNewRelicEventMessageHandler,
SQSXRayEventMessageHandler,
} from '@standardnotes/domain-events-infra'
import { GetUserSubscription } from '../Domain/UseCase/GetUserSubscription/GetUserSubscription'
import { ChangeCredentials } from '../Domain/UseCase/ChangeCredentials/ChangeCredentials'
@@ -189,6 +190,7 @@ import {
ControllerContainer,
ControllerContainerInterface,
MapperInterface,
ServiceIdentifier,
SharedVaultUser,
} from '@standardnotes/domain-core'
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
@@ -273,6 +275,7 @@ import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedT
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') {}
@@ -319,6 +322,8 @@ 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) {
@@ -349,7 +354,11 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
}
}
container.bind<SNSClient>(TYPES.Auth_SNS).toConstantValue(new SNSClient(snsConfig))
let snsClient = new SNSClient(snsConfig)
if (isConfiguredForAWSProduction) {
snsClient = captureAWSv3Client(snsClient)
}
container.bind<SNSClient>(TYPES.Auth_SNS).toConstantValue(snsClient)
const sqsConfig: SQSClientConfig = {
region: env.get('SQS_AWS_REGION', true),
@@ -363,13 +372,17 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
}
}
container.bind<SQSClient>(TYPES.Auth_SQS).toConstantValue(new SQSClient(sqsConfig))
let sqsClient = new SQSClient(sqsConfig)
if (isConfiguredForAWSProduction) {
sqsClient = captureAWSv3Client(sqsClient)
}
container.bind<SQSClient>(TYPES.Auth_SQS).toConstantValue(sqsClient)
}
// Mapping
container
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper)
.toConstantValue(new SessionTracePersistenceMapper())
.toConstantValue(new SessionTracePersistenceMapper(container.get<TimerInterface>(TYPES.Auth_Timer)))
container
.bind<MapperInterface<Authenticator, TypeORMAuthenticator>>(TYPES.Auth_AuthenticatorPersistenceMapper)
.toConstantValue(new AuthenticatorPersistenceMapper())
@@ -458,8 +471,9 @@ export class ContainerConfigLoader {
.bind<SessionTraceRepositoryInterface>(TYPES.Auth_SessionTraceRepository)
.toConstantValue(
new TypeORMSessionTraceRepository(
container.get(TYPES.Auth_ORMSessionTraceRepository),
container.get(TYPES.Auth_SessionTracePersistenceMapper),
container.get<Repository<TypeORMSessionTrace>>(TYPES.Auth_ORMSessionTraceRepository),
container.get<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper),
container.get<TimerInterface>(TYPES.Auth_Timer),
),
)
container
@@ -497,20 +511,6 @@ 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)
@@ -739,6 +739,32 @@ 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)
@@ -964,6 +990,15 @@ export class ContainerConfigLoader {
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
@@ -1011,7 +1046,6 @@ export class ContainerConfigLoader {
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
container.get<RevokedSessionRepositoryInterface>(TYPES.Auth_RevokedSessionRepository),
container.get<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
@@ -1102,8 +1136,10 @@ export class ContainerConfigLoader {
.bind<EmailSubscriptionUnsubscribedEventHandler>(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)
.toConstantValue(
new EmailSubscriptionUnsubscribedEventHandler(
container.get(TYPES.Auth_UserRepository),
container.get(TYPES.Auth_SettingService),
container.get<DisableEmailSettingBasedOnEmailSubscription>(
TYPES.Auth_DisableEmailSettingBasedOnEmailSubscription,
),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
@@ -1201,8 +1237,12 @@ export class ContainerConfigLoader {
container
.bind<DomainEventMessageHandlerInterface>(TYPES.Auth_DomainEventMessageHandler)
.toConstantValue(
env.get('NEW_RELIC_ENABLED', true) === 'true'
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Auth_Logger))
isConfiguredForAWSProduction
? new SQSXRayEventMessageHandler(
ServiceIdentifier.NAMES.AuthWorker,
eventHandlers,
container.get(TYPES.Auth_Logger),
)
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Auth_Logger)),
)

View File

@@ -161,6 +161,7 @@ const TYPES = {
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'),

View File

@@ -6,7 +6,6 @@ import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSession
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { RemoveSharedVaultUser } from '../UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
@@ -14,7 +13,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
private sessionRepository: SessionRepositoryInterface,
private ephemeralSessionRepository: EphemeralSessionRepositoryInterface,
private revokedSessionRepository: RevokedSessionRepositoryInterface,
private removeSharedVaultUser: RemoveSharedVaultUser,
private logger: Logger,
) {}
@@ -37,13 +35,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
await this.removeSessions(userUuid.value)
const result = await this.removeSharedVaultUser.execute({
userUuid: userUuid.value,
})
if (result.isFailed()) {
this.logger.error(`Could not remove shared vault user: ${result.getError()}`)
}
await this.userRepository.remove(user)
this.logger.info(`Finished account cleanup for user: ${userUuid.value}`)

View File

@@ -1,117 +0,0 @@
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,50 +1,21 @@
import { EmailLevel, Username } from '@standardnotes/domain-core'
import { DomainEventHandlerInterface, EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
import { SettingName } from '@standardnotes/settings'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { Logger } from 'winston'
import { DisableEmailSettingBasedOnEmailSubscription } from '../UseCase/DisableEmailSettingBasedOnEmailSubscription/DisableEmailSettingBasedOnEmailSubscription'
export class EmailSubscriptionUnsubscribedEventHandler implements DomainEventHandlerInterface {
constructor(
private userRepository: UserRepositoryInterface,
private settingsService: SettingServiceInterface,
private disableEmailSettingBasedOnEmailSubscription: DisableEmailSettingBasedOnEmailSubscription,
private logger: Logger,
) {}
async handle(event: EmailSubscriptionUnsubscribedEvent): Promise<void> {
const usernameOrError = Username.create(event.payload.userEmail)
if (usernameOrError.isFailed()) {
return
}
const username = usernameOrError.getValue()
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,
},
const result = await this.disableEmailSettingBasedOnEmailSubscription.execute({
userEmail: event.payload.userEmail,
level: event.payload.level,
})
}
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}`)
if (result.isFailed()) {
this.logger.error(`Failed to disable email setting for user ${event.payload.userEmail}: ${result.getError()}`)
}
}
}

View File

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

View File

@@ -1,14 +1,17 @@
import 'reflect-metadata'
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
import { CreateValetToken } from './CreateValetToken'
import { TransitionStatus } from '@standardnotes/domain-core'
import { TimerInterface } from '@standardnotes/time'
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
import { CreateValetToken } from './CreateValetToken'
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>
@@ -20,6 +23,7 @@ describe('CreateValetToken', () => {
let regularSubscription: UserSubscription
let sharedSubscription: UserSubscription
let user: User
let transitionStatusRepository: TransitionStatusRepositoryInterface
const createUseCase = () =>
new CreateValetToken(
@@ -29,6 +33,7 @@ describe('CreateValetToken', () => {
userSubscriptionService,
timer,
valetTokenTTL,
transitionStatusRepository,
)
beforeEach(() => {
@@ -66,6 +71,11 @@ 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 () => {
@@ -166,6 +176,7 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: undefined,
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{
@@ -206,6 +217,7 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: '2-3-4',
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{
@@ -266,6 +278,7 @@ describe('CreateValetToken', () => {
{
sharedSubscriptionUuid: undefined,
regularSubscriptionUuid: '1-2-3',
ongoingTransition: false,
permittedOperation: 'write',
permittedResources: [
{

View File

@@ -13,6 +13,8 @@ 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 {
@@ -25,6 +27,8 @@ 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> {
@@ -83,6 +87,8 @@ 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,
@@ -91,6 +97,7 @@ 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

@@ -0,0 +1,96 @@
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

@@ -0,0 +1,72 @@
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

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

View File

@@ -84,14 +84,11 @@ describe('Register', () => {
expect(settingService.applyDefaultSettingsUponRegistration).toHaveBeenCalled()
})
it('should register a new user with default role and transition role', async () => {
it('should register a new user with default set of roles', async () => {
const role = new Role()
role.name = RoleName.NAMES.CoreUser
const transitionRole = new Role()
transitionRole.name = RoleName.NAMES.TransitionUser
roleRepository.findOneByName = jest.fn().mockReturnValueOnce(role).mockReturnValueOnce(transitionRole)
roleRepository.findOneByName = jest.fn().mockReturnValueOnce(role)
expect(
await createUseCase().execute({
@@ -120,7 +117,7 @@ describe('Register', () => {
version: '004',
createdAt: new Date(1),
updatedAt: new Date(1),
roles: Promise.resolve([role, transitionRole]),
roles: Promise.resolve([role]),
})
})

View File

@@ -77,10 +77,6 @@ export class Register implements UseCaseInterface {
if (defaultRole) {
roles.push(defaultRole)
}
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
if (transitionRole) {
roles.push(transitionRole)
}
user.roles = Promise.resolve(roles)
Object.assign(user, registrationFields)

View File

@@ -15,7 +15,7 @@ describe('TraceSession', () => {
beforeEach(() => {
sessionTraceRepository = {} as jest.Mocked<SessionTraceRepositoryInterface>
sessionTraceRepository.findOneByUserUuidAndDate = jest.fn().mockReturnValue(null)
sessionTraceRepository.save = jest.fn()
sessionTraceRepository.insert = 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.save).toHaveBeenCalledTimes(1)
expect(sessionTraceRepository.insert).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.save).not.toHaveBeenCalled()
expect(sessionTraceRepository.insert).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.save).not.toHaveBeenCalled()
expect(sessionTraceRepository.insert).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.save).not.toHaveBeenCalled()
expect(sessionTraceRepository.insert).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.save).not.toHaveBeenCalled()
expect(sessionTraceRepository.insert).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.save).not.toHaveBeenCalled()
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
mock.mockRestore()
})

View File

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

View File

@@ -2,10 +2,12 @@ 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()
@@ -39,6 +41,13 @@ 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,18 +1,16 @@
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(
@inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
@inject(TYPES.Auth_Logger) logger: Logger,
tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
isConfiguredForAWSProduction: boolean,
logger: Logger,
) {
super(tokenDecoder, logger)
super(tokenDecoder, isConfiguredForAWSProduction, logger)
}
protected override handleMissingToken(request: Request, _response: Response, next: NextFunction): boolean {

View File

@@ -1,18 +1,16 @@
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(
@inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
@inject(TYPES.Auth_Logger) logger: Logger,
tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
isConfiguredForAWSProduction: boolean,
logger: Logger,
) {
super(tokenDecoder, logger)
super(tokenDecoder, isConfiguredForAWSProduction, 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 ttl2Hourse = 7_200
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl2Hourse, status.value)
const ttl24Hours = 86_400
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl24Hours, status.value)
break
}
}

View File

@@ -1,5 +1,7 @@
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'
@@ -8,13 +10,14 @@ 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: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
})
.andWhere('trace.subscription_plan_name = :subscriptionPlanName', {
subscriptionPlanName: subscriptionPlanName.value,
@@ -26,7 +29,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
return this.ormRepository
.createQueryBuilder('trace')
.where('trace.creation_date = :creationDate', {
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
})
.getCount()
}
@@ -44,7 +47,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
.createQueryBuilder('trace')
.where('trace.user_uuid = :userUuid AND trace.creation_date = :creationDate', {
userUuid: userUuid.value,
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
})
.getOne()
@@ -55,9 +58,9 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
return this.mapper.toDomain(typeOrm)
}
async save(sessionTrace: SessionTrace): Promise<void> {
async insert(sessionTrace: SessionTrace): Promise<void> {
const persistence = this.mapper.toProjection(sessionTrace)
await this.ormRepository.save(persistence)
await this.ormRepository.insert(persistence)
}
}

View File

@@ -1,8 +1,11 @@
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()) {
@@ -50,11 +53,7 @@ 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(
domain.props.createdAt.getFullYear(),
domain.props.createdAt.getMonth(),
domain.props.createdAt.getDate(),
)
typeOrm.creationDate = new Date(this.timer.convertDateToFormattedString(domain.props.createdAt, 'YYYY-MM-DD'))
typeOrm.expiresAt = domain.props.expiresAt
return typeOrm

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.51.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.4...@standardnotes/common@1.51.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.50.4](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.3...@standardnotes/common@1.50.4) (2023-09-04)
**Note:** Version bump only for package @standardnotes/common

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/common",
"version": "1.50.4",
"version": "1.51.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"devDependencies": {
"@types/jest": "^29.5.1",

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.1...@standardnotes/domain-core@1.34.2) (2023-10-04)
### Bug Fixes
* identifying services in workers ([eab78b3](https://github.com/standardnotes/server/commit/eab78b3a95ec82cb779d069d172169bfa92368c9))
## [1.34.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.0...@standardnotes/domain-core@1.34.1) (2023-09-27)
### Bug Fixes
* removing items in a vault and notifying about designated survivor ([#855](https://github.com/standardnotes/server/issues/855)) ([1d06ffe](https://github.com/standardnotes/server/commit/1d06ffe9d51722ada7baa040e1d5ed351fc28f39))
# [1.34.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.2...@standardnotes/domain-core@1.34.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.33.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.1...@standardnotes/domain-core@1.33.2) (2023-09-25)
### Bug Fixes
* **domain-core:** notification paylod to string casting ([a02a287](https://github.com/standardnotes/server/commit/a02a28774b6d500200043faefb9ebac3719e7661))
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.0...@standardnotes/domain-core@1.33.1) (2023-09-25)
### Bug Fixes
* refactor the structure of notifications ([#853](https://github.com/standardnotes/server/issues/853)) ([cebab59](https://github.com/standardnotes/server/commit/cebab59a026c6868886e0945787a8ddb0442fbc3))
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.32.0...@standardnotes/domain-core@1.33.0) (2023-09-21)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-core",
"version": "1.33.0",
"version": "1.34.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -21,7 +21,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest --coverage --no-cache --passWithNoTests"
},
"dependencies": {
"uuid": "^9.0.0"

View File

@@ -4,6 +4,7 @@ import { Result } from '../Core/Result'
import { NotificationPayloadProps } from './NotificationPayloadProps'
import { NotificationType } from './NotificationType'
import { Uuid } from '../Common/Uuid'
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
private constructor(props: NotificationPayloadProps) {
@@ -14,8 +15,10 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
return JSON.stringify({
version: this.props.version,
type: this.props.type.value,
sharedVaultUuid: this.props.sharedVaultUuid.value,
itemUuid: this.props.itemUuid ? this.props.itemUuid.value : undefined,
primaryIdentifier: this.props.primaryIdentifier.value,
primaryIndentifierType: this.props.primaryIndentifierType.value,
secondaryIdentifier: this.props.secondaryIdentifier?.value,
secondaryIdentifierType: this.props.secondaryIdentifierType?.value,
})
}
@@ -29,26 +32,43 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
}
const type = typeOrError.getValue()
const sharedVaultUuidOrError = Uuid.create(props.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail<NotificationPayload>(sharedVaultUuidOrError.getError())
const primaryIdentifierOrError = Uuid.create(props.primaryIdentifier)
if (primaryIdentifierOrError.isFailed()) {
return Result.fail<NotificationPayload>(primaryIdentifierOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const primaryIdentifier = primaryIdentifierOrError.getValue()
let itemUuid: Uuid | undefined = undefined
if (props.itemUuid) {
const itemUuidOrError = Uuid.create(props.itemUuid)
if (itemUuidOrError.isFailed()) {
return Result.fail<NotificationPayload>(itemUuidOrError.getError())
const primaryIndentifierTypeOrError = NotificationPayloadIdentifierType.create(props.primaryIndentifierType)
if (primaryIndentifierTypeOrError.isFailed()) {
return Result.fail<NotificationPayload>(primaryIndentifierTypeOrError.getError())
}
const primaryIndentifierType = primaryIndentifierTypeOrError.getValue()
let secondaryIdentifier: Uuid | undefined
if (props.secondaryIdentifier) {
const secondaryIdentifierOrError = Uuid.create(props.secondaryIdentifier)
if (secondaryIdentifierOrError.isFailed()) {
return Result.fail<NotificationPayload>(secondaryIdentifierOrError.getError())
}
itemUuid = itemUuidOrError.getValue()
secondaryIdentifier = secondaryIdentifierOrError.getValue()
}
let secondaryIdentifierType: NotificationPayloadIdentifierType | undefined
if (props.secondaryIdentifierType) {
const secondaryIdentifierTypeOrError = NotificationPayloadIdentifierType.create(props.secondaryIdentifierType)
if (secondaryIdentifierTypeOrError.isFailed()) {
return Result.fail<NotificationPayload>(secondaryIdentifierTypeOrError.getError())
}
secondaryIdentifierType = secondaryIdentifierTypeOrError.getValue()
}
return NotificationPayload.create({
version: props.version,
type,
sharedVaultUuid,
itemUuid,
primaryIdentifier,
primaryIndentifierType,
secondaryIdentifier,
secondaryIdentifierType,
})
} catch (error) {
return Result.fail<NotificationPayload>((error as Error).message)
@@ -57,7 +77,7 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
static create(props: NotificationPayloadProps): Result<NotificationPayload> {
if (
props.itemUuid === undefined &&
props.secondaryIdentifier === undefined &&
props.type.equals(NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue())
) {
return Result.fail<NotificationPayload>(

View File

@@ -0,0 +1,28 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { NotificationPayloadIdentifierTypeProps } from './NotificationPayloadIdentifierTypeProps'
export class NotificationPayloadIdentifierType extends ValueObject<NotificationPayloadIdentifierTypeProps> {
static readonly TYPES = {
SharedVaultUuid: 'shared_vault_uuid',
UserUuid: 'user_uuid',
SharedVaultInviteUuid: 'shared_vault_invite_uuid',
ItemUuid: 'item_uuid',
}
private constructor(props: NotificationPayloadIdentifierTypeProps) {
super(props)
}
get value(): string {
return this.props.value
}
static create(type: string): Result<NotificationPayloadIdentifierType> {
if (!Object.values(this.TYPES).includes(type)) {
return Result.fail<NotificationPayloadIdentifierType>(`Invalid notification payload identifier type: ${type}`)
}
return Result.ok<NotificationPayloadIdentifierType>(new NotificationPayloadIdentifierType({ value: type }))
}
}

View File

@@ -0,0 +1,3 @@
export interface NotificationPayloadIdentifierTypeProps {
value: string
}

View File

@@ -1,9 +1,12 @@
import { Uuid } from '../Common/Uuid'
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
import { NotificationType } from './NotificationType'
export interface NotificationPayloadProps {
type: NotificationType
sharedVaultUuid: Uuid
primaryIdentifier: Uuid
primaryIndentifierType: NotificationPayloadIdentifierType
secondaryIdentifier?: Uuid
secondaryIdentifierType?: NotificationPayloadIdentifierType
version: string
itemUuid?: Uuid
}

View File

@@ -7,8 +7,9 @@ export class NotificationType extends ValueObject<NotificationTypeProps> {
SharedVaultItemRemoved: 'shared_vault_item_removed',
SelfRemovedFromSharedVault: 'self_removed_from_shared_vault',
UserRemovedFromSharedVault: 'user_removed_from_shared_vault',
UserDesignatedAsSurvivor: 'user_designated_as_survivor',
UserAddedToSharedVault: 'user_added_to_shared_vault',
SharedVaultInviteDeclined: 'shared_vault_invite_declined',
SharedVaultInviteCanceled: 'shared_vault_invite_canceled',
SharedVaultFileUploaded: 'shared_vault_file_uploaded',
SharedVaultFileRemoved: 'shared_vault_file_removed',
}

View File

@@ -6,9 +6,13 @@ export class ServiceIdentifier extends ValueObject<ServiceIdentifierProps> {
static readonly NAMES = {
ApiGateway: 'ApiGateway',
Auth: 'Auth',
AuthWorker: 'AuthWorker',
SyncingServer: 'SyncingServer',
SyncingServerWorker: 'SyncingServerWorker',
Revisions: 'Revisions',
RevisionsWorker: 'RevisionsWorker',
Files: 'Files',
FilesWorker: 'FilesWorker',
}
get value(): string {

View File

@@ -48,6 +48,8 @@ export * from './Env/AbstractEnv'
export * from './Mapping/MapperInterface'
export * from './Notification/NotificationPayload'
export * from './Notification/NotificationPayloadIdentifierType'
export * from './Notification/NotificationPayloadIdentifierTypeProps'
export * from './Notification/NotificationPayloadProps'
export * from './Notification/NotificationType'
export * from './Notification/NotificationTypeProps'

View File

@@ -3,6 +3,60 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.6](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.5...@standardnotes/domain-events-infra@1.14.6) (2023-10-05)
### Bug Fixes
* **domain-events-infra:** setting user metadata on workers ([c4b6f17](https://github.com/standardnotes/server/commit/c4b6f17ebcfe7bd77b6741f881a0d1f13ba809a4))
## [1.14.5](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.4...@standardnotes/domain-events-infra@1.14.5) (2023-10-04)
### Bug Fixes
* identifying services in workers ([eab78b3](https://github.com/standardnotes/server/commit/eab78b3a95ec82cb779d069d172169bfa92368c9))
## [1.14.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.3...@standardnotes/domain-events-infra@1.14.4) (2023-10-04)
### Bug Fixes
* **domain-events-infra:** subsegment name ([ba7cbb9](https://github.com/standardnotes/server/commit/ba7cbb989ba3592ea0c13fc39f85e314517fbb2d))
## [1.14.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.2...@standardnotes/domain-events-infra@1.14.3) (2023-10-04)
### Bug Fixes
* **domain-events-infra:** handling segments ([207ef9f](https://github.com/standardnotes/server/commit/207ef9f3e531b730f3f4869fb128354dbd659f46))
* **domain-events-infra:** remove redundant flush ([64f1fe5](https://github.com/standardnotes/server/commit/64f1fe59c23894bda9ad40c6bdeaf2997a8b48ce))
## [1.14.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.1...@standardnotes/domain-events-infra@1.14.2) (2023-10-04)
### Bug Fixes
* **domain-events-infra:** handling async functions ([f6bc1c3](https://github.com/standardnotes/server/commit/f6bc1c30846ecfb159f7fb1cdd8bfc3ab15b2dde))
* **domain-events-infra:** imports ([7668713](https://github.com/standardnotes/server/commit/7668713dd6f65b65e546152828f5f52ea34f8bf5))
## [1.14.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.14.0...@standardnotes/domain-events-infra@1.14.1) (2023-10-03)
### Bug Fixes
* **domain-events-infra:** change segment closing ([e066b6a](https://github.com/standardnotes/server/commit/e066b6a126754e5b1ea73b8ef5ce9068dc424d77))
# [1.14.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.13.1...@standardnotes/domain-events-infra@1.14.0) (2023-10-03)
### Features
* add xray segment tracing on auth-worker ([b1b244a](https://github.com/standardnotes/server/commit/b1b244a2cf1e17ddf67fc9b238b4b25a1bc5a190))
## [1.13.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.13.0...@standardnotes/domain-events-infra@1.13.1) (2023-09-28)
**Note:** Version bump only for package @standardnotes/domain-events-infra
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.34...@standardnotes/domain-events-infra@1.13.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.12.34](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.33...@standardnotes/domain-events-infra@1.12.34) (2023-09-25)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.12.34",
"version": "1.14.6",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -21,12 +21,13 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"dependencies": {
"@aws-sdk/client-sns": "^3.332.0",
"@aws-sdk/client-sqs": "^3.332.0",
"@standardnotes/domain-events": "workspace:*",
"aws-xray-sdk": "^3.5.2",
"ioredis": "^5.2.4",
"reflect-metadata": "^0.1.13",
"sqs-consumer": "^6.2.1",

View File

@@ -0,0 +1,60 @@
import { Logger } from 'winston'
import * as zlib from 'zlib'
import { Segment, Subsegment, captureAsyncFunc } from 'aws-xray-sdk'
import {
DomainEventHandlerInterface,
DomainEventInterface,
DomainEventMessageHandlerInterface,
} from '@standardnotes/domain-events'
export class SQSXRayEventMessageHandler implements DomainEventMessageHandlerInterface {
constructor(
private serviceName: string,
private handlers: Map<string, DomainEventHandlerInterface>,
private logger: Logger,
) {}
async handleMessage(message: string): Promise<void> {
const messageParsed = JSON.parse(message)
const domainEventJson = zlib.unzipSync(Buffer.from(messageParsed.Message, 'base64')).toString()
const domainEvent: DomainEventInterface = JSON.parse(domainEventJson)
domainEvent.createdAt = new Date(domainEvent.createdAt)
const handler = this.handlers.get(domainEvent.type)
if (!handler) {
this.logger.debug(`Event handler for event type ${domainEvent.type} does not exist`)
return
}
this.logger.debug(`Received event: ${domainEvent.type}`)
const xRaySegment = new Segment(this.serviceName)
if (domainEvent.meta.correlation.userIdentifierType === 'uuid') {
xRaySegment.addMetadata('userUuid', domainEvent.meta.correlation.userIdentifier)
}
await captureAsyncFunc(
domainEvent.type,
async (subsegment?: Subsegment) => {
await handler.handle(domainEvent)
if (subsegment) {
subsegment.close()
}
},
xRaySegment,
)
xRaySegment.close()
}
async handleError(error: Error): Promise<void> {
this.logger.error('Error occured while handling SQS message: %O', error)
}
}

View File

@@ -12,3 +12,4 @@ export * from './SQS/SQSNewRelicBounceNotificiationHandler'
export * from './SQS/SQSDomainEventSubscriberFactory'
export * from './SQS/SQSEventMessageHandler'
export * from './SQS/SQSNewRelicEventMessageHandler'
export * from './SQS/SQSXRayEventMessageHandler'

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.131.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.131.0...@standardnotes/domain-events@2.131.1) (2023-09-28)
**Note:** Version bump only for package @standardnotes/domain-events
# [2.131.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.130.0...@standardnotes/domain-events@2.131.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.130.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.1...@standardnotes/domain-events@2.130.0) (2023-09-25)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.130.0",
"version": "2.131.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest --coverage --no-cache --passWithNoTests"
},
"dependencies": {
"@standardnotes/predicates": "workspace:*",

View File

@@ -3,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.9](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.8...@standardnotes/event-store@1.12.9) (2023-10-05)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.8](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.7...@standardnotes/event-store@1.12.8) (2023-10-04)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.7](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.6...@standardnotes/event-store@1.12.7) (2023-10-04)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.6](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.5...@standardnotes/event-store@1.12.6) (2023-10-04)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.5](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.4...@standardnotes/event-store@1.12.5) (2023-10-04)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.3...@standardnotes/event-store@1.12.4) (2023-10-03)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.2...@standardnotes/event-store@1.12.3) (2023-10-03)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.1...@standardnotes/event-store@1.12.2) (2023-09-28)
**Note:** Version bump only for package @standardnotes/event-store
## [1.12.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.0...@standardnotes/event-store@1.12.1) (2023-09-27)
**Note:** Version bump only for package @standardnotes/event-store
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.52...@standardnotes/event-store@1.12.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.11.52](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.51...@standardnotes/event-store@1.11.52) (2023-09-25)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.51](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.50...@standardnotes/event-store@1.11.51) (2023-09-25)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.50](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.49...@standardnotes/event-store@1.11.50) (2023-09-25)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.11.50",
"version": "1.12.9",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",
@@ -13,7 +13,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js"
},
"author": "Karol Sójko <karol@standardnotes.com>",

View File

@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.26.0...@standardnotes/files-server@1.26.1) (2023-10-05)
**Note:** Version bump only for package @standardnotes/files-server
# [1.26.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.6...@standardnotes/files-server@1.26.0) (2023-10-04)
### Features
* add xray to syncing server and files ([6583ff6](https://github.com/standardnotes/files/commit/6583ff6cd90f7881c1a79c0f904f1b1db96fc5b3))
## [1.25.6](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.5...@standardnotes/files-server@1.25.6) (2023-10-04)
**Note:** Version bump only for package @standardnotes/files-server
## [1.25.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.4...@standardnotes/files-server@1.25.5) (2023-10-04)
**Note:** Version bump only for package @standardnotes/files-server
## [1.25.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.3...@standardnotes/files-server@1.25.4) (2023-10-04)
**Note:** Version bump only for package @standardnotes/files-server
## [1.25.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.2...@standardnotes/files-server@1.25.3) (2023-10-04)
**Note:** Version bump only for package @standardnotes/files-server
## [1.25.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.1...@standardnotes/files-server@1.25.2) (2023-10-03)
**Note:** Version bump only for package @standardnotes/files-server
## [1.25.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.25.0...@standardnotes/files-server@1.25.1) (2023-10-03)
**Note:** Version bump only for package @standardnotes/files-server
# [1.25.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.24.1...@standardnotes/files-server@1.25.0) (2023-09-28)
### Features
* block file operations during transition ([#856](https://github.com/standardnotes/files/issues/856)) ([676cf36](https://github.com/standardnotes/files/commit/676cf36f8d769aa433880b2800f0457d06fbbf14))
## [1.24.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.24.0...@standardnotes/files-server@1.24.1) (2023-09-27)
**Note:** Version bump only for package @standardnotes/files-server
# [1.24.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.2...@standardnotes/files-server@1.24.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/files/issues/854)) ([ca6dbc0](https://github.com/standardnotes/files/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.23.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.1...@standardnotes/files-server@1.23.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/files-server
## [1.23.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.0...@standardnotes/files-server@1.23.1) (2023-09-25)
**Note:** Version bump only for package @standardnotes/files-server
# [1.23.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.28...@standardnotes/files-server@1.23.0) (2023-09-25)
### Features

View File

@@ -10,6 +10,7 @@ import helmet from 'helmet'
import * as cors from 'cors'
import { urlencoded, json, raw, Request, Response, NextFunction } from 'express'
import * as winston from 'winston'
import * as AWSXRay from 'aws-xray-sdk'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const robots = require('express-robots-txt')
@@ -17,15 +18,27 @@ 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) => {
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.Files))
}
app.use((_request: Request, response: Response, next: NextFunction) => {
response.setHeader('X-Files-Version', container.get(TYPES.Files_VERSION))
next()
@@ -90,6 +103,10 @@ 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

@@ -8,6 +8,7 @@ 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()
void container.load().then((container) => {
@@ -16,6 +17,14 @@ 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.Files_Logger)
logger.info('Starting worker...')

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.23.0",
"version": "1.26.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -22,7 +22,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --fix --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js"
},
@@ -37,6 +37,7 @@
"@standardnotes/sncrypto-common": "^1.13.4",
"@standardnotes/sncrypto-node": "workspace:*",
"@standardnotes/time": "workspace:*",
"aws-xray-sdk": "^3.5.2",
"connect-busboy": "^1.0.0",
"cors": "^2.8.5",
"dayjs": "^1.11.6",

View File

@@ -1,5 +1,6 @@
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 { S3Client, S3ClientConfig } from '@aws-sdk/client-s3'
@@ -19,7 +20,7 @@ import {
SNSDomainEventPublisher,
SQSDomainEventSubscriberFactory,
SQSEventMessageHandler,
SQSNewRelicEventMessageHandler,
SQSXRayEventMessageHandler,
} from '@standardnotes/domain-events-infra'
import { StreamDownloadFile } from '../Domain/UseCase/StreamDownloadFile/StreamDownloadFile'
import { FileDownloaderInterface } from '../Domain/Services/FileDownloaderInterface'
@@ -53,6 +54,7 @@ import { S3FileMover } from '../Infra/S3/S3FileMover'
import { FSFileMover } from '../Infra/FS/FSFileMover'
import { MoveFile } from '../Domain/UseCase/MoveFile/MoveFile'
import { SharedVaultValetTokenAuthMiddleware } from '../Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware'
import { ServiceIdentifier } from '@standardnotes/domain-core'
export class ContainerConfigLoader {
async load(configuration?: {
@@ -83,7 +85,9 @@ export class ContainerConfigLoader {
.toConstantValue(env.get('FILE_UPLOAD_PATH', true) ?? `${__dirname}/../../uploads`)
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
const isConfiguredForAWSProduction = !isConfiguredForHomeServer && !isConfiguredForSelfHosting
let logger: winston.Logger
if (configuration?.logger) {
@@ -149,7 +153,11 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
}
}
container.bind<SNSClient>(TYPES.Files_SNS).toConstantValue(new SNSClient(snsConfig))
let snsClient = new SNSClient(snsConfig)
if (isConfiguredForAWSProduction) {
snsClient = captureAWSv3Client(snsClient)
}
container.bind<SNSClient>(TYPES.Files_SNS).toConstantValue(snsClient)
}
if (env.get('SQS_QUEUE_URL', true)) {
@@ -165,7 +173,11 @@ export class ContainerConfigLoader {
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
}
}
container.bind<SQSClient>(TYPES.Files_SQS).toConstantValue(new SQSClient(sqsConfig))
let sqsClient = new SQSClient(sqsConfig)
if (isConfiguredForAWSProduction) {
sqsClient = captureAWSv3Client(sqsClient)
}
container.bind<SQSClient>(TYPES.Files_SQS).toConstantValue(sqsClient)
}
container
@@ -185,7 +197,10 @@ export class ContainerConfigLoader {
if (env.get('S3_ENDPOINT', true)) {
s3Opts.endpoint = env.get('S3_ENDPOINT', true)
}
const s3Client = new S3Client(s3Opts)
let s3Client = new S3Client(s3Opts)
if (isConfiguredForAWSProduction) {
s3Client = captureAWSv3Client(s3Client)
}
container.bind<S3Client>(TYPES.Files_S3).toConstantValue(s3Client)
container.bind<FileDownloaderInterface>(TYPES.Files_FileDownloader).to(S3FileDownloader)
container.bind<FileUploaderInterface>(TYPES.Files_FileUploader).to(S3FileUploader)
@@ -198,7 +213,9 @@ export class ContainerConfigLoader {
.toConstantValue(
new FSFileUploader(container.get(TYPES.Files_FILE_UPLOAD_PATH), container.get(TYPES.Files_Logger)),
)
container.bind<FileRemoverInterface>(TYPES.Files_FileRemover).to(FSFileRemover)
container
.bind<FileRemoverInterface>(TYPES.Files_FileRemover)
.toConstantValue(new FSFileRemover(container.get<string>(TYPES.Files_FILE_UPLOAD_PATH)))
container.bind<FileMoverInterface>(TYPES.Files_FileMover).to(FSFileMover)
}
@@ -247,12 +264,26 @@ export class ContainerConfigLoader {
// Handlers
container
.bind<AccountDeletionRequestedEventHandler>(TYPES.Files_AccountDeletionRequestedEventHandler)
.to(AccountDeletionRequestedEventHandler)
.toConstantValue(
new AccountDeletionRequestedEventHandler(
container.get<MarkFilesToBeRemoved>(TYPES.Files_MarkFilesToBeRemoved),
container.get<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Files_DomainEventFactory),
container.get<winston.Logger>(TYPES.Files_Logger),
),
)
container
.bind<SharedSubscriptionInvitationCanceledEventHandler>(
TYPES.Files_SharedSubscriptionInvitationCanceledEventHandler,
)
.to(SharedSubscriptionInvitationCanceledEventHandler)
.toConstantValue(
new SharedSubscriptionInvitationCanceledEventHandler(
container.get<MarkFilesToBeRemoved>(TYPES.Files_MarkFilesToBeRemoved),
container.get<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Files_DomainEventFactory),
container.get<winston.Logger>(TYPES.Files_Logger),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Files_AccountDeletionRequestedEventHandler)],
@@ -276,7 +307,11 @@ export class ContainerConfigLoader {
.bind<DomainEventMessageHandlerInterface>(TYPES.Files_DomainEventMessageHandler)
.toConstantValue(
env.get('NEW_RELIC_ENABLED', true) === 'true'
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Files_Logger))
? new SQSXRayEventMessageHandler(
ServiceIdentifier.NAMES.FilesWorker,
eventHandlers,
container.get(TYPES.Files_Logger),
)
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Files_Logger)),
)
container

View File

@@ -1,5 +1,5 @@
export type RemovedFileDescription = {
userUuid: string
userOrSharedVaultUuid: string
filePath: string
fileName: string
fileByteSize: number

View File

@@ -3,18 +3,17 @@ import {
DomainEventHandlerInterface,
DomainEventPublisherInterface,
} from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
@injectable()
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
@inject(TYPES.Files_MarkFilesToBeRemoved) private markFilesToBeRemoved: MarkFilesToBeRemoved,
@inject(TYPES.Files_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
@inject(TYPES.Files_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
private markFilesToBeRemoved: MarkFilesToBeRemoved,
private domainEventPublisher: DomainEventPublisherInterface,
private domainEventFactory: DomainEventFactoryInterface,
private logger: Logger,
) {}
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
@@ -27,16 +26,23 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
})
if (result.isFailed()) {
this.logger.error(`Could not mark files for removal for user ${event.payload.userUuid}: ${result.getError()}`)
return
}
const filesRemoved = result.getValue()
this.logger.debug(`Marked ${filesRemoved.length} files for removal for user ${event.payload.userUuid}`)
for (const fileRemoved of filesRemoved) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createFileRemovedEvent({
regularSubscriptionUuid: event.payload.regularSubscriptionUuid,
...fileRemoved,
userUuid: fileRemoved.userOrSharedVaultUuid,
filePath: fileRemoved.filePath,
fileName: fileRemoved.fileName,
fileByteSize: fileRemoved.fileByteSize,
}),
)
}

View File

@@ -3,18 +3,17 @@ import {
DomainEventHandlerInterface,
DomainEventPublisherInterface,
} from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
@injectable()
export class SharedSubscriptionInvitationCanceledEventHandler implements DomainEventHandlerInterface {
constructor(
@inject(TYPES.Files_MarkFilesToBeRemoved) private markFilesToBeRemoved: MarkFilesToBeRemoved,
@inject(TYPES.Files_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
@inject(TYPES.Files_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
private markFilesToBeRemoved: MarkFilesToBeRemoved,
private domainEventPublisher: DomainEventPublisherInterface,
private domainEventFactory: DomainEventFactoryInterface,
private logger: Logger,
) {}
async handle(event: SharedSubscriptionInvitationCanceledEvent): Promise<void> {
@@ -27,16 +26,25 @@ export class SharedSubscriptionInvitationCanceledEventHandler implements DomainE
})
if (result.isFailed()) {
this.logger.error(
`Could not mark files to be removed for invitee: ${event.payload.inviteeIdentifier}: ${result.getError()}`,
)
return
}
const filesRemoved = result.getValue()
this.logger.debug(`Marked ${filesRemoved.length} files for removal for invitee ${event.payload.inviteeIdentifier}`)
for (const fileRemoved of filesRemoved) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createFileRemovedEvent({
regularSubscriptionUuid: event.payload.inviterSubscriptionUuid,
...fileRemoved,
userUuid: fileRemoved.userOrSharedVaultUuid,
filePath: fileRemoved.filePath,
fileName: fileRemoved.fileName,
fileByteSize: fileRemoved.fileByteSize,
}),
)
}

View File

@@ -29,6 +29,10 @@ export class SharedVaultRemovedEventHandler implements DomainEventHandlerInterfa
const filesRemoved = result.getValue()
this.logger.debug(
`Marked ${filesRemoved.length} files for removal for shared vault ${event.payload.sharedVaultUuid}`,
)
for (const fileRemoved of filesRemoved) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createSharedVaultFileRemovedEvent({

View File

@@ -2,5 +2,5 @@ import { RemovedFileDescription } from '../File/RemovedFileDescription'
export interface FileRemoverInterface {
remove(filePath: string): Promise<number>
markFilesToBeRemoved(userUuid: string): Promise<Array<RemovedFileDescription>>
markFilesToBeRemoved(userOrSharedVaultUuid: string): Promise<Array<RemovedFileDescription>>
}

View File

@@ -1,18 +1,42 @@
import { inject, injectable } from 'inversify'
import { promises } from 'fs'
import { FileRemoverInterface } from '../../Domain/Services/FileRemoverInterface'
import { RemovedFileDescription } from '../../Domain/File/RemovedFileDescription'
import TYPES from '../../Bootstrap/Types'
@injectable()
export class FSFileRemover implements FileRemoverInterface {
constructor(@inject(TYPES.Files_FILE_UPLOAD_PATH) private fileUploadPath: string) {}
constructor(private fileUploadPath: string) {}
async markFilesToBeRemoved(userUuid: string): Promise<Array<RemovedFileDescription>> {
await promises.rmdir(`${this.fileUploadPath}/${userUuid}`)
async markFilesToBeRemoved(userOrSharedVaultUuid: string): Promise<Array<RemovedFileDescription>> {
const removedFileDescriptions: RemovedFileDescription[] = []
return []
let directoryExists: boolean
try {
await promises.access(`${this.fileUploadPath}/${userOrSharedVaultUuid}`)
directoryExists = true
} catch (error) {
directoryExists = false
}
if (!directoryExists) {
return []
}
const files = await promises.readdir(`${this.fileUploadPath}/${userOrSharedVaultUuid}`, { withFileTypes: true })
for (const file of files) {
const filePath = `${this.fileUploadPath}/${userOrSharedVaultUuid}/${file.name}`
const fileByteSize = await this.remove(`${userOrSharedVaultUuid}/${file.name}`)
removedFileDescriptions.push({
filePath,
fileByteSize,
userOrSharedVaultUuid,
fileName: file.name,
})
}
return removedFileDescriptions
}
async remove(filePath: string): Promise<number> {

View File

@@ -13,6 +13,7 @@ describe('ValetTokenAuthMiddleware', () => {
const logger = {
debug: jest.fn(),
error: jest.fn(),
} as unknown as jest.Mocked<Logger>
const createMiddleware = () => new ValetTokenAuthMiddleware(tokenDecoder, logger)
@@ -222,4 +223,27 @@ describe('ValetTokenAuthMiddleware', () => {
expect(next).toHaveBeenCalledWith(error)
})
it('should throw an error if the valet token indicates an ongoing transition', async () => {
request.headers['x-valet-token'] = 'valet-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 30,
},
],
permittedOperation: 'write',
uploadBytesLimit: -1,
uploadBytesUsed: 80,
ongoingTransition: true,
})
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(500)
expect(next).not.toHaveBeenCalled()
})
})

View File

@@ -46,6 +46,19 @@ export class ValetTokenAuthMiddleware extends BaseMiddleware {
return
}
if (valetTokenData.ongoingTransition === true) {
this.logger.error(`Cannot perform file operations for user ${valetTokenData.userUuid} during transition`)
response.status(500).send({
error: {
tag: 'ongoing-transition',
message: 'Cannot perform file operations during transition',
},
})
return
}
for (const resource of valetTokenData.permittedResources) {
const resourceUuidOrError = Uuid.create(resource.remoteIdentifier)
if (resourceUuidOrError.isFailed()) {

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