Compare commits

..

54 Commits

Author SHA1 Message Date
standardci
c0cb7f7a92 chore(release): publish new version
- @standardnotes/api-gateway@1.31.1
 - @standardnotes/auth-server@1.45.0
 - @standardnotes/domain-events-infra@1.8.27
 - @standardnotes/domain-events@2.68.0
 - @standardnotes/event-store@1.4.6
 - @standardnotes/files-server@1.6.18
 - @standardnotes/scheduler-server@1.10.46
 - @standardnotes/syncing-server@1.9.8
 - @standardnotes/websockets-server@1.1.3
 - @standardnotes/workspace-server@1.14.0
2022-10-13 10:52:01 +00:00
Karol Sójko
86379eb96d feat: publish workspace invite accepted event for websockets 2022-10-13 12:50:10 +02:00
standardci
f7762a97e3 chore(release): publish new version
- @standardnotes/api-gateway@1.31.0
 - @standardnotes/auth-server@1.44.0
2022-10-13 10:25:22 +00:00
Karol Sójko
86ae4a59a3 feat(auth): remove websocket handling in favor of websockets service 2022-10-13 12:23:14 +02:00
standardci
863e8555ae chore(release): publish new version
- @standardnotes/websockets-server@1.1.2
2022-10-13 10:11:43 +00:00
Karol Sójko
4e21edce6b fix(websockets): add http client binding 2022-10-13 12:09:20 +02:00
standardci
5663841145 chore(release): publish new version
- @standardnotes/websockets-server@1.1.1
2022-10-13 09:59:16 +00:00
Karol Sójko
2f7ef497ab fix(websockets): remove unnecessary sns bindings 2022-10-13 11:57:09 +02:00
standardci
ace63cfcc1 chore(release): publish new version
- @standardnotes/api-gateway@1.30.1
 - @standardnotes/auth-server@1.43.1
 - @standardnotes/common@1.40.0
 - @standardnotes/domain-events-infra@1.8.26
 - @standardnotes/domain-events@2.67.0
 - @standardnotes/event-store@1.4.5
 - @standardnotes/files-server@1.6.17
 - @standardnotes/predicates@1.4.11
 - @standardnotes/scheduler-server@1.10.45
 - @standardnotes/security@1.4.9
 - @standardnotes/syncing-server@1.9.7
 - @standardnotes/websockets-server@1.1.0
 - @standardnotes/workspace-server@1.13.2
2022-10-13 09:42:21 +00:00
Karol Sójko
d28c268e86 feat(websockets): add websockets service 2022-10-13 11:40:06 +02:00
standardci
6f43726a3b chore(release): publish new version
- @standardnotes/workspace-server@1.13.1
2022-10-12 13:22:13 +00:00
Karol Sójko
4f6a2a83d3 fix(workspace): add workspace to workspace user foreign keys 2022-10-12 15:20:33 +02:00
standardci
937ce5a157 chore(release): publish new version
- @standardnotes/api-gateway@1.30.0
 - @standardnotes/auth-server@1.43.0
 - @standardnotes/workspace-server@1.13.0
2022-10-12 12:47:21 +00:00
Karol Sójko
0c1a779ef0 feat(workspace): add endpoints for initiating keyshare in a workspace 2022-10-12 14:45:43 +02:00
standardci
e01d1f44d0 chore(release): publish new version
- @standardnotes/workspace-server@1.12.0
2022-10-12 11:02:28 +00:00
Karol Sójko
cea9021c16 feat(workspace): add initiating key share 2022-10-12 13:00:24 +02:00
standardci
3039f58b5a chore(release): publish new version
- @standardnotes/api-gateway@1.29.0
 - @standardnotes/auth-server@1.42.0
 - @standardnotes/workspace-server@1.11.0
2022-10-11 13:30:43 +00:00
Karol Sójko
e2326190d4 fix: add missing dependency 2022-10-11 15:29:03 +02:00
Karol Sójko
095d13f8bb feat: add listin worspaces and workspace users 2022-10-11 15:24:21 +02:00
standardci
1292d1d898 chore(release): publish new version
- @standardnotes/api-gateway@1.28.2
 - @standardnotes/auth-server@1.41.2
 - @standardnotes/common@1.39.0
 - @standardnotes/domain-events-infra@1.8.25
 - @standardnotes/domain-events@2.66.3
 - @standardnotes/event-store@1.4.4
 - @standardnotes/files-server@1.6.16
 - @standardnotes/predicates@1.4.10
 - @standardnotes/scheduler-server@1.10.44
 - @standardnotes/security@1.4.8
 - @standardnotes/syncing-server@1.9.6
 - @standardnotes/workspace-server@1.10.0
2022-10-11 10:01:41 +00:00
Karol Sójko
8bc92616d2 feat(workspace): extract workspace user status to common 2022-10-11 12:00:01 +02:00
standardci
ae45fafaee chore(release): publish new version
- @standardnotes/api-gateway@1.28.1
 - @standardnotes/auth-server@1.41.1
 - @standardnotes/common@1.38.0
 - @standardnotes/domain-events-infra@1.8.24
 - @standardnotes/domain-events@2.66.2
 - @standardnotes/event-store@1.4.3
 - @standardnotes/files-server@1.6.15
 - @standardnotes/predicates@1.4.9
 - @standardnotes/scheduler-server@1.10.43
 - @standardnotes/security@1.4.7
 - @standardnotes/syncing-server@1.9.5
 - @standardnotes/workspace-server@1.9.0
2022-10-11 09:54:42 +00:00
Karol Sójko
f74227067b feat(workspace): add invite access level 2022-10-11 11:52:31 +02:00
standardci
5f76d25ec3 chore(release): publish new version
- @standardnotes/workspace-server@1.8.0
2022-10-11 09:33:00 +00:00
Karol Sójko
ba9d3bfe46 feat(workspace): add workspace user display name 2022-10-11 11:30:49 +02:00
standardci
3dc6babfca chore(release): publish new version
- @standardnotes/workspace-server@1.7.0
2022-10-11 08:02:58 +00:00
Karol Sójko
ace2b6936a feat(workspace): accepting invitation 2022-10-11 10:01:13 +02:00
standardci
712e874bfe chore(release): publish new version
- @standardnotes/api-gateway@1.28.0
 - @standardnotes/auth-server@1.41.0
 - @standardnotes/workspace-server@1.6.0
2022-10-11 07:44:12 +00:00
Karol Sójko
266adda45b feat(workspace): add invite to workspace endpoints 2022-10-11 09:41:45 +02:00
standardci
f5ebe4a69e chore(release): publish new version
- @standardnotes/api-gateway@1.27.4
 - @standardnotes/auth-server@1.40.4
 - @standardnotes/common@1.37.0
 - @standardnotes/domain-events-infra@1.8.23
 - @standardnotes/domain-events@2.66.1
 - @standardnotes/event-store@1.4.2
 - @standardnotes/files-server@1.6.14
 - @standardnotes/predicates@1.4.8
 - @standardnotes/scheduler-server@1.10.42
 - @standardnotes/security@1.4.6
 - @standardnotes/syncing-server@1.9.4
 - @standardnotes/workspace-server@1.5.1
2022-10-10 10:34:40 +00:00
Karol Sójko
15d960d47b feat(common): add WORKSPACE_INVITE_CREATED email message identifier 2022-10-10 12:32:35 +02:00
standardci
f700b04b8f chore(release): publish new version
- @standardnotes/api-gateway@1.27.3
 - @standardnotes/auth-server@1.40.3
 - @standardnotes/domain-events-infra@1.8.22
 - @standardnotes/domain-events@2.66.0
 - @standardnotes/event-store@1.4.1
 - @standardnotes/files-server@1.6.13
 - @standardnotes/scheduler-server@1.10.41
 - @standardnotes/syncing-server@1.9.3
 - @standardnotes/workspace-server@1.5.0
2022-10-10 10:23:57 +00:00
Karol Sójko
6f9683c41a feat(workspace): add publishing workspace invite created 2022-10-10 12:22:19 +02:00
standardci
0ad605c906 chore(release): publish new version
- @standardnotes/api-gateway@1.27.2
 - @standardnotes/auth-server@1.40.2
 - @standardnotes/domain-events-infra@1.8.21
 - @standardnotes/domain-events@2.65.0
 - @standardnotes/event-store@1.4.0
 - @standardnotes/files-server@1.6.12
 - @standardnotes/scheduler-server@1.10.40
 - @standardnotes/syncing-server@1.9.2
 - @standardnotes/workspace-server@1.4.1
2022-10-10 10:09:16 +00:00
Karol Sójko
db4c49c57b feat: add workspace invite created event 2022-10-10 12:07:23 +02:00
standardci
b5c72dda8f chore(release): publish new version
- @standardnotes/workspace-server@1.4.0
2022-10-10 09:41:14 +00:00
Karol Sójko
e06cc3ba80 feat(workspace): add inviting to workspace 2022-10-10 11:38:59 +02:00
standardci
8a72a1a559 chore(release): publish new version
- @standardnotes/workspace-server@1.3.0
2022-10-10 08:52:50 +00:00
Karol Sójko
3f61d3163e feat(workspace): add creating root workspace upon user registration 2022-10-10 10:51:16 +02:00
standardci
34b3c7ce16 chore(release): publish new version
- @standardnotes/workspace-server@1.2.3
2022-10-10 08:33:46 +00:00
Karol Sójko
0ce4185379 fix(workspace): add optional parameters to creating workspace 2022-10-10 10:31:45 +02:00
standardci
1f7989dbed chore(release): publish new version
- @standardnotes/api-gateway@1.27.1
 - @standardnotes/auth-server@1.40.1
 - @standardnotes/common@1.36.1
 - @standardnotes/domain-events-infra@1.8.20
 - @standardnotes/domain-events@2.64.1
 - @standardnotes/event-store@1.3.25
 - @standardnotes/files-server@1.6.11
 - @standardnotes/predicates@1.4.7
 - @standardnotes/scheduler-server@1.10.39
 - @standardnotes/security@1.4.5
 - @standardnotes/syncing-server@1.9.1
 - @standardnotes/workspace-server@1.2.2
2022-10-10 08:26:57 +00:00
Karol Sójko
0ea88ad202 fix(workspace): extract workspace type to common types 2022-10-10 10:25:00 +02:00
standardci
2d41742c34 chore(release): publish new version
- @standardnotes/workspace-server@1.2.1
2022-10-10 08:11:37 +00:00
Karol Sójko
447d600dbe fix(workspace): rename private key to encrypted private key 2022-10-10 10:09:49 +02:00
standardci
3f6db48f83 chore(release): publish new version
- @standardnotes/api-gateway@1.27.0
 - @standardnotes/workspace-server@1.2.0
2022-10-07 11:06:37 +00:00
Karol Sójko
156ab65272 feat: add workspaces creation 2022-10-07 13:05:00 +02:00
standardci
a986ee1ccb chore(release): publish new version
- @standardnotes/api-gateway@1.26.2
 - @standardnotes/auth-server@1.40.0
 - @standardnotes/common@1.36.0
 - @standardnotes/domain-events-infra@1.8.19
 - @standardnotes/domain-events@2.64.0
 - @standardnotes/event-store@1.3.24
 - @standardnotes/files-server@1.6.10
 - @standardnotes/predicates@1.4.6
 - @standardnotes/scheduler-server@1.10.38
 - @standardnotes/security@1.4.4
 - @standardnotes/syncing-server@1.9.0
 - @standardnotes/workspace-server@1.1.2
2022-10-07 09:24:41 +00:00
Karol Sójko
868b7d149a feat: add user protocol version to the user registration event 2022-10-07 11:22:56 +02:00
standardci
b1763b539e chore(release): publish new version
- @standardnotes/auth-server@1.39.2
2022-10-06 13:40:42 +00:00
Karol Sójko
d21517abe6 fix(auth): add warning logs for unrecognized payment type 2022-10-06 13:30:47 +02:00
Karol Sójko
7ef6765d5b chore: upgrade dependabot 2022-10-06 13:05:37 +02:00
Karol Sójko
11492977c9 Merge pull request #157 from standardnotes/dependabot/github_actions/crazy-max/ghaction-import-gpg-5
chore(deps): bump crazy-max/ghaction-import-gpg from 4 to 5
2022-10-06 12:48:55 +02:00
dependabot[bot]
d133c5aacd chore(deps): bump crazy-max/ghaction-import-gpg from 4 to 5
Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 4 to 5.
- [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases)
- [Changelog](https://github.com/crazy-max/ghaction-import-gpg/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v4...v5)

---
updated-dependencies:
- dependency-name: crazy-max/ghaction-import-gpg
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-12 06:31:27 +00:00
194 changed files with 4575 additions and 335 deletions

View File

@@ -85,6 +85,11 @@ updates:
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/packages/workspace"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:

View File

@@ -24,7 +24,7 @@ jobs:
git config --global user.name "standardci"
git config --global user.email "ci@standardnotes.com"
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v4
uses: crazy-max/ghaction-import-gpg@v5
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}

182
.github/workflows/websockets.release.yml vendored Normal file
View File

@@ -0,0 +1,182 @@
name: Websockets Server
concurrency:
group: websockets
cancel-in-progress: true
on:
push:
tags:
- '*standardnotes/websockets-server*'
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
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: Build
run: yarn build
- name: Lint
run: yarn lint:websockets
- name: Test
run: yarn test:websockets
publish-aws-ecr:
needs: test
runs-on: ubuntu-latest
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: Build locally
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: websockets
IMAGE_TAG: ${{ github.sha }}
run: |
yarn docker build @standardnotes/websockets-server -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
publish-docker-hub:
needs: test
runs-on: ubuntu-latest
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: Build locally
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish Docker image as stable
run: |
yarn docker build @standardnotes/websockets-server -t standardnotes/websockets:latest
docker push standardnotes/websockets:latest
deploy-web:
needs: publish-aws-ecr
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition websockets-prod --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="websockets-prod") | .environment[] | select(.name=="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
with:
task-definition: task-definition.json
container-name: websockets-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/websockets:${{ github.sha }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def-prod.outputs.task-definition }}
service: websockets-prod
cluster: prod
wait-for-service-stability: true
deploy-worker:
needs: publish-aws-ecr
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition websockets-worker-prod --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="websockets-worker-prod") | .environment[] | select(.name=="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
with:
task-definition: task-definition.json
container-name: websockets-worker-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/websockets:${{ github.sha }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def-prod.outputs.task-definition }}
service: websockets-worker-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: [ deploy-web, deploy-worker ]
runs-on: ubuntu-latest
steps:
- name: Create New Relic deployment marker for Web
uses: newrelic/deployment-marker-action@v1
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_WEBSOCKETS_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
- name: Create New Relic deployment marker for Worker
uses: newrelic/deployment-marker-action@v1
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_WEBSOCKETS_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"

170
.pnp.cjs generated
View File

@@ -80,6 +80,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"name": "@standardnotes/time",\
"reference": "workspace:packages/time"\
},\
{\
"name": "@standardnotes/websockets-server",\
"reference": "workspace:packages/websockets"\
},\
{\
"name": "@standardnotes/workspace-server",\
"reference": "workspace:packages/workspace"\
@@ -104,6 +108,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@standardnotes/sncrypto-node", ["workspace:packages/sncrypto-node"]],\
["@standardnotes/syncing-server", ["workspace:packages/syncing-server"]],\
["@standardnotes/time", ["workspace:packages/time"]],\
["@standardnotes/websockets-server", ["workspace:packages/websockets"]],\
["@standardnotes/workspace-server", ["workspace:packages/workspace"]]\
],\
"fallbackPool": [\
@@ -2521,16 +2526,30 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/api", [\
["npm:1.9.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.9.0-507434ff00-cc3feac393.zip/node_modules/@standardnotes/api/",\
["npm:1.16.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.16.0-efccf518ba-465f76dd29.zip/node_modules/@standardnotes/api/",\
"packageDependencies": [\
["@standardnotes/api", "npm:1.9.0"],\
["@standardnotes/api", "npm:1.16.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/encryption", "npm:1.15.9"],\
["@standardnotes/models", "npm:1.22.0"],\
["@standardnotes/responses", "npm:1.10.3"],\
["@standardnotes/encryption", "npm:1.17.1"],\
["@standardnotes/models", "npm:1.27.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/utils", "npm:1.9.0"],\
["@standardnotes/utils", "npm:1.10.0"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}],\
["npm:1.16.1", {\
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.16.1-7af309f020-1f939c1825.zip/node_modules/@standardnotes/api/",\
"packageDependencies": [\
["@standardnotes/api", "npm:1.16.1"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/encryption", "npm:1.18.0"],\
["@standardnotes/models", "npm:1.27.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/utils", "npm:1.10.0"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
@@ -2600,7 +2619,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/analytics", "workspace:packages/analytics"],\
["@standardnotes/api", "npm:1.9.0"],\
["@standardnotes/api", "npm:1.16.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
@@ -2725,15 +2744,28 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/encryption", [\
["npm:1.15.9", {\
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.15.9-00c7fac9f6-7595ac08ce.zip/node_modules/@standardnotes/encryption/",\
["npm:1.17.1", {\
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.17.1-f4d1330273-2b2408ffbd.zip/node_modules/@standardnotes/encryption/",\
"packageDependencies": [\
["@standardnotes/encryption", "npm:1.15.9"],\
["@standardnotes/encryption", "npm:1.17.1"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/models", "npm:1.22.0"],\
["@standardnotes/responses", "npm:1.10.3"],\
["@standardnotes/sncrypto-common", "npm:1.12.0"],\
["@standardnotes/utils", "npm:1.9.0"],\
["@standardnotes/models", "npm:1.27.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
["@standardnotes/utils", "npm:1.10.0"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}],\
["npm:1.18.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.18.0-d83f58c21a-f9c39d0986.zip/node_modules/@standardnotes/encryption/",\
"packageDependencies": [\
["@standardnotes/encryption", "npm:1.18.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/models", "npm:1.27.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
["@standardnotes/utils", "npm:1.10.0"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
@@ -2790,6 +2822,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}],\
["npm:1.53.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.53.0-8ea4a2d559-a856e815a3.zip/node_modules/@standardnotes/features/",\
"packageDependencies": [\
["@standardnotes/features", "npm:1.53.0"],\
["@standardnotes/auth", "npm:3.19.4"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/security", "workspace:packages/security"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}]\
]],\
["@standardnotes/files-server", [\
@@ -2845,14 +2888,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/models", [\
["npm:1.22.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.22.0-2cc72f987b-9928246368.zip/node_modules/@standardnotes/models/",\
["npm:1.26.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.26.0-dade8919ab-f595a3de88.zip/node_modules/@standardnotes/models/",\
"packageDependencies": [\
["@standardnotes/models", "npm:1.22.0"],\
["@standardnotes/models", "npm:1.26.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/features", "npm:1.52.1"],\
["@standardnotes/responses", "npm:1.10.3"],\
["@standardnotes/utils", "npm:1.9.0"],\
["@standardnotes/features", "npm:1.53.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/utils", "npm:1.10.0"],\
["lodash", "npm:4.17.21"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}],\
["npm:1.27.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.27.0-831bd645c6-263fd9e923.zip/node_modules/@standardnotes/models/",\
"packageDependencies": [\
["@standardnotes/models", "npm:1.27.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/features", "npm:1.53.0"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/utils", "npm:1.10.0"],\
["lodash", "npm:4.17.21"],\
["reflect-metadata", "npm:0.1.13"]\
],\
@@ -2888,12 +2944,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/responses", [\
["npm:1.10.3", {\
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.10.3-7cdb15f83a-4a1e31eb89.zip/node_modules/@standardnotes/responses/",\
["npm:1.11.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.11.0-d066ddbbb6-46d6a47980.zip/node_modules/@standardnotes/responses/",\
"packageDependencies": [\
["@standardnotes/responses", "npm:1.10.3"],\
["@standardnotes/responses", "npm:1.11.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/features", "npm:1.52.1"],\
["@standardnotes/features", "npm:1.53.0"],\
["@standardnotes/security", "workspace:packages/security"],\
["reflect-metadata", "npm:0.1.13"]\
],\
@@ -3004,10 +3060,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/sncrypto-common", [\
["npm:1.12.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.12.0-1a093ff006-b89a14bd23.zip/node_modules/@standardnotes/sncrypto-common/",\
["npm:1.13.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.0-18cb5f8eb9-e58258f525.zip/node_modules/@standardnotes/sncrypto-common/",\
"packageDependencies": [\
["@standardnotes/sncrypto-common", "npm:1.12.0"],\
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
@@ -3114,6 +3170,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@standardnotes/utils", [\
["npm:1.10.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.10.0-0dc2ade40b-c02d54ca8a.zip/node_modules/@standardnotes/utils/",\
"packageDependencies": [\
["@standardnotes/utils", "npm:1.10.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["dompurify", "npm:2.4.0"],\
["lodash", "npm:4.17.21"],\
["reflect-metadata", "npm:0.1.13"]\
],\
"linkType": "HARD"\
}],\
["npm:1.6.12", {\
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip/node_modules/@standardnotes/utils/",\
"packageDependencies": [\
@@ -3123,17 +3190,45 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["lodash", "npm:4.17.21"]\
],\
"linkType": "HARD"\
}],\
["npm:1.9.0", {\
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.9.0-da939553f6-4591aff48d.zip/node_modules/@standardnotes/utils/",\
}]\
]],\
["@standardnotes/websockets-server", [\
["workspace:packages/websockets", {\
"packageLocation": "./packages/websockets/",\
"packageDependencies": [\
["@standardnotes/utils", "npm:1.9.0"],\
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/api", "npm:1.16.1"],\
["@standardnotes/common", "workspace:packages/common"],\
["dompurify", "npm:2.4.0"],\
["lodash", "npm:4.17.21"],\
["reflect-metadata", "npm:0.1.13"]\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
["@standardnotes/security", "workspace:packages/security"],\
["@types/cors", "npm:2.8.12"],\
["@types/express", "npm:4.17.13"],\
["@types/ioredis", "npm:4.28.10"],\
["@types/jest", "npm:29.1.1"],\
["@types/newrelic", "npm:7.0.3"],\
["@typescript-eslint/eslint-plugin", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:5.30.5"],\
["aws-sdk", "npm:2.1168.0"],\
["axios", "npm:0.27.2"],\
["cors", "npm:2.8.5"],\
["dotenv", "npm:16.0.1"],\
["eslint", "npm:8.19.0"],\
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["express", "npm:4.18.1"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:29.1.2"],\
["mysql2", "npm:2.3.3"],\
["newrelic", "npm:9.0.0"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:29.0.3"],\
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.7"],\
["winston", "npm:3.8.1"]\
],\
"linkType": "HARD"\
"linkType": "SOFT"\
}]\
]],\
["@standardnotes/workspace-server", [\
@@ -3143,10 +3238,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@standardnotes/workspace-server", "workspace:packages/workspace"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/api", "npm:1.16.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
["@standardnotes/models", "npm:1.26.0"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/time", "workspace:packages/time"],\
["@types/cors", "npm:2.8.12"],\
["@types/express", "npm:4.17.13"],\
["@types/ioredis", "npm:4.28.10"],\

Binary file not shown.

View File

@@ -18,6 +18,7 @@
"lint:files": "yarn workspace @standardnotes/files-server lint",
"lint:api-gateway": "yarn workspace @standardnotes/api-gateway lint",
"lint:event-store": "yarn workspace @standardnotes/event-store lint",
"lint:websockets": "yarn workspace @standardnotes/websockets-server lint",
"lint:workspace": "yarn workspace @standardnotes/workspace-server lint",
"test": "yarn workspaces foreach -p -j 10 --verbose run test",
"test:auth": "yarn workspace @standardnotes/auth-server test",
@@ -25,16 +26,18 @@
"test:syncing-server": "yarn workspace @standardnotes/syncing-server test",
"test:files": "yarn workspace @standardnotes/files-server test",
"test:event-store": "yarn workspace @standardnotes/event-store test",
"test:websockets": "yarn workspace @standardnotes/websockets-server test",
"test:workspace": "yarn workspace @standardnotes/workspace-server test",
"clean": "yarn workspaces foreach -p --verbose run clean",
"setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env",
"build": "yarn workspaces foreach -pt -j 10 --verbose run build",
"build:auth": "yarn workspace @standardnotes/auth-server build",
"build:scheduler": "yarn workspace @standardnotes/scheduler-server build",
"build:syncing-server": "yarn workspace @standardnotes/syncing-server build",
"build:files": "yarn workspace @standardnotes/files-server build",
"build:api-gateway": "yarn workspace @standardnotes/api-gateway build",
"build:workspace": "yarn workspace @standardnotes/workspace-server build",
"build:auth": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/auth-server run build",
"build:scheduler": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/scheduler-server run build",
"build:syncing-server": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/syncing-server run build",
"build:files": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/files-server run build",
"build:api-gateway": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/api-gateway run build",
"build:websockets": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/websockets-server run build",
"build:workspace": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/workspace-server run build",
"start:auth": "yarn workspace @standardnotes/auth-server start",
"start:auth-worker": "yarn workspace @standardnotes/auth-server worker",
"start:scheduler": "yarn workspace @standardnotes/scheduler-server worker",
@@ -43,6 +46,7 @@
"start:files": "yarn workspace @standardnotes/files-server start",
"start:files-worker": "yarn workspace @standardnotes/files-server worker",
"start:api-gateway": "yarn workspace @standardnotes/api-gateway start",
"start:websockets": "yarn workspace @standardnotes/websockets-server start",
"start:workspace": "yarn workspace @standardnotes/workspace-server start",
"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",

View File

@@ -6,6 +6,8 @@ PORT=3000
SYNCING_SERVER_JS_URL=http://syncing_server_js:3000
AUTH_SERVER_URL=http://auth:3000
WORKSPACE_SERVER_URL=http://workspace:3000
WEB_SOCKET_SERVER_URL=http://websockets:3000
PAYMENTS_SERVER_URL=http://payments:3000
FILES_SERVER_URL=http://files:3000

View File

@@ -3,6 +3,72 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.31.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.31.0...@standardnotes/api-gateway@1.31.1) (2022-10-13)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.31.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.30.1...@standardnotes/api-gateway@1.31.0) (2022-10-13)
### Features
* **auth:** remove websocket handling in favor of websockets service ([86ae4a5](https://github.com/standardnotes/api-gateway/commit/86ae4a59a3ac7915ad96ed5176b545f4d005e837))
## [1.30.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.30.0...@standardnotes/api-gateway@1.30.1) (2022-10-13)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.30.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.29.0...@standardnotes/api-gateway@1.30.0) (2022-10-12)
### Features
* **workspace:** add endpoints for initiating keyshare in a workspace ([0c1a779](https://github.com/standardnotes/api-gateway/commit/0c1a779ef03819928e7e791a6843d90eb9fed964))
# [1.29.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.2...@standardnotes/api-gateway@1.29.0) (2022-10-11)
### Features
* add listin worspaces and workspace users ([095d13f](https://github.com/standardnotes/api-gateway/commit/095d13f8bbfe543fcf086840e1a985447a6c51ef))
## [1.28.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.1...@standardnotes/api-gateway@1.28.2) (2022-10-11)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.28.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.0...@standardnotes/api-gateway@1.28.1) (2022-10-11)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.28.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.4...@standardnotes/api-gateway@1.28.0) (2022-10-11)
### Features
* **workspace:** add invite to workspace endpoints ([266adda](https://github.com/standardnotes/api-gateway/commit/266adda45bd3ad84bc6605824b6be1dd912f3f9a))
## [1.27.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.3...@standardnotes/api-gateway@1.27.4) (2022-10-10)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.27.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.2...@standardnotes/api-gateway@1.27.3) (2022-10-10)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.27.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.1...@standardnotes/api-gateway@1.27.2) (2022-10-10)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.27.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.0...@standardnotes/api-gateway@1.27.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.27.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.26.2...@standardnotes/api-gateway@1.27.0) (2022-10-07)
### Features
* add workspaces creation ([156ab65](https://github.com/standardnotes/api-gateway/commit/156ab6527265351b13797394cbd34da037ab5a38))
## [1.26.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.26.1...@standardnotes/api-gateway@1.26.2) (2022-10-07)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.26.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.26.0...@standardnotes/api-gateway@1.26.1) (2022-10-06)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -19,6 +19,8 @@ import '../src/Controller/v1/TokensController'
import '../src/Controller/v1/OfflineController'
import '../src/Controller/v1/FilesController'
import '../src/Controller/v1/SubscriptionInvitesController'
import '../src/Controller/v1/WorkspacesController'
import '../src/Controller/v1/InvitesController'
import '../src/Controller/v2/PaymentsControllerV2'
import '../src/Controller/v2/ActionsControllerV2'

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.26.1",
"version": "1.31.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -75,6 +75,8 @@ export class ContainerConfigLoader {
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
container.bind(TYPES.WORKSPACE_SERVER_URL).toConstantValue(env.get('WORKSPACE_SERVER_URL'))
container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL'))
container
.bind(TYPES.HTTP_CALL_TIMEOUT)
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)

View File

@@ -8,6 +8,8 @@ const TYPES = {
AUTH_SERVER_URL: Symbol.for('AUTH_SERVER_URL'),
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
WORKSPACE_SERVER_URL: Symbol.for('WORKSPACE_SERVER_URL'),
WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
VERSION: Symbol.for('VERSION'),

View File

@@ -0,0 +1,23 @@
import { inject } from 'inversify'
import { Request, Response } from 'express'
import { controller, BaseHttpController, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/invites', TYPES.AuthMiddleware)
export class InvitesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/:inviteUuid/accept')
async accept(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(
request,
response,
`invites/${request.params.inviteUuid}/accept`,
request.body,
)
}
}

View File

@@ -17,7 +17,7 @@ export class WebSocketsController extends BaseHttpController {
@httpPost('/tokens', TYPES.AuthMiddleware)
async createWebSocketConnectionToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'sockets/tokens', request.body)
await this.httpService.callWebSocketServer(request, response, 'sockets/tokens', request.body)
}
@httpPost('/connections', TYPES.WebSocketAuthMiddleware)
@@ -30,7 +30,7 @@ export class WebSocketsController extends BaseHttpController {
return
}
await this.httpService.callAuthServer(
await this.httpService.callWebSocketServer(
request,
response,
`sockets/connections/${request.headers.connectionid}`,
@@ -48,7 +48,7 @@ export class WebSocketsController extends BaseHttpController {
return
}
await this.httpService.callAuthServer(
await this.httpService.callWebSocketServer(
request,
response,
`sockets/connections/${request.headers.connectionid}`,

View File

@@ -0,0 +1,53 @@
import { inject } from 'inversify'
import { Request, Response } from 'express'
import { controller, BaseHttpController, httpPost, httpGet } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/workspaces', TYPES.AuthMiddleware)
export class WorkspacesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/')
async create(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
}
@httpGet('/:workspaceUuid/users')
async listWorkspaceUsers(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(
request,
response,
`workspaces/${request.params.workspaceUuid}/users`,
request.body,
)
}
@httpPost('/:workspaceUuid/users/:userUuid/keyshare')
async initiateKeyshare(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(
request,
response,
`workspaces/${request.params.workspaceUuid}/users/${request.params.userUuid}/keyshare`,
request.body,
)
}
@httpGet('/')
async listWorkspaces(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
}
@httpPost('/:workspaceUuid/invites')
async invite(request: Request, response: Response): Promise<void> {
await this.httpService.callWorkspaceServer(
request,
response,
`workspaces/${request.params.workspaceUuid}/invites`,
request.body,
)
}
}

View File

@@ -16,6 +16,8 @@ export class HttpService implements HttpServiceInterface {
@inject(TYPES.SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
@inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
@inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string,
@inject(TYPES.WORKSPACE_SERVER_URL) private workspaceServerUrl: string,
@inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
@inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Logger) private logger: Logger,
@@ -48,6 +50,24 @@ export class HttpService implements HttpServiceInterface {
await this.callServer(this.authServerUrl, request, response, endpoint, payload)
}
async callWorkspaceServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.workspaceServerUrl, request, response, endpoint, payload)
}
async callWebSocketServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
}
async callPaymentsServer(
request: Request,
response: Response,

View File

@@ -31,4 +31,16 @@ export interface HttpServiceInterface {
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callWorkspaceServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callWebSocketServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
}

View File

@@ -67,7 +67,6 @@ VALET_TOKEN_SECRET=
VALET_TOKEN_TTL=
WEB_SOCKET_CONNECTION_TOKEN_SECRET=
WEB_SOCKET_CONNECTION_TOKEN_TTL=
# (Optional) Analytics
ANALYTICS_ENABLED=false

View File

@@ -3,6 +3,76 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.45.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.44.0...@standardnotes/auth-server@1.45.0) (2022-10-13)
### Features
* publish workspace invite accepted event for websockets ([86379eb](https://github.com/standardnotes/server/commit/86379eb96d7231d6a76ee91350accef2d44a941d))
# [1.44.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.43.1...@standardnotes/auth-server@1.44.0) (2022-10-13)
### Features
* **auth:** remove websocket handling in favor of websockets service ([86ae4a5](https://github.com/standardnotes/server/commit/86ae4a59a3ac7915ad96ed5176b545f4d005e837))
## [1.43.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.43.0...@standardnotes/auth-server@1.43.1) (2022-10-13)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.43.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.42.0...@standardnotes/auth-server@1.43.0) (2022-10-12)
### Features
* **workspace:** add endpoints for initiating keyshare in a workspace ([0c1a779](https://github.com/standardnotes/server/commit/0c1a779ef03819928e7e791a6843d90eb9fed964))
# [1.42.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.2...@standardnotes/auth-server@1.42.0) (2022-10-11)
### Features
* add listin worspaces and workspace users ([095d13f](https://github.com/standardnotes/server/commit/095d13f8bbfe543fcf086840e1a985447a6c51ef))
## [1.41.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.1...@standardnotes/auth-server@1.41.2) (2022-10-11)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.41.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.0...@standardnotes/auth-server@1.41.1) (2022-10-11)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.41.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.4...@standardnotes/auth-server@1.41.0) (2022-10-11)
### Features
* **workspace:** add invite to workspace endpoints ([266adda](https://github.com/standardnotes/server/commit/266adda45bd3ad84bc6605824b6be1dd912f3f9a))
## [1.40.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.3...@standardnotes/auth-server@1.40.4) (2022-10-10)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.40.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.2...@standardnotes/auth-server@1.40.3) (2022-10-10)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.40.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.1...@standardnotes/auth-server@1.40.2) (2022-10-10)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.40.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.0...@standardnotes/auth-server@1.40.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.40.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.39.2...@standardnotes/auth-server@1.40.0) (2022-10-07)
### Features
* add user protocol version to the user registration event ([868b7d1](https://github.com/standardnotes/server/commit/868b7d149a572d1991b77daaa37e4c77e10f07d3))
## [1.39.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.39.1...@standardnotes/auth-server@1.39.2) (2022-10-06)
### Bug Fixes
* **auth:** add warning logs for unrecognized payment type ([d21517a](https://github.com/standardnotes/server/commit/d21517abe6a7ed4773e73e83a347bae69495c084))
## [1.39.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.39.0...@standardnotes/auth-server@1.39.1) (2022-10-06)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.39.1",
"version": "1.45.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -34,7 +34,7 @@
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/api": "^1.9.0",
"@standardnotes/api": "^1.16.0",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",

View File

@@ -79,10 +79,6 @@ import { DeleteAccount } from '../Domain/UseCase/DeleteAccount/DeleteAccount'
import { DeleteSetting } from '../Domain/UseCase/DeleteSetting/DeleteSetting'
import { SettingFactory } from '../Domain/Setting/SettingFactory'
import { SettingService } from '../Domain/Setting/SettingService'
import { WebSocketsConnectionRepositoryInterface } from '../Domain/WebSockets/WebSocketsConnectionRepositoryInterface'
import { RedisWebSocketsConnectionRepository } from '../Infra/Redis/RedisWebSocketsConnectionRepository'
import { AddWebSocketsConnection } from '../Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection'
import { RemoveWebSocketsConnection } from '../Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection'
import axios, { AxiosInstance } from 'axios'
import { UserSubscription } from '../Domain/Subscription/UserSubscription'
import { MySQLUserSubscriptionRepository } from '../Infra/MySQL/MySQLUserSubscriptionRepository'
@@ -206,9 +202,6 @@ import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventH
import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler'
import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEventHandler'
import { SubscriptionInvitesController } from '../Controller/SubscriptionInvitesController'
import { CreateWebSocketConnectionToken } from '../Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken'
import { WebSocketsController } from '../Controller/WebSocketsController'
import { WebSocketServerInterface } from '@standardnotes/api'
import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -273,7 +266,6 @@ export class ContainerConfigLoader {
// Controller
container.bind<AuthController>(TYPES.AuthController).to(AuthController)
container.bind<SubscriptionInvitesController>(TYPES.SubscriptionInvitesController).to(SubscriptionInvitesController)
container.bind<WebSocketServerInterface>(TYPES.WebSocketsController).to(WebSocketsController)
// Repositories
container.bind<SessionRepositoryInterface>(TYPES.SessionRepository).to(MySQLSessionRepository)
@@ -295,9 +287,6 @@ export class ContainerConfigLoader {
.bind<RedisEphemeralSessionRepository>(TYPES.EphemeralSessionRepository)
.to(RedisEphemeralSessionRepository)
container.bind<LockRepository>(TYPES.LockRepository).to(LockRepository)
container
.bind<WebSocketsConnectionRepositoryInterface>(TYPES.WebSocketsConnectionRepository)
.to(RedisWebSocketsConnectionRepository)
container
.bind<SubscriptionTokenRepositoryInterface>(TYPES.SubscriptionTokenRepository)
.to(RedisSubscriptionTokenRepository)
@@ -375,9 +364,6 @@ export class ContainerConfigLoader {
container
.bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
container
.bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_TTL)
.toConstantValue(+env.get('WEB_SOCKET_CONNECTION_TOKEN_TTL', true))
container.bind(TYPES.ENCRYPTION_SERVER_KEY).toConstantValue(env.get('ENCRYPTION_SERVER_KEY'))
container.bind(TYPES.ACCESS_TOKEN_AGE).toConstantValue(env.get('ACCESS_TOKEN_AGE'))
container.bind(TYPES.REFRESH_TOKEN_AGE).toConstantValue(env.get('REFRESH_TOKEN_AGE'))
@@ -399,7 +385,6 @@ export class ContainerConfigLoader {
container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL'))
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
container.bind(TYPES.SYNCING_SERVER_URL).toConstantValue(env.get('SYNCING_SERVER_URL'))
container.bind(TYPES.WEBSOCKETS_API_URL).toConstantValue(env.get('WEBSOCKETS_API_URL', true))
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
@@ -424,8 +409,6 @@ export class ContainerConfigLoader {
container.bind<UpdateSetting>(TYPES.UpdateSetting).to(UpdateSetting)
container.bind<DeleteSetting>(TYPES.DeleteSetting).to(DeleteSetting)
container.bind<DeleteAccount>(TYPES.DeleteAccount).to(DeleteAccount)
container.bind<AddWebSocketsConnection>(TYPES.AddWebSocketsConnection).to(AddWebSocketsConnection)
container.bind<RemoveWebSocketsConnection>(TYPES.RemoveWebSocketsConnection).to(RemoveWebSocketsConnection)
container.bind<GetUserSubscription>(TYPES.GetUserSubscription).to(GetUserSubscription)
container.bind<GetUserOfflineSubscription>(TYPES.GetUserOfflineSubscription).to(GetUserOfflineSubscription)
container.bind<CreateSubscriptionToken>(TYPES.CreateSubscriptionToken).to(CreateSubscriptionToken)
@@ -454,9 +437,6 @@ export class ContainerConfigLoader {
container.bind<GetSubscriptionSetting>(TYPES.GetSubscriptionSetting).to(GetSubscriptionSetting)
container.bind<GetUserAnalyticsId>(TYPES.GetUserAnalyticsId).to(GetUserAnalyticsId)
container.bind<VerifyPredicate>(TYPES.VerifyPredicate).to(VerifyPredicate)
container
.bind<CreateWebSocketConnectionToken>(TYPES.CreateWebSocketConnectionToken)
.to(CreateWebSocketConnectionToken)
container.bind<CreateCrossServiceToken>(TYPES.CreateCrossServiceToken).to(CreateCrossServiceToken)
// Handlers
@@ -547,11 +527,6 @@ export class ContainerConfigLoader {
container
.bind<TokenEncoderInterface<ValetTokenData>>(TYPES.ValetTokenEncoder)
.toConstantValue(new TokenEncoder<ValetTokenData>(container.get(TYPES.VALET_TOKEN_SECRET)))
container
.bind<TokenEncoderInterface<WebSocketConnectionTokenData>>(TYPES.WebSocketConnectionTokenEncoder)
.toConstantValue(
new TokenEncoder<WebSocketConnectionTokenData>(container.get(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)),
)
container.bind<AuthenticationMethodResolver>(TYPES.AuthenticationMethodResolver).to(AuthenticationMethodResolver)
container.bind<DomainEventFactory>(TYPES.DomainEventFactory).to(DomainEventFactory)
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())

View File

@@ -6,7 +6,6 @@ const TYPES = {
// Controller
AuthController: Symbol.for('AuthController'),
SubscriptionInvitesController: Symbol.for('SubscriptionInvitesController'),
WebSocketsController: Symbol.for('WebSocketsController'),
// Repositories
UserRepository: Symbol.for('UserRepository'),
SessionRepository: Symbol.for('SessionRepository'),
@@ -17,7 +16,6 @@ const TYPES = {
OfflineSettingRepository: Symbol.for('OfflineSettingRepository'),
LockRepository: Symbol.for('LockRepository'),
RoleRepository: Symbol.for('RoleRepository'),
WebSocketsConnectionRepository: Symbol.for('WebSocketsConnectionRepository'),
UserSubscriptionRepository: Symbol.for('UserSubscriptionRepository'),
OfflineUserSubscriptionRepository: Symbol.for('OfflineUserSubscriptionRepository'),
SubscriptionTokenRepository: Symbol.for('SubscriptionTokenRepository'),
@@ -83,7 +81,6 @@ const TYPES = {
REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'),
NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'),
SYNCING_SERVER_URL: Symbol.for('SYNCING_SERVER_URL'),
WEBSOCKETS_API_URL: Symbol.for('WEBSOCKETS_API_URL'),
VERSION: Symbol.for('VERSION'),
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
// use cases
@@ -107,8 +104,6 @@ const TYPES = {
UpdateSetting: Symbol.for('UpdateSetting'),
DeleteSetting: Symbol.for('DeleteSetting'),
DeleteAccount: Symbol.for('DeleteAccount'),
AddWebSocketsConnection: Symbol.for('AddWebSocketsConnection'),
RemoveWebSocketsConnection: Symbol.for('RemoveWebSocketsConnection'),
GetUserSubscription: Symbol.for('GetUserSubscription'),
GetUserOfflineSubscription: Symbol.for('GetUserOfflineSubscription'),
CreateSubscriptionToken: Symbol.for('CreateSubscriptionToken'),
@@ -125,7 +120,6 @@ const TYPES = {
GetSubscriptionSetting: Symbol.for('GetSubscriptionSetting'),
GetUserAnalyticsId: Symbol.for('GetUserAnalyticsId'),
VerifyPredicate: Symbol.for('VerifyPredicate'),
CreateWebSocketConnectionToken: Symbol.for('CreateWebSocketConnectionToken'),
CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'),
// Handlers
UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'),
@@ -168,7 +162,6 @@ const TYPES = {
CrossServiceTokenEncoder: Symbol.for('CrossServiceTokenEncoder'),
SessionTokenEncoder: Symbol.for('SessionTokenEncoder'),
ValetTokenEncoder: Symbol.for('ValetTokenEncoder'),
WebSocketConnectionTokenEncoder: Symbol.for('WebSocketConnectionTokenEncoder'),
WebSocketConnectionTokenDecoder: Symbol.for('WebSocketConnectionTokenDecoder'),
AuthenticationMethodResolver: Symbol.for('AuthenticationMethodResolver'),
DomainEventPublisher: Symbol.for('DomainEventPublisher'),

View File

@@ -11,6 +11,7 @@ import TYPES from '../Bootstrap/Types'
import { ClearLoginAttempts } from '../Domain/UseCase/ClearLoginAttempts'
import { Register } from '../Domain/UseCase/Register'
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
import { ProtocolVersion } from '@standardnotes/common'
@injectable()
export class AuthController implements UserServerInterface {
@@ -59,10 +60,11 @@ export class AuthController implements UserServerInterface {
await this.clearLoginAttempts.execute({ email: registerResult.authResponse.user.email as string })
await this.domainEventPublisher.publish(
this.domainEventFactory.createUserRegisteredEvent(
<string>registerResult.authResponse.user.uuid,
<string>registerResult.authResponse.user.email,
),
this.domainEventFactory.createUserRegisteredEvent({
userUuid: <string>registerResult.authResponse.user.uuid,
email: <string>registerResult.authResponse.user.email,
protocolVersion: (<string>registerResult.authResponse.user.protocolVersion) as ProtocolVersion,
}),
)
return {

View File

@@ -1,8 +1,9 @@
import { Uuid } from '@standardnotes/common'
import { ProtocolVersion, Uuid } from '@standardnotes/common'
export interface AuthResponse {
user: {
uuid: Uuid
email: string
protocolVersion: ProtocolVersion
}
}

View File

@@ -1,5 +1,5 @@
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { Uuid } from '@standardnotes/common'
import { ProtocolVersion, Uuid } from '@standardnotes/common'
import * as crypto from 'crypto'
import { inject, injectable } from 'inversify'
@@ -39,7 +39,11 @@ export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface
this.logger.debug(`Created JWT token for user ${dto.user.uuid}: ${token}`)
return {
user: this.userProjector.projectSimple(dto.user) as { uuid: Uuid; email: string },
user: this.userProjector.projectSimple(dto.user) as {
uuid: Uuid
email: string
protocolVersion: ProtocolVersion
},
token,
}
}

View File

@@ -3,7 +3,7 @@ import {
SessionTokenData,
TokenEncoderInterface,
} from '@standardnotes/security'
import { Uuid } from '@standardnotes/common'
import { ProtocolVersion, Uuid } from '@standardnotes/common'
import { SessionBody } from '@standardnotes/responses'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
@@ -48,7 +48,11 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
return {
session: sessionPayload,
key_params: this.keyParamsFactory.create(dto.user, true),
user: this.userProjector.projectSimple(dto.user) as { uuid: Uuid; email: string },
user: this.userProjector.projectSimple(dto.user) as {
uuid: Uuid
email: string
protocolVersion: ProtocolVersion
},
}
}

View File

@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { EmailMessageIdentifier, RoleName } from '@standardnotes/common'
import { EmailMessageIdentifier, ProtocolVersion, RoleName } from '@standardnotes/common'
import { PredicateName, PredicateAuthority, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
@@ -18,6 +18,29 @@ describe('DomainEventFactory', () => {
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
})
it('should create a WEB_SOCKET_MESSAGE_REQUESTED event', () => {
expect(
createFactory().createWebSocketMessageRequestedEvent({
userUuid: '1-2-3',
message: 'foobar',
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: '1-2-3',
userIdentifierType: 'uuid',
},
origin: 'auth',
},
payload: {
userUuid: '1-2-3',
message: 'foobar',
},
type: 'WEB_SOCKET_MESSAGE_REQUESTED',
})
})
it('should create a EMAIL_MESSAGE_REQUESTED event', () => {
expect(
createFactory().createEmailMessageRequestedEvent({
@@ -212,7 +235,13 @@ describe('DomainEventFactory', () => {
})
it('should create a USER_REGISTERED event', () => {
expect(createFactory().createUserRegisteredEvent('1-2-3', 'test@test.te')).toEqual({
expect(
createFactory().createUserRegisteredEvent({
userUuid: '1-2-3',
email: 'test@test.te',
protocolVersion: ProtocolVersion.V004,
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
@@ -224,6 +253,7 @@ describe('DomainEventFactory', () => {
payload: {
userUuid: '1-2-3',
email: 'test@test.te',
protocolVersion: '004',
},
type: 'USER_REGISTERED',
})

View File

@@ -1,4 +1,4 @@
import { EmailMessageIdentifier, RoleName, Uuid } from '@standardnotes/common'
import { EmailMessageIdentifier, JSONString, ProtocolVersion, RoleName, Uuid } from '@standardnotes/common'
import {
AccountDeletionRequestedEvent,
UserEmailChangedEvent,
@@ -15,6 +15,7 @@ import {
PredicateVerifiedEvent,
DomainEventService,
EmailMessageRequestedEvent,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
@@ -27,6 +28,21 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
createWebSocketMessageRequestedEvent(dto: { userUuid: Uuid; message: JSONString }): WebSocketMessageRequestedEvent {
return {
type: 'WEB_SOCKET_MESSAGE_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.Auth,
},
payload: dto,
}
}
createEmailMessageRequestedEvent(dto: {
userEmail: string
messageIdentifier: EmailMessageIdentifier
@@ -253,21 +269,22 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
}
}
createUserRegisteredEvent(userUuid: string, email: string): UserRegisteredEvent {
createUserRegisteredEvent(dto: {
userUuid: string
email: string
protocolVersion: ProtocolVersion
}): UserRegisteredEvent {
return {
type: 'USER_REGISTERED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: userUuid,
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.Auth,
},
payload: {
userUuid,
email,
},
payload: dto,
}
}

View File

@@ -1,4 +1,4 @@
import { Uuid, RoleName, EmailMessageIdentifier } from '@standardnotes/common'
import { Uuid, RoleName, EmailMessageIdentifier, ProtocolVersion, JSONString } from '@standardnotes/common'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import {
AccountDeletionRequestedEvent,
@@ -15,10 +15,12 @@ import {
SharedSubscriptionInvitationCanceledEvent,
PredicateVerifiedEvent,
EmailMessageRequestedEvent,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
export interface DomainEventFactoryInterface {
createWebSocketMessageRequestedEvent(dto: { userUuid: Uuid; message: JSONString }): WebSocketMessageRequestedEvent
createEmailMessageRequestedEvent(dto: {
userEmail: string
messageIdentifier: EmailMessageIdentifier
@@ -33,7 +35,11 @@ export interface DomainEventFactoryInterface {
muteSignInEmailsSettingUuid: Uuid
}): UserSignedInEvent
createListedAccountRequestedEvent(userUuid: string, userEmail: string): ListedAccountRequestedEvent
createUserRegisteredEvent(userUuid: string, email: string): UserRegisteredEvent
createUserRegisteredEvent(dto: {
userUuid: string
email: string
protocolVersion: ProtocolVersion
}): UserRegisteredEvent
createEmailBackupRequestedEvent(
userUuid: string,
muteEmailsSettingUuid: string,

View File

@@ -7,6 +7,7 @@ import { PaymentSuccessEventHandler } from './PaymentSuccessEventHandler'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { User } from '../User/User'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { Logger } from 'winston'
describe('PaymentSuccessEventHandler', () => {
let userRepository: UserRepositoryInterface
@@ -15,9 +16,10 @@ describe('PaymentSuccessEventHandler', () => {
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
let logger: Logger
const createHandler = () =>
new PaymentSuccessEventHandler(userRepository, getUserAnalyticsId, analyticsStore, statisticsStore)
new PaymentSuccessEventHandler(userRepository, getUserAnalyticsId, analyticsStore, statisticsStore, logger)
beforeEach(() => {
user = {} as jest.Mocked<User>
@@ -42,6 +44,9 @@ describe('PaymentSuccessEventHandler', () => {
paymentType: 'initial',
subscriptionName: 'PRO_PLAN',
}
logger = {} as jest.Mocked<Logger>
logger.warn = jest.fn()
})
it('should mark payment success for analytics', async () => {

View File

@@ -8,6 +8,7 @@ import {
import { PaymentType, SubscriptionBillingFrequency, SubscriptionName } from '@standardnotes/common'
import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
@@ -61,6 +62,7 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
async handle(event: PaymentSuccessEvent): Promise<void> {
@@ -83,6 +85,10 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
?.get(event.payload.billingFrequency as SubscriptionBillingFrequency)
if (detailedMeasure !== undefined) {
statisticMeasures.push(detailedMeasure)
} else {
this.logger.warn(
`Could not find detailed measure for: subscription - ${event.payload.subscriptionName}, payment type - ${event.payload.paymentType}, billing frequency - ${event.payload.billingFrequency}`,
)
}
for (const measure of statisticMeasures) {

View File

@@ -6,6 +6,7 @@ import { UserRegisteredEventHandler } from './UserRegisteredEventHandler'
import { AxiosInstance } from 'axios'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
import { ProtocolVersion } from '@standardnotes/common'
describe('UserRegisteredEventHandler', () => {
let httpClient: AxiosInstance
@@ -35,6 +36,7 @@ describe('UserRegisteredEventHandler', () => {
event.payload = {
userUuid: '1-2-3',
email: 'test@test.te',
protocolVersion: ProtocolVersion.V004,
}
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>

View File

@@ -1,43 +1,27 @@
import { WebSocketServerInterface } from '@standardnotes/api'
import { ErrorTag } from '@standardnotes/common'
import { TokenDecoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security'
import { Request, Response } from 'express'
import { Request } from 'express'
import { inject } from 'inversify'
import {
BaseHttpController,
controller,
httpDelete,
httpPost,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
results,
} from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { AddWebSocketsConnection } from '../../Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection'
import { CreateCrossServiceToken } from '../../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
import { RemoveWebSocketsConnection } from '../../Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection'
@controller('/sockets')
export class InversifyExpressWebSocketsController extends BaseHttpController {
constructor(
@inject(TYPES.AddWebSocketsConnection) private addWebSocketsConnection: AddWebSocketsConnection,
@inject(TYPES.RemoveWebSocketsConnection) private removeWebSocketsConnection: RemoveWebSocketsConnection,
@inject(TYPES.CreateCrossServiceToken) private createCrossServiceToken: CreateCrossServiceToken,
@inject(TYPES.WebSocketsController) private webSocketsController: WebSocketServerInterface,
@inject(TYPES.WebSocketConnectionTokenDecoder)
private tokenDecoder: TokenDecoderInterface<WebSocketConnectionTokenData>,
) {
super()
}
@httpPost('/tokens', TYPES.ApiGatewayAuthMiddleware)
async createConnectionToken(_request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.webSocketsController.createConnectionToken({
userUuid: response.locals.user.uuid,
})
return this.json(result.data, result.status)
}
@httpPost('/tokens/validate')
async validateToken(request: Request): Promise<results.JsonResult> {
if (!request.headers.authorization) {
@@ -72,26 +56,4 @@ export class InversifyExpressWebSocketsController extends BaseHttpController {
return this.json({ authToken: result.token })
}
@httpPost('/connections/:connectionId', TYPES.ApiGatewayAuthMiddleware)
async storeWebSocketsConnection(
request: Request,
response: Response,
): Promise<results.JsonResult | results.BadRequestErrorMessageResult> {
await this.addWebSocketsConnection.execute({
userUuid: response.locals.user.uuid,
connectionId: request.params.connectionId,
})
return this.json({ success: true })
}
@httpDelete('/connections/:connectionId')
async deleteWebSocketsConnection(
request: Request,
): Promise<results.JsonResult | results.BadRequestErrorMessageResult> {
await this.removeWebSocketsConnection.execute({ connectionId: request.params.connectionId })
return this.json({ success: true })
}
}

View File

@@ -1,38 +1,25 @@
import 'reflect-metadata'
import { UserRolesChangedEvent } from '@standardnotes/domain-events'
import {
DomainEventPublisherInterface,
UserRolesChangedEvent,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { RoleName } from '@standardnotes/common'
import { User } from '../../Domain/User/User'
import { WebSocketsClientService } from './WebSocketsClientService'
import { WebSocketsConnectionRepositoryInterface } from '../../Domain/WebSockets/WebSocketsConnectionRepositoryInterface'
import { DomainEventFactoryInterface } from '../../Domain/Event/DomainEventFactoryInterface'
import { AxiosInstance } from 'axios'
import { Logger } from 'winston'
describe('WebSocketsClientService', () => {
let connectionIds: string[]
let user: User
let event: UserRolesChangedEvent
let webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface
let domainEventFactory: DomainEventFactoryInterface
let httpClient: AxiosInstance
let logger: Logger
let domainEventPublisher: DomainEventPublisherInterface
let webSocketsApiUrl = 'http://test-websockets'
const createService = () =>
new WebSocketsClientService(
webSocketsConnectionRepository,
domainEventFactory,
httpClient,
webSocketsApiUrl,
logger,
)
const createService = () => new WebSocketsClientService(domainEventFactory, domainEventPublisher)
beforeEach(() => {
connectionIds = ['1', '2']
user = {
uuid: '123',
email: 'test@test.com',
@@ -45,43 +32,22 @@ describe('WebSocketsClientService', () => {
event = {} as jest.Mocked<UserRolesChangedEvent>
webSocketsConnectionRepository = {} as jest.Mocked<WebSocketsConnectionRepositoryInterface>
webSocketsConnectionRepository.findAllByUserUuid = jest.fn().mockReturnValue(connectionIds)
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createUserRolesChangedEvent = jest.fn().mockReturnValue(event)
domainEventFactory.createWebSocketMessageRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<WebSocketMessageRequestedEvent>)
httpClient = {} as jest.Mocked<AxiosInstance>
httpClient.request = jest.fn()
logger = {} as jest.Mocked<Logger>
logger.debug = jest.fn()
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
})
it('should send a user role changed event to all user connections', async () => {
it('should request a message about a user role changed', async () => {
await createService().sendUserRolesChangedEvent(user)
expect(domainEventFactory.createUserRolesChangedEvent).toHaveBeenCalledWith('123', 'test@test.com', [
RoleName.ProUser,
])
expect(httpClient.request).toHaveBeenCalledTimes(connectionIds.length)
connectionIds.map((id, index) => {
expect(httpClient.request).toHaveBeenNthCalledWith(
index + 1,
expect.objectContaining({
method: 'POST',
url: `${webSocketsApiUrl}/${id}`,
data: JSON.stringify(event),
}),
)
})
})
it('should not send a user role changed event if web sockets api url not defined', async () => {
webSocketsApiUrl = ''
await createService().sendUserRolesChangedEvent(user)
expect(httpClient.request).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
})

View File

@@ -1,52 +1,31 @@
import { AxiosInstance } from 'axios'
import { RoleName } from '@standardnotes/common'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { DomainEventFactoryInterface } from '../../Domain/Event/DomainEventFactoryInterface'
import { User } from '../../Domain/User/User'
import { WebSocketsConnectionRepositoryInterface } from '../../Domain/WebSockets/WebSocketsConnectionRepositoryInterface'
import { ClientServiceInterface } from '../../Domain/Client/ClientServiceInterface'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
@injectable()
export class WebSocketsClientService implements ClientServiceInterface {
constructor(
@inject(TYPES.WebSocketsConnectionRepository)
private webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface,
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
@inject(TYPES.WEBSOCKETS_API_URL) private webSocketsApiUrl: string,
@inject(TYPES.Logger) private logger: Logger,
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
) {}
async sendUserRolesChangedEvent(user: User): Promise<void> {
if (!this.webSocketsApiUrl) {
this.logger.debug('Web Sockets API url not defined. Skipped sending user role changed event.')
return
}
const userConnections = await this.webSocketsConnectionRepository.findAllByUserUuid(user.uuid)
const event = this.domainEventFactory.createUserRolesChangedEvent(
user.uuid,
user.email,
(await user.roles).map((role) => role.name) as RoleName[],
)
for (const connectionUuid of userConnections) {
await this.httpClient.request({
method: 'POST',
url: `${this.webSocketsApiUrl}/${connectionUuid}`,
headers: {
Accept: 'text/plain',
'Content-Type': 'text/plain',
},
data: JSON.stringify(event),
validateStatus:
/* istanbul ignore next */
(status: number) => status >= 200 && status < 500,
})
}
await this.domainEventPublisher.publish(
this.domainEventFactory.createWebSocketMessageRequestedEvent({
userUuid: user.uuid,
message: JSON.stringify(event),
}),
)
}
}

View File

@@ -9,6 +9,7 @@ export class UserProjector implements ProjectorInterface<User> {
return {
uuid: user.uuid,
email: user.email,
protocolVersion: user.version,
}
}

View File

@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.40.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.39.0...@standardnotes/common@1.40.0) (2022-10-13)
### Features
* **websockets:** add websockets service ([d28c268](https://github.com/standardnotes/server/commit/d28c268e86644f34f5550eeded5bb4a7407d4a99))
# [1.39.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.38.0...@standardnotes/common@1.39.0) (2022-10-11)
### Features
* **workspace:** extract workspace user status to common ([8bc9261](https://github.com/standardnotes/server/commit/8bc92616d2fbeb833c3fcbef6b87538745fc7f3e))
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.37.0...@standardnotes/common@1.38.0) (2022-10-11)
### Features
* **workspace:** add invite access level ([f742270](https://github.com/standardnotes/server/commit/f74227067b7151cb63a54e815e57f81984467bfe))
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.36.1...@standardnotes/common@1.37.0) (2022-10-10)
### Features
* **common:** add WORKSPACE_INVITE_CREATED email message identifier ([15d960d](https://github.com/standardnotes/server/commit/15d960d47b0bcf5aeddf869ac939eafb08166db7))
## [1.36.1](https://github.com/standardnotes/server/compare/@standardnotes/common@1.36.0...@standardnotes/common@1.36.1) (2022-10-10)
### Bug Fixes
* **workspace:** extract workspace type to common types ([0ea88ad](https://github.com/standardnotes/server/commit/0ea88ad202d54de79d1433183c1706823639da93))
# [1.36.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.35.1...@standardnotes/common@1.36.0) (2022-10-07)
### Features
* add user protocol version to the user registration event ([868b7d1](https://github.com/standardnotes/server/commit/868b7d149a572d1991b77daaa37e4c77e10f07d3))
## [1.35.1](https://github.com/standardnotes/server/compare/@standardnotes/common@1.35.0...@standardnotes/common@1.35.1) (2022-10-06)
**Note:** Version bump only for package @standardnotes/common

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/common",
"version": "1.35.1",
"version": "1.40.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -0,0 +1 @@
export type JSONString = string

View File

@@ -24,4 +24,5 @@ export enum EmailMessageIdentifier {
REFUND_NOTICE = 'REFUND_NOTICE',
REFUND_REQUESTED = 'REFUND_REQUESTED',
RATE_ADJUSTMENT_NOTICE = 'RATE_ADJUSTMENT_NOTICE',
WORKSPACE_INVITE_CREATED = 'WORKSPACE_INVITE_CREATED',
}

View File

@@ -3,6 +3,7 @@ export enum ProtocolVersion {
V002 = '002',
V003 = '003',
V004 = '004',
V005 = '005',
}
export const ProtocolVersionLatest = ProtocolVersion.V004

View File

@@ -2,6 +2,7 @@ export * from './Content/ContentType'
export * from './Content/ContentDecoder'
export * from './Content/ContentDecoderInterface'
export * from './DataType/AnyRecord'
export * from './DataType/JSONString'
export * from './DataType/MicrosecondsTimestamp'
export * from './DataType/Uuid'
export * from './DataType/ApplicationIdentifier'
@@ -24,3 +25,6 @@ export * from './Type/Either'
export * from './Type/Only'
export * from './Validator/UuidValidator'
export * from './Validator/ValidatorInterface'
export * from './Workspace/WorkspaceAccessLevel'
export * from './Workspace/WorkspaceType'
export * from './Workspace/WorkspaceUserStatus'

View File

@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.27](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.26...@standardnotes/domain-events-infra@1.8.27) (2022-10-13)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.26](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.25...@standardnotes/domain-events-infra@1.8.26) (2022-10-13)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.25](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.24...@standardnotes/domain-events-infra@1.8.25) (2022-10-11)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.24](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.23...@standardnotes/domain-events-infra@1.8.24) (2022-10-11)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.23](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.22...@standardnotes/domain-events-infra@1.8.23) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.22](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.21...@standardnotes/domain-events-infra@1.8.22) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.21](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.20...@standardnotes/domain-events-infra@1.8.21) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.20](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.19...@standardnotes/domain-events-infra@1.8.20) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.19](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.18...@standardnotes/domain-events-infra@1.8.19) (2022-10-07)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.18](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.17...@standardnotes/domain-events-infra@1.8.18) (2022-10-06)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.8.18",
"version": "1.8.27",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.68.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.67.0...@standardnotes/domain-events@2.68.0) (2022-10-13)
### Features
* publish workspace invite accepted event for websockets ([86379eb](https://github.com/standardnotes/server/commit/86379eb96d7231d6a76ee91350accef2d44a941d))
# [2.67.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.3...@standardnotes/domain-events@2.67.0) (2022-10-13)
### Features
* **websockets:** add websockets service ([d28c268](https://github.com/standardnotes/server/commit/d28c268e86644f34f5550eeded5bb4a7407d4a99))
## [2.66.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.2...@standardnotes/domain-events@2.66.3) (2022-10-11)
**Note:** Version bump only for package @standardnotes/domain-events
## [2.66.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.1...@standardnotes/domain-events@2.66.2) (2022-10-11)
**Note:** Version bump only for package @standardnotes/domain-events
## [2.66.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.0...@standardnotes/domain-events@2.66.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events
# [2.66.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.65.0...@standardnotes/domain-events@2.66.0) (2022-10-10)
### Features
* **workspace:** add publishing workspace invite created ([6f9683c](https://github.com/standardnotes/server/commit/6f9683c41a1135489832d9a854a114c82825a647))
# [2.65.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.64.1...@standardnotes/domain-events@2.65.0) (2022-10-10)
### Features
* add workspace invite created event ([db4c49c](https://github.com/standardnotes/server/commit/db4c49c57b81bfea6b8c6b8774c6a30e0561e154))
## [2.64.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.64.0...@standardnotes/domain-events@2.64.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/domain-events
# [2.64.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.63.1...@standardnotes/domain-events@2.64.0) (2022-10-07)
### Features
* add user protocol version to the user registration event ([868b7d1](https://github.com/standardnotes/server/commit/868b7d149a572d1991b77daaa37e4c77e10f07d3))
## [2.63.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.63.0...@standardnotes/domain-events@2.63.1) (2022-10-06)
**Note:** Version bump only for package @standardnotes/domain-events

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.63.1",
"version": "2.68.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -8,4 +8,5 @@ export enum DomainEventService {
ApiGateway = 'api-gateway',
Files = 'files',
Scheduler = 'scheduler',
Workspace = 'workspace',
}

View File

@@ -1,4 +1,5 @@
export interface UserRegisteredEventPayload {
userUuid: string
email: string
protocolVersion: string
}

View File

@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { WebSocketMessageRequestedEventPayload } from './WebSocketMessageRequestedEventPayload'
export interface WebSocketMessageRequestedEvent extends DomainEventInterface {
type: 'WEB_SOCKET_MESSAGE_REQUESTED'
payload: WebSocketMessageRequestedEventPayload
}

View File

@@ -0,0 +1,6 @@
import { JSONString, Uuid } from '@standardnotes/common'
export interface WebSocketMessageRequestedEventPayload {
userUuid: Uuid
message: JSONString
}

View File

@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { WorkspaceInviteAcceptedEventPayload } from './WorkspaceInviteAcceptedEventPayload'
export interface WorkspaceInviteAcceptedEvent extends DomainEventInterface {
type: 'WORKSPACE_INVITE_ACCEPTED'
payload: WorkspaceInviteAcceptedEventPayload
}

View File

@@ -0,0 +1,5 @@
export interface WorkspaceInviteAcceptedEventPayload {
inviterUuid: string
inviteeUuid: string
workspaceUuid: string
}

View File

@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { WorkspaceInviteCreatedEventPayload } from './WorkspaceInviteCreatedEventPayload'
export interface WorkspaceInviteCreatedEvent extends DomainEventInterface {
type: 'WORKSPACE_INVITE_CREATED'
payload: WorkspaceInviteCreatedEventPayload
}

View File

@@ -0,0 +1,6 @@
export interface WorkspaceInviteCreatedEventPayload {
inviterUuid: string
inviteeEmail: string
inviteUuid: string
workspaceUuid: string
}

View File

@@ -100,6 +100,12 @@ export * from './Event/UserRolesChangedEvent'
export * from './Event/UserRolesChangedEventPayload'
export * from './Event/UserSignedInEvent'
export * from './Event/UserSignedInEventPayload'
export * from './Event/WebSocketMessageRequestedEvent'
export * from './Event/WebSocketMessageRequestedEventPayload'
export * from './Event/WorkspaceInviteAcceptedEvent'
export * from './Event/WorkspaceInviteAcceptedEventPayload'
export * from './Event/WorkspaceInviteCreatedEvent'
export * from './Event/WorkspaceInviteCreatedEventPayload'
export * from './Handler/DomainEventHandlerInterface'
export * from './Handler/DomainEventMessageHandlerInterface'

View File

@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.6](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.5...@standardnotes/event-store@1.4.6) (2022-10-13)
**Note:** Version bump only for package @standardnotes/event-store
## [1.4.5](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.4...@standardnotes/event-store@1.4.5) (2022-10-13)
**Note:** Version bump only for package @standardnotes/event-store
## [1.4.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.3...@standardnotes/event-store@1.4.4) (2022-10-11)
**Note:** Version bump only for package @standardnotes/event-store
## [1.4.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.2...@standardnotes/event-store@1.4.3) (2022-10-11)
**Note:** Version bump only for package @standardnotes/event-store
## [1.4.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.1...@standardnotes/event-store@1.4.2) (2022-10-10)
**Note:** Version bump only for package @standardnotes/event-store
## [1.4.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.0...@standardnotes/event-store@1.4.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/event-store
# [1.4.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.25...@standardnotes/event-store@1.4.0) (2022-10-10)
### Features
* add workspace invite created event ([db4c49c](https://github.com/standardnotes/server/commit/db4c49c57b81bfea6b8c6b8774c6a30e0561e154))
## [1.3.25](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.24...@standardnotes/event-store@1.3.25) (2022-10-10)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.24](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.23...@standardnotes/event-store@1.3.24) (2022-10-07)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.23](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.22...@standardnotes/event-store@1.3.23) (2022-10-06)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.3.23",
"version": "1.4.6",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -86,6 +86,7 @@ export class ContainerConfigLoader {
['SUBSCRIPTION_RATE_ADJUSTED', container.get(TYPES.EventHandler)],
['REFUND_REQUESTED', container.get(TYPES.EventHandler)],
['INVOICE_GENERATED', container.get(TYPES.EventHandler)],
['WORKSPACE_INVITE_CREATED', container.get(TYPES.EventHandler)],
])
container

View File

@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.18](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.17...@standardnotes/files-server@1.6.18) (2022-10-13)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.17](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.16...@standardnotes/files-server@1.6.17) (2022-10-13)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.16](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.15...@standardnotes/files-server@1.6.16) (2022-10-11)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.15](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.14...@standardnotes/files-server@1.6.15) (2022-10-11)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.14](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.13...@standardnotes/files-server@1.6.14) (2022-10-10)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.13](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.12...@standardnotes/files-server@1.6.13) (2022-10-10)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.12](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.11...@standardnotes/files-server@1.6.12) (2022-10-10)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.11](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.10...@standardnotes/files-server@1.6.11) (2022-10-10)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.10](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.9...@standardnotes/files-server@1.6.10) (2022-10-07)
**Note:** Version bump only for package @standardnotes/files-server
## [1.6.9](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.8...@standardnotes/files-server@1.6.9) (2022-10-06)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.6.9",
"version": "1.6.18",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.11](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.10...@standardnotes/predicates@1.4.11) (2022-10-13)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.10](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.9...@standardnotes/predicates@1.4.10) (2022-10-11)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.9](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.8...@standardnotes/predicates@1.4.9) (2022-10-11)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.8](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.7...@standardnotes/predicates@1.4.8) (2022-10-10)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.7](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.6...@standardnotes/predicates@1.4.7) (2022-10-10)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.6](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.5...@standardnotes/predicates@1.4.6) (2022-10-07)
**Note:** Version bump only for package @standardnotes/predicates
## [1.4.5](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.4...@standardnotes/predicates@1.4.5) (2022-10-06)
**Note:** Version bump only for package @standardnotes/predicates

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/predicates",
"version": "1.4.5",
"version": "1.4.11",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.46](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.45...@standardnotes/scheduler-server@1.10.46) (2022-10-13)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.45](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.44...@standardnotes/scheduler-server@1.10.45) (2022-10-13)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.44](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.43...@standardnotes/scheduler-server@1.10.44) (2022-10-11)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.43](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.42...@standardnotes/scheduler-server@1.10.43) (2022-10-11)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.42](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.41...@standardnotes/scheduler-server@1.10.42) (2022-10-10)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.41](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.40...@standardnotes/scheduler-server@1.10.41) (2022-10-10)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.40](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.39...@standardnotes/scheduler-server@1.10.40) (2022-10-10)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.39](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.38...@standardnotes/scheduler-server@1.10.39) (2022-10-10)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.38](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.37...@standardnotes/scheduler-server@1.10.38) (2022-10-07)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.37](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.36...@standardnotes/scheduler-server@1.10.37) (2022-10-06)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.10.37",
"version": "1.10.46",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.9](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.8...@standardnotes/security@1.4.9) (2022-10-13)
**Note:** Version bump only for package @standardnotes/security
## [1.4.8](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.7...@standardnotes/security@1.4.8) (2022-10-11)
**Note:** Version bump only for package @standardnotes/security
## [1.4.7](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.6...@standardnotes/security@1.4.7) (2022-10-11)
**Note:** Version bump only for package @standardnotes/security
## [1.4.6](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.5...@standardnotes/security@1.4.6) (2022-10-10)
**Note:** Version bump only for package @standardnotes/security
## [1.4.5](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.4...@standardnotes/security@1.4.5) (2022-10-10)
**Note:** Version bump only for package @standardnotes/security
## [1.4.4](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.3...@standardnotes/security@1.4.4) (2022-10-07)
**Note:** Version bump only for package @standardnotes/security
## [1.4.3](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.2...@standardnotes/security@1.4.3) (2022-10-06)
**Note:** Version bump only for package @standardnotes/security

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/security",
"version": "1.4.3",
"version": "1.4.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.9.8](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.7...@standardnotes/syncing-server@1.9.8) (2022-10-13)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.7](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.6...@standardnotes/syncing-server@1.9.7) (2022-10-13)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.5...@standardnotes/syncing-server@1.9.6) (2022-10-11)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.4...@standardnotes/syncing-server@1.9.5) (2022-10-11)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.3...@standardnotes/syncing-server@1.9.4) (2022-10-10)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.2...@standardnotes/syncing-server@1.9.3) (2022-10-10)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.1...@standardnotes/syncing-server@1.9.2) (2022-10-10)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.9.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.0...@standardnotes/syncing-server@1.9.1) (2022-10-10)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.9.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.8.21...@standardnotes/syncing-server@1.9.0) (2022-10-07)
### Features
* add user protocol version to the user registration event ([868b7d1](https://github.com/standardnotes/syncing-server-js/commit/868b7d149a572d1991b77daaa37e4c77e10f07d3))
## [1.8.21](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.8.20...@standardnotes/syncing-server@1.8.21) (2022-10-06)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.8.21",
"version": "1.9.8",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -1,6 +1,7 @@
import { TimerInterface } from '@standardnotes/time'
import 'reflect-metadata'
import { TimerInterface } from '@standardnotes/time'
import { DomainEventFactory } from './DomainEventFactory'
describe('DomainEventFactory', () => {
@@ -13,24 +14,6 @@ describe('DomainEventFactory', () => {
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
})
it('should create a USER_REGISTERED event', () => {
expect(createFactory().createUserRegisteredEvent('1-2-3', 'test@test.te')).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: '1-2-3',
userIdentifierType: 'uuid',
},
origin: 'syncing-server',
},
payload: {
userUuid: '1-2-3',
email: 'test@test.te',
},
type: 'USER_REGISTERED',
})
})
it('should create a ITEMS_SYNCED event', () => {
expect(
createFactory().createItemsSyncedEvent({

View File

@@ -7,7 +7,6 @@ import {
GoogleDriveBackupFailedEvent,
ItemsSyncedEvent,
OneDriveBackupFailedEvent,
UserRegisteredEvent,
} from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
@@ -113,24 +112,6 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
}
}
createUserRegisteredEvent(userUuid: string, email: string): UserRegisteredEvent {
return {
type: 'USER_REGISTERED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
},
payload: {
userUuid,
email,
},
}
}
createEmailArchiveExtensionSyncedEvent(userUuid: string, extensionId: string): EmailArchiveExtensionSyncedEvent {
return {
type: 'EMAIL_ARCHIVE_EXTENSION_SYNCED',

View File

@@ -6,11 +6,9 @@ import {
GoogleDriveBackupFailedEvent,
ItemsSyncedEvent,
OneDriveBackupFailedEvent,
UserRegisteredEvent,
} from '@standardnotes/domain-events'
export interface DomainEventFactoryInterface {
createUserRegisteredEvent(userUuid: string, email: string): UserRegisteredEvent
createDropboxBackupFailedEvent(muteCloudEmailsSettingUuid: string, email: string): DropboxBackupFailedEvent
createGoogleDriveBackupFailedEvent(muteCloudEmailsSettingUuid: string, email: string): GoogleDriveBackupFailedEvent
createOneDriveBackupFailedEvent(muteCloudEmailsSettingUuid: string, email: string): OneDriveBackupFailedEvent

View File

@@ -0,0 +1,28 @@
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development
PORT=3000
AUTH_JWT_SECRET=auth_jwt_secret
REDIS_URL=redis://cache
SNS_TOPIC_ARN=
SNS_AWS_REGION=
SQS_QUEUE_URL=
SQS_AWS_REGION=
REDIS_EVENTS_CHANNEL=events
WEB_SOCKET_CONNECTION_TOKEN_SECRET=
WEB_SOCKET_CONNECTION_TOKEN_TTL=
# (Optional) New Relic Setup
NEW_RELIC_ENABLED=false
NEW_RELIC_APP_NAME=Websockets
NEW_RELIC_LICENSE_KEY=
NEW_RELIC_NO_CONFIG_FILE=true
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=false
NEW_RELIC_LOG_ENABLED=false
NEW_RELIC_LOG_LEVEL=info

View File

@@ -0,0 +1,3 @@
dist
test-setup.ts
data

View File

@@ -0,0 +1,6 @@
{
"extends": "../../.eslintrc",
"parserOptions": {
"project": "./linter.tsconfig.json"
}
}

View File

@@ -0,0 +1,26 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.3](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.1.2...@standardnotes/websockets-server@1.1.3) (2022-10-13)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.1.2](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.1.1...@standardnotes/websockets-server@1.1.2) (2022-10-13)
### Bug Fixes
* **websockets:** add http client binding ([4e21edc](https://github.com/standardnotes/server/commit/4e21edce6b034312f121db4dce716e82ff7d5eaa))
## [1.1.1](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.1.0...@standardnotes/websockets-server@1.1.1) (2022-10-13)
### Bug Fixes
* **websockets:** remove unnecessary sns bindings ([2f7ef49](https://github.com/standardnotes/server/commit/2f7ef497ab4875685d6a0f282eeae11005900bc3))
# 1.1.0 (2022-10-13)
### Features
* **websockets:** add websockets service ([d28c268](https://github.com/standardnotes/server/commit/d28c268e86644f34f5550eeded5bb4a7407d4a99))

View File

@@ -0,0 +1,27 @@
FROM node:16.15.1-alpine AS builder
# Install dependencies for building native libraries
RUN apk add --update git openssh-client python3 alpine-sdk
WORKDIR /workspace
# docker-build plugin copies everything needed for `yarn install` to `manifests` folder.
COPY manifests ./
RUN yarn install --immutable
FROM node:16.15.1-alpine
RUN apk add --update curl
WORKDIR /workspace
# Copy the installed dependencies from the previous stage.
COPY --from=builder /workspace ./
# docker-build plugin runs `yarn pack` in all workspace dependencies and copies them to `packs` folder.
COPY packs ./
ENTRYPOINT [ "/workspace/packages/websockets/docker/entrypoint.sh" ]
CMD [ "start-web" ]

View File

@@ -0,0 +1,70 @@
import 'reflect-metadata'
import 'newrelic'
import * as Sentry from '@sentry/node'
import '../src/Infra/InversifyExpressUtils/InversifyExpressHealthCheckController'
import '../src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController'
import * as cors from 'cors'
import { urlencoded, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
import * as winston from 'winston'
import { InversifyExpressServer } from 'inversify-express-utils'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
const env: Env = new Env()
env.load()
const server = new InversifyExpressServer(container)
server.setConfig((app) => {
app.use((_request: Request, response: Response, next: NextFunction) => {
response.setHeader('X-Websockets-Version', container.get(TYPES.VERSION))
next()
})
app.use(json())
app.use(urlencoded({ extended: true }))
app.use(cors())
if (env.get('SENTRY_DSN', true)) {
Sentry.init({
dsn: env.get('SENTRY_DSN'),
integrations: [new Sentry.Integrations.Http({ tracing: false, breadcrumbs: true })],
tracesSampleRate: 0,
})
app.use(Sentry.Handlers.requestHandler() as RequestHandler)
}
})
const logger: winston.Logger = container.get(TYPES.Logger)
server.setErrorConfig((app) => {
if (env.get('SENTRY_DSN', true)) {
app.use(Sentry.Handlers.errorHandler() as ErrorRequestHandler)
}
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
logger.error(error.stack)
response.status(500).send({
error: {
message:
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
},
})
})
})
const serverInstance = server.build()
serverInstance.listen(env.get('PORT'))
logger.info(`Server started on port ${process.env.PORT}`)
})

View File

@@ -0,0 +1,25 @@
import 'reflect-metadata'
import 'newrelic'
import { Logger } from 'winston'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-events'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
const env: Env = new Env()
env.load()
const logger: Logger = container.get(TYPES.Logger)
logger.info('Starting worker...')
const subscriberFactory: DomainEventSubscriberFactoryInterface = container.get(TYPES.DomainEventSubscriberFactory)
subscriberFactory.create().start()
setInterval(() => logger.info('Alive and kicking!'), 20 * 60 * 1000)
})

View File

@@ -0,0 +1,22 @@
#!/bin/sh
set -e
COMMAND=$1 && shift 1
case "$COMMAND" in
'start-web' )
echo "Starting Web..."
yarn workspace @standardnotes/websockets-server start
;;
'start-worker' )
echo "Starting Worker..."
yarn workspace @standardnotes/websockets-server worker
;;
* )
echo "Unknown command"
;;
esac
exec "$@"

View File

@@ -0,0 +1,12 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base = require('../../jest.config')
const { defaults: tsjPreset } = require('ts-jest/presets')
module.exports = {
...base,
transform: {
...tsjPreset.transform,
},
coveragePathIgnorePatterns: ['/Bootstrap/', '/InversifyExpressUtils/'],
setupFilesAfterEnv: ['./test-setup.ts'],
}

View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["dist", "test-setup.ts"]
}

View File

@@ -0,0 +1,59 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.1.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"private": true,
"description": "Websockets Server",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"author": "Karol Sójko <karol@standardnotes.com>",
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"setup:env": "cp .env.sample .env",
"prebuild": "yarn clean",
"build": "tsc --rootDir ./",
"lint": "eslint . --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"typeorm": "typeorm-ts-node-commonjs"
},
"dependencies": {
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/api": "^1.16.1",
"@standardnotes/common": "workspace:^",
"@standardnotes/domain-events": "workspace:^",
"@standardnotes/domain-events-infra": "workspace:^",
"@standardnotes/security": "workspace:^",
"aws-sdk": "^2.1159.0",
"axios": "^0.27.2",
"cors": "2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.2.0",
"mysql2": "^2.3.3",
"newrelic": "^9.0.0",
"reflect-metadata": "0.1.13",
"typeorm": "^0.3.6",
"winston": "^3.8.1"
},
"devDependencies": {
"@types/cors": "^2.8.9",
"@types/express": "^4.17.11",
"@types/ioredis": "^4.28.10",
"@types/jest": "^29.1.1",
"@types/newrelic": "^7.0.3",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"eslint": "^8.14.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^29.1.2",
"ts-jest": "^29.0.3"
}
}

View File

@@ -0,0 +1,177 @@
import * as winston from 'winston'
import axios, { AxiosInstance } from 'axios'
import Redis from 'ioredis'
import * as AWS from 'aws-sdk'
import { Container } from 'inversify'
import {
DomainEventHandlerInterface,
DomainEventMessageHandlerInterface,
DomainEventSubscriberFactoryInterface,
} from '@standardnotes/domain-events'
import { Env } from './Env'
import TYPES from './Types'
import { WebSocketsConnectionRepositoryInterface } from '../Domain/WebSockets/WebSocketsConnectionRepositoryInterface'
import { RedisWebSocketsConnectionRepository } from '../Infra/Redis/RedisWebSocketsConnectionRepository'
import { AddWebSocketsConnection } from '../Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection'
import { RemoveWebSocketsConnection } from '../Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection'
import { WebSocketsClientMessenger } from '../Infra/WebSockets/WebSocketsClientMessenger'
import {
RedisDomainEventSubscriberFactory,
RedisEventMessageHandler,
SQSDomainEventSubscriberFactory,
SQSEventMessageHandler,
SQSNewRelicEventMessageHandler,
} from '@standardnotes/domain-events-infra'
import { ApiGatewayAuthMiddleware } from '../Controller/ApiGatewayAuthMiddleware'
import {
CrossServiceTokenData,
TokenDecoder,
TokenDecoderInterface,
TokenEncoder,
TokenEncoderInterface,
WebSocketConnectionTokenData,
} from '@standardnotes/security'
import { CreateWebSocketConnectionToken } from '../Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken'
import { WebSocketsController } from '../Controller/WebSocketsController'
import { WebSocketServerInterface } from '@standardnotes/api'
import { ClientMessengerInterface } from '../Client/ClientMessengerInterface'
import { WebSocketMessageRequestedEventHandler } from '../Domain/Handler/WebSocketMessageRequestedEventHandler'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
const env: Env = new Env()
env.load()
const container = new Container()
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
if (isRedisInClusterMode) {
redis = new Redis.Cluster(redisUrl.split(','))
} else {
redis = new Redis(redisUrl)
}
container.bind(TYPES.Redis).toConstantValue(redis)
const newrelicWinstonFormatter = newrelicFormatter(winston)
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
if (env.get('SQS_QUEUE_URL', true)) {
const sqsConfig: AWS.SQS.Types.ClientConfiguration = {
apiVersion: 'latest',
region: env.get('SQS_AWS_REGION', true),
}
if (env.get('SQS_ACCESS_KEY_ID', true) && env.get('SQS_SECRET_ACCESS_KEY', true)) {
sqsConfig.credentials = {
accessKeyId: env.get('SQS_ACCESS_KEY_ID', true),
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
}
}
container.bind<AWS.SQS>(TYPES.SQS).toConstantValue(new AWS.SQS(sqsConfig))
}
// Controller
container.bind<WebSocketServerInterface>(TYPES.WebSocketsController).to(WebSocketsController)
// Repositories
container
.bind<WebSocketsConnectionRepositoryInterface>(TYPES.WebSocketsConnectionRepository)
.to(RedisWebSocketsConnectionRepository)
// Middleware
container.bind<ApiGatewayAuthMiddleware>(TYPES.ApiGatewayAuthMiddleware).to(ApiGatewayAuthMiddleware)
// env vars
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
container
.bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
container
.bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_TTL)
.toConstantValue(+env.get('WEB_SOCKET_CONNECTION_TOKEN_TTL', true))
container.bind(TYPES.REDIS_URL).toConstantValue(env.get('REDIS_URL'))
container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true))
container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL'))
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
container.bind(TYPES.WEBSOCKETS_API_URL).toConstantValue(env.get('WEBSOCKETS_API_URL', true))
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
// use cases
container.bind<AddWebSocketsConnection>(TYPES.AddWebSocketsConnection).to(AddWebSocketsConnection)
container.bind<RemoveWebSocketsConnection>(TYPES.RemoveWebSocketsConnection).to(RemoveWebSocketsConnection)
container
.bind<CreateWebSocketConnectionToken>(TYPES.CreateWebSocketConnectionToken)
.to(CreateWebSocketConnectionToken)
// Handlers
container
.bind<WebSocketMessageRequestedEventHandler>(TYPES.WebSocketMessageRequestedEventHandler)
.to(WebSocketMessageRequestedEventHandler)
// Services
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
container
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
.toConstantValue(new TokenDecoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
container
.bind<TokenEncoderInterface<WebSocketConnectionTokenData>>(TYPES.WebSocketConnectionTokenEncoder)
.toConstantValue(
new TokenEncoder<WebSocketConnectionTokenData>(container.get(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)),
)
container.bind<ClientMessengerInterface>(TYPES.WebSocketsClientMessenger).to(WebSocketsClientMessenger)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['WEB_SOCKET_MESSAGE_REQUESTED', container.get(TYPES.WebSocketMessageRequestedEventHandler)],
])
if (env.get('SQS_QUEUE_URL', true)) {
container
.bind<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler)
.toConstantValue(
env.get('NEW_RELIC_ENABLED', true) === 'true'
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Logger))
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Logger)),
)
container
.bind<DomainEventSubscriberFactoryInterface>(TYPES.DomainEventSubscriberFactory)
.toConstantValue(
new SQSDomainEventSubscriberFactory(
container.get(TYPES.SQS),
container.get(TYPES.SQS_QUEUE_URL),
container.get(TYPES.DomainEventMessageHandler),
),
)
} else {
container
.bind<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler)
.toConstantValue(new RedisEventMessageHandler(eventHandlers, container.get(TYPES.Logger)))
container
.bind<DomainEventSubscriberFactoryInterface>(TYPES.DomainEventSubscriberFactory)
.toConstantValue(
new RedisDomainEventSubscriberFactory(
container.get(TYPES.Redis),
container.get(TYPES.DomainEventMessageHandler),
container.get(TYPES.REDIS_EVENTS_CHANNEL),
),
)
}
return container
}
}

View File

@@ -0,0 +1,24 @@
import { config, DotenvParseOutput } from 'dotenv'
import { injectable } from 'inversify'
@injectable()
export class Env {
private env?: DotenvParseOutput
public load(): void {
const output = config()
this.env = <DotenvParseOutput>output.parsed
}
public get(key: string, optional = false): string {
if (!this.env) {
this.load()
}
if (!process.env[key] && !optional) {
throw new Error(`Environment variable ${key} not set`)
}
return <string>process.env[key]
}
}

View File

@@ -0,0 +1,37 @@
const TYPES = {
Logger: Symbol.for('Logger'),
Redis: Symbol.for('Redis'),
SQS: Symbol.for('SQS'),
// Controller
WebSocketsController: Symbol.for('WebSocketsController'),
// Repositories
WebSocketsConnectionRepository: Symbol.for('WebSocketsConnectionRepository'),
// Middleware
ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'),
// env vars
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
WEB_SOCKET_CONNECTION_TOKEN_SECRET: Symbol.for('WEB_SOCKET_CONNECTION_TOKEN_SECRET'),
WEB_SOCKET_CONNECTION_TOKEN_TTL: Symbol.for('WEB_SOCKET_CONNECTION_TOKEN_TTL'),
REDIS_URL: Symbol.for('REDIS_URL'),
SQS_QUEUE_URL: Symbol.for('SQS_QUEUE_URL'),
SQS_AWS_REGION: Symbol.for('SQS_AWS_REGION'),
REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'),
NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'),
WEBSOCKETS_API_URL: Symbol.for('WEBSOCKETS_API_URL'),
VERSION: Symbol.for('VERSION'),
// use cases
AddWebSocketsConnection: Symbol.for('AddWebSocketsConnection'),
RemoveWebSocketsConnection: Symbol.for('RemoveWebSocketsConnection'),
CreateWebSocketConnectionToken: Symbol.for('CreateWebSocketConnectionToken'),
// Handlers
WebSocketMessageRequestedEventHandler: Symbol.for('WebSocketMessageRequestedEventHandler'),
// Services
CrossServiceTokenDecoder: Symbol.for('CrossServiceTokenDecoder'),
WebSocketConnectionTokenEncoder: Symbol.for('WebSocketConnectionTokenEncoder'),
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
DomainEventMessageHandler: Symbol.for('DomainEventMessageHandler'),
HTTPClient: Symbol.for('HTTPClient'),
WebSocketsClientMessenger: Symbol.for('WebSocketsClientMessenger'),
}
export default TYPES

View File

@@ -0,0 +1,5 @@
import { JSONString, Uuid } from '@standardnotes/common'
export interface ClientMessengerInterface {
send(userUuid: Uuid, message: JSONString): Promise<void>
}

View File

@@ -0,0 +1,99 @@
import 'reflect-metadata'
import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware'
import { NextFunction, Request, Response } from 'express'
import { Logger } from 'winston'
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { RoleName } from '@standardnotes/common'
describe('ApiGatewayAuthMiddleware', () => {
let tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>
let request: Request
let response: Response
let next: NextFunction
const logger = {
debug: jest.fn(),
} as unknown as jest.Mocked<Logger>
const createMiddleware = () => new ApiGatewayAuthMiddleware(tokenDecoder, logger)
beforeEach(() => {
tokenDecoder = {} as jest.Mocked<TokenDecoderInterface<CrossServiceTokenData>>
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
user: {
uuid: '1-2-3',
email: 'test@test.te',
},
roles: [
{
uuid: 'a-b-c',
name: RoleName.CoreUser,
},
],
})
request = {
headers: {},
} as jest.Mocked<Request>
response = {
locals: {},
} as jest.Mocked<Response>
response.status = jest.fn().mockReturnThis()
response.send = jest.fn()
next = jest.fn()
})
it('should authorize user', async () => {
request.headers['x-auth-token'] = 'auth-jwt-token'
await createMiddleware().handler(request, response, next)
expect(response.locals.user).toEqual({
uuid: '1-2-3',
email: 'test@test.te',
})
expect(response.locals.roles).toEqual([
{
uuid: 'a-b-c',
name: RoleName.CoreUser,
},
])
expect(next).toHaveBeenCalled()
})
it('should not authorize if request is missing auth jwt token in headers', async () => {
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(401)
expect(next).not.toHaveBeenCalled()
})
it('should not authorize if auth jwt token is malformed', async () => {
request.headers['x-auth-token'] = 'auth-jwt-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue(undefined)
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(401)
expect(next).not.toHaveBeenCalled()
})
it('should pass the error to next middleware if one occurres', async () => {
request.headers['x-auth-token'] = 'auth-jwt-token'
const error = new Error('Ooops')
tokenDecoder.decodeToken = jest.fn().mockImplementation(() => {
throw error
})
await createMiddleware().handler(request, response, next)
expect(response.status).not.toHaveBeenCalled()
expect(next).toHaveBeenCalledWith(error)
})
})

View File

@@ -0,0 +1,59 @@
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
@injectable()
export class ApiGatewayAuthMiddleware extends BaseMiddleware {
constructor(
@inject(TYPES.CrossServiceTokenDecoder) private tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
try {
if (!request.headers['x-auth-token']) {
this.logger.debug('ApiGatewayAuthMiddleware missing x-auth-token header.')
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
return
}
const token: CrossServiceTokenData | undefined = this.tokenDecoder.decodeToken(
request.headers['x-auth-token'] as string,
)
if (token === undefined) {
this.logger.debug('ApiGatewayAuthMiddleware authentication failure.')
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
return
}
response.locals.user = token.user
response.locals.roles = token.roles
response.locals.session = token.session
response.locals.readOnlyAccess = token.session?.readonly_access ?? false
return next()
} catch (error) {
return next(error)
}
}
}

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