Compare commits

..

45 Commits

Author SHA1 Message Date
standardci
b57816bba4 chore(release): publish new version
- @standardnotes/api-gateway@1.1.5
 - @standardnotes/auth-server@1.1.5
 - @standardnotes/files-server@1.1.5
 - @standardnotes/scheduler-server@1.1.5
 - @standardnotes/syncing-server@1.1.5
2022-06-27 04:59:52 +00:00
Karol Sójko
b6db194a22 fix: upgrade sentry node sdk 2022-06-27 06:59:15 +02:00
standardci
8f708164cd chore(release): publish new version
- @standardnotes/api-gateway@1.1.4
 - @standardnotes/auth-server@1.1.4
 - @standardnotes/files-server@1.1.4
 - @standardnotes/scheduler-server@1.1.4
 - @standardnotes/syncing-server@1.1.4
2022-06-24 13:19:23 +00:00
Karol Sójko
ff09ae0a47 fix: newrelic deps and setup db and cache for local development purposes 2022-06-24 15:18:41 +02:00
Karol Sójko
d21d752029 fix: newrelic deploy markers on syncing-server 2022-06-23 19:42:23 +02:00
standardci
2ea077a7cd chore(release): publish new version
- @standardnotes/api-gateway@1.1.3
 - @standardnotes/auth-server@1.1.3
 - @standardnotes/files-server@1.1.3
 - @standardnotes/scheduler-server@1.1.3
 - @standardnotes/syncing-server@1.1.3
2022-06-23 17:23:35 +00:00
Karol Sójko
0d67c55e12 fix: curl in the final image 2022-06-23 19:22:52 +02:00
Karol Sójko
8837dca039 fix: pushing tags one by one on release 2022-06-23 18:55:55 +02:00
standardci
c9ec846a3c chore(release): publish new version
- @standardnotes/api-gateway@1.1.2
 - @standardnotes/auth-server@1.1.2
 - @standardnotes/files-server@1.1.2
 - @standardnotes/scheduler-server@1.1.2
 - @standardnotes/syncing-server@1.1.2
2022-06-23 16:49:22 +00:00
Karol Sójko
786829f317 fix: workflow step names in scheduler 2022-06-23 18:48:37 +02:00
Karol Sójko
10891af33b Merge branch 'main' of github.com:standardnotes/server 2022-06-23 18:47:18 +02:00
Karol Sójko
3f091175e2 fix: workflow step names 2022-06-23 18:47:13 +02:00
standardci
0e5d7c918b chore(release): publish new version
- @standardnotes/api-gateway@1.1.1
 - @standardnotes/auth-server@1.1.1
 - @standardnotes/files-server@1.1.1
 - @standardnotes/scheduler-server@1.1.1
 - @standardnotes/syncing-server@1.1.1
2022-06-23 16:40:39 +00:00
Karol Sójko
fd2358a4b8 Merge branch 'main' of github.com:standardnotes/server 2022-06-23 18:39:49 +02:00
Karol Sójko
dd36b1859c feat: prepare auth for one branch only development 2022-06-23 18:39:42 +02:00
standardci
8c9a8a484f chore(release): publish new version
- @standardnotes/api-gateway@1.1.0
 - @standardnotes/auth-server@1.1.0
 - @standardnotes/files-server@1.1.0
 - @standardnotes/scheduler-server@1.1.0
 - @standardnotes/syncing-server@1.1.0
2022-06-23 16:36:52 +00:00
Karol Sójko
451ed1ae3a Merge branch 'main' of github.com:standardnotes/server 2022-06-23 18:36:03 +02:00
Karol Sójko
4ec30df2dc fix: versioning on push 2022-06-23 18:35:48 +02:00
Karol Sójko
163b7ff2d8 feat: prepare api-gateway for one branch only development 2022-06-23 18:34:03 +02:00
standardci
6e136e98b3 chore(release): publish new version
- @standardnotes/api-gateway@1.1.0-alpha.4
 - @standardnotes/auth-server@1.1.0-alpha.8
 - @standardnotes/files-server@1.1.0-alpha.6
 - @standardnotes/scheduler-server@1.1.0-alpha.16
 - @standardnotes/syncing-server@1.1.0-alpha.10
2022-06-23 16:28:36 +00:00
Karol Sójko
100eef2cb8 feat: prepare files server and scheduler for one branch only development 2022-06-23 18:27:37 +02:00
Karol Sójko
1d8cf4b675 fix: rename syncing-server release workflow 2022-06-23 14:16:13 +02:00
Karol Sójko
5a01517097 Merge branch 'develop' of github.com:standardnotes/server into develop 2022-06-23 14:11:50 +02:00
Karol Sójko
ca54d4e0a0 feat: prepare syncing server for one branch only development 2022-06-23 14:11:23 +02:00
standardci
2bcc4a2254 chore(release): publish
- @standardnotes/api-gateway@1.1.0-alpha.3
 - @standardnotes/auth-server@1.1.0-alpha.7
 - @standardnotes/files-server@1.1.0-alpha.5
 - @standardnotes/scheduler-server@1.1.0-alpha.15
 - @standardnotes/syncing-server@1.1.0-alpha.9
2022-06-23 11:49:15 +00:00
Karol Sójko
afe5ff3e70 Merge branch 'develop' of github.com:standardnotes/server into develop 2022-06-23 13:48:31 +02:00
Karol Sójko
4d8b021284 fix: remove not working discord notifications 2022-06-23 13:48:20 +02:00
standardci
281dd3d378 chore(release): publish
- @standardnotes/api-gateway@1.1.0-alpha.2
 - @standardnotes/auth-server@1.1.0-alpha.6
 - @standardnotes/files-server@1.1.0-alpha.4
 - @standardnotes/scheduler-server@1.1.0-alpha.14
 - @standardnotes/syncing-server@1.1.0-alpha.8
2022-06-23 11:37:21 +00:00
Karol Sójko
7efb48dd2a fix: add missing curl to docker image for healthcheck purposes 2022-06-23 13:36:32 +02:00
standardci
d7b68bcafb chore(release): publish
- @standardnotes/syncing-server@1.1.0-alpha.7
2022-06-23 10:23:58 +00:00
Karol Sójko
04d1dffe53 Merge branch 'develop' of github.com:standardnotes/server into develop 2022-06-23 12:23:08 +02:00
Karol Sójko
db492c3787 fix: add install to the test process of syncing-server to compile microtime package 2022-06-23 12:22:59 +02:00
standardci
d04b04507a chore(release): publish
- @standardnotes/syncing-server@1.1.0-alpha.6
2022-06-23 10:14:16 +00:00
Karol Sójko
6c87d3614d fix: upgrade time lib for syncing-server 2022-06-23 12:13:28 +02:00
Karol Sójko
dd6d409ebb fix: docker hub building process 2022-06-23 11:47:43 +02:00
standardci
9f75c2b601 chore(release): publish
- @standardnotes/api-gateway@1.1.0-alpha.1
 - @standardnotes/files-server@1.1.0-alpha.3
 - @standardnotes/syncing-server@1.1.0-alpha.5
2022-06-23 09:41:40 +00:00
Karol Sójko
9df87a0e3d fix: local builds before dockage image build 2022-06-23 11:40:52 +02:00
Karol Sójko
628dcf1539 Merge branch 'develop' of github.com:standardnotes/server into develop 2022-06-23 11:40:41 +02:00
Karol Sójko
38b42dad62 fix: bump docker github action version 2022-06-23 11:37:25 +02:00
standardci
90359d61d9 chore(release): publish
- @standardnotes/api-gateway@1.1.0-alpha.0
 - @standardnotes/files-server@1.1.0-alpha.2
 - @standardnotes/syncing-server@1.1.0-alpha.4
2022-06-23 09:34:02 +00:00
Karol Sójko
57c3b9c29e feat: add api-gateway package 2022-06-23 11:33:14 +02:00
Karol Sójko
b25f2e8c54 fix: remove unnessary cp of dotenv file 2022-06-23 08:50:55 +02:00
standardci
5be40fa99c chore(release): publish
- @standardnotes/auth-server@1.1.0-alpha.5
 - @standardnotes/files-server@1.1.0-alpha.1
 - @standardnotes/scheduler-server@1.1.0-alpha.13
 - @standardnotes/syncing-server@1.1.0-alpha.3
2022-06-22 14:48:20 +00:00
Karol Sójko
bc909dd3aa Merge branch 'develop' of github.com:standardnotes/server into develop 2022-06-22 16:47:36 +02:00
Karol Sójko
3110c20596 fix: make DISABLE_USER_REGISTRATION env var optional 2022-06-22 16:47:30 +02:00
97 changed files with 3293 additions and 865 deletions

6
.env.sample Normal file
View File

@@ -0,0 +1,6 @@
DB_PORT=3306
DB_USERNAME=std_notes_user
DB_PASSWORD=changeme123
DB_DATABASE=standard_notes_db
REDIS_PORT=6379

View File

@@ -0,0 +1,141 @@
name: Api Gateway
concurrency:
group: api_gateway
cancel-in-progress: true
on:
push:
tags:
- '*standardnotes/api-gateway*'
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v1
with:
node-version: '16.x'
- run: yarn lint:api-gateway
publish-aws-ecr:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:api-gateway
- 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: api-gateway
IMAGE_TAG: ${{ github.sha }}
run: |
yarn docker build @standardnotes/api-gateway -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: Build locally
run: yarn build:api-gateway
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build, tag, and push image to Docker Hub
run: |
yarn docker build @standardnotes/api-gateway -t standardnotes/api-gateway:${{ github.sha }}
docker push standardnotes/api-gateway:${{ github.sha }}
docker tag standardnotes/api-gateway:${{ github.sha }} standardnotes/api-gateway:latest
docker push standardnotes/api-gateway: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: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition api-gateway-dev --query taskDefinition > task-definition.json
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="api-gateway-dev") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: api-gateway-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/api-gateway:${{ github.sha }}
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: api-gateway-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition api-gateway-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="api-gateway-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: api-gateway-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/api-gateway:${{ github.sha }}
- name: PROD - 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: api-gateway-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: deploy-web
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_API_GATEWAY_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"

View File

@@ -1,14 +1,13 @@
name: Auth Server Dev
name: Auth Server
concurrency:
group: auth_dev_environment
group: auth
cancel-in-progress: true
on:
push:
tags:
- '@standardnotes/auth-server@[0-9]*.[0-9]*.[0-9]*-alpha.[0-9]*'
- '@standardnotes/auth-server@[0-9]*.[0-9]*.[0-9]*-beta.[0-9]*'
- '*standardnotes/auth-server*'
workflow_dispatch:
jobs:
@@ -50,8 +49,8 @@ jobs:
run: |
yarn docker build @standardnotes/auth-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:dev
docker push $ECR_REGISTRY/$ECR_REPOSITORY:dev
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
@@ -71,8 +70,8 @@ jobs:
run: |
yarn docker build @standardnotes/auth-server -t standardnotes/auth:${{ github.sha }}
docker push standardnotes/auth:${{ github.sha }}
docker tag standardnotes/auth:${{ github.sha }} standardnotes/auth:dev
docker push standardnotes/auth:dev
docker tag standardnotes/auth:${{ github.sha }} standardnotes/auth:latest
docker push standardnotes/auth:latest
deploy-web:
needs: publish-aws-ecr
@@ -86,26 +85,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition auth-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="auth-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: auth-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/auth:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: auth-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition auth-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="auth-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: auth-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/auth:${{ github.sha }}
- name: PROD - 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: auth-prod
cluster: prod
wait-for-service-stability: true
deploy-worker:
needs: publish-aws-ecr
@@ -119,26 +138,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition auth-worker-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="auth-worker-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: auth-worker-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/auth:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: auth-worker-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition auth-worker-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="auth-worker-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: auth-worker-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/auth:${{ github.sha }}
- name: PROD - 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: auth-worker-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: [ deploy-web, deploy-worker ]
@@ -150,7 +189,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_AUTH_WEB_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_AUTH_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
@@ -159,19 +198,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_AUTH_WORKER_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_AUTH_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
notify_discord:
needs: [ deploy-web, deploy-worker ]
runs-on: ubuntu-latest
steps:
- name: Run Discord Webhook
uses: johnnyhuy/actions-discord-git-webhook@main
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -1,14 +1,13 @@
name: Files Server Dev
name: Files Server
concurrency:
group: files_dev_environment
group: files
cancel-in-progress: true
on:
push:
tags:
- '@standardnotes/files-server@[0-9]*.[0-9]*.[0-9]*-alpha.[0-9]*'
- '@standardnotes/files-server@[0-9]*.[0-9]*.[0-9]*-beta.[0-9]*'
- '*standardnotes/files-server*'
workflow_dispatch:
jobs:
@@ -29,8 +28,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: cp .env.sample .env
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:files
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -47,10 +47,10 @@ jobs:
ECR_REPOSITORY: files
IMAGE_TAG: ${{ github.sha }}
run: |
yarn docker build @standardnotes/files -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
yarn docker build @standardnotes/files-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:dev
docker push $ECR_REGISTRY/$ECR_REPOSITORY:dev
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
@@ -58,15 +58,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: cp .env.sample .env
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@master
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:files
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
name: standardnotes/files
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "dev,${{ github.sha }}"
- name: Build, tag, and push image to Docker Hub
run: |
yarn docker build @standardnotes/files-server -t standardnotes/files:${{ github.sha }}
docker push standardnotes/files:${{ github.sha }}
docker tag standardnotes/files:${{ github.sha }} standardnotes/files:latest
docker push standardnotes/files:latest
deploy-web:
needs: publish-aws-ecr
@@ -80,26 +85,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition files-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="files-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: files-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/files:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: files-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition files-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="files-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: files-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/files:${{ github.sha }}
- name: PROD - 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: files-prod
cluster: prod
wait-for-service-stability: true
deploy-worker:
needs: publish-aws-ecr
@@ -113,26 +138,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition files-worker-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="files-worker-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: files-worker-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/files:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: files-worker-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition files-worker-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="files-worker-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: files-worker-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/files:${{ github.sha }}
- name: PROD - 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: files-worker-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: [ deploy-web, deploy-worker ]
@@ -144,7 +189,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_FILES_WEB_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_FILES_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
@@ -153,18 +198,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_FILES_WORKER_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_FILES_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
notify_discord:
needs: [ deploy-web, deploy-worker ]
runs-on: ubuntu-latest
steps:
- name: Run Discord Webhook
uses: johnnyhuy/actions-discord-git-webhook@main
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -1,14 +1,13 @@
name: Scheduler Server Dev
name: Scheduler Server
concurrency:
group: scheduler_dev_environment
group: scheduler
cancel-in-progress: true
on:
push:
tags:
- '@standardnotes/scheduler-server@[0-9]*.[0-9]*.[0-9]*-alpha.[0-9]*'
- '@standardnotes/scheduler-server@[0-9]*.[0-9]*.[0-9]*-beta.[0-9]*'
- '*standardnotes/scheduler-server*'
workflow_dispatch:
jobs:
@@ -50,8 +49,8 @@ jobs:
run: |
yarn docker build @standardnotes/scheduler-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:dev
docker push $ECR_REGISTRY/$ECR_REPOSITORY:dev
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
@@ -71,8 +70,8 @@ jobs:
run: |
yarn docker build @standardnotes/scheduler-server -t standardnotes/scheduler:${{ github.sha }}
docker push standardnotes/scheduler:${{ github.sha }}
docker tag standardnotes/scheduler:${{ github.sha }} standardnotes/scheduler:dev
docker push standardnotes/scheduler:dev
docker tag standardnotes/scheduler:${{ github.sha }} standardnotes/scheduler:latest
docker push standardnotes/scheduler:latest
deploy-worker:
needs: publish-aws-ecr
@@ -86,26 +85,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition scheduler-worker-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="scheduler-worker-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: scheduler-worker-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/scheduler-worker:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: scheduler-worker-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition scheduler-worker-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="scheduler-worker-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: scheduler-worker-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/scheduler-worker:${{ github.sha }}
- name: PROD - 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: scheduler-worker-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: [ deploy-worker ]
@@ -118,19 +137,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SCHEDULER_WORKER_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SCHEDULER_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
notify_discord:
needs: [ deploy-worker ]
runs-on: ubuntu-latest
steps:
- name: Run Discord Webhook
uses: johnnyhuy/actions-discord-git-webhook@main
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -1,14 +1,13 @@
name: Syncing Server Dev
name: Syncing Server
concurrency:
group: syncing_server_dev_environment
group: syncing_server
cancel-in-progress: true
on:
push:
tags:
- '@standardnotes/syncing-server@[0-9]*.[0-9]*.[0-9]*-alpha.[0-9]*'
- '@standardnotes/syncing-server@[0-9]*.[0-9]*.[0-9]*-beta.[0-9]*'
- '*standardnotes/syncing-server*'
workflow_dispatch:
jobs:
@@ -20,6 +19,7 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: '16.x'
- run: yarn install --immutable
- run: yarn lint:syncing-server
- run: yarn test:syncing-server
@@ -29,8 +29,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: cp .env.sample .env
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:syncing-server
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -49,8 +50,8 @@ jobs:
run: |
yarn docker build @standardnotes/syncing-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:dev
docker push $ECR_REGISTRY/$ECR_REPOSITORY:dev
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
@@ -58,15 +59,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: cp .env.sample .env
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@master
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:syncing-server
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
name: standardnotes/syncing-server-js
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "dev,${{ github.sha }}"
- name: Build, tag, and push image to Docker Hub
run: |
yarn docker build @standardnotes/syncing-server -t standardnotes/syncing-server-js:${{ github.sha }}
docker push standardnotes/syncing-server-js:${{ github.sha }}
docker tag standardnotes/syncing-server-js:${{ github.sha }} standardnotes/syncing-server-js:latest
docker push standardnotes/syncing-server-js:latest
deploy-web:
needs: publish-aws-ecr
@@ -80,26 +86,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition syncing-server-js-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="syncing-server-js-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: syncing-server-js-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/syncing-server-js:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: syncing-server-js-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition syncing-server-js-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="syncing-server-js-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: syncing-server-js-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/syncing-server-js:${{ github.sha }}
- name: PROD - 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: syncing-server-js-prod
cluster: prod
wait-for-service-stability: true
deploy-worker:
needs: publish-aws-ecr
@@ -113,26 +139,46 @@ jobs:
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
- name: DEV - Download task definition
run: |
aws ecs describe-task-definition --task-definition syncing-server-js-worker-dev --query taskDefinition > task-definition.json
- name: Fill in the new version in the Amazon ECS task definition
- name: DEV - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="syncing-server-js-worker-dev") | .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
- name: DEV - Fill in the new image ID in the Amazon ECS task definition
id: task-def-dev
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: syncing-server-js-worker-dev
image: ${{ secrets.AWS_ECR_REGISTRY }}/syncing-server-js:${{ github.sha }}
- name: Deploy Amazon ECS task definition
- name: DEV - Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
task-definition: ${{ steps.task-def-dev.outputs.task-definition }}
service: syncing-server-js-worker-dev
cluster: dev
wait-for-service-stability: true
- name: PROD - Download task definition
run: |
aws ecs describe-task-definition --task-definition syncing-server-js-worker-prod --query taskDefinition > task-definition.json
- name: PROD - Fill in the new version in the Amazon ECS task definition
run: |
jq '(.containerDefinitions[] | select(.name=="syncing-server-js-worker-prod") | .environment[] | select(.name=="VERSION")).value = "${{ github.sha }}"' task-definition.json > tmp.json && mv tmp.json task-definition.json
- name: PROD - 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: syncing-server-js-worker-prod
image: ${{ secrets.AWS_ECR_REGISTRY }}/syncing-server-js:${{ github.sha }}
- name: PROD - 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: syncing-server-js-worker-prod
cluster: prod
wait-for-service-stability: true
newrelic:
needs: [ deploy-web, deploy-worker ]
@@ -144,7 +190,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WEB_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
@@ -153,18 +199,7 @@ jobs:
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WORKER_DEV }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
notify_discord:
needs: [ deploy-web, deploy-worker ]
runs-on: ubuntu-latest
steps:
- name: Run Discord Webhook
uses: johnnyhuy/actions-discord-git-webhook@main
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -2,7 +2,7 @@ name: Version Bump
on:
push:
branches: [ develop, main ]
branches: [ main ]
jobs:
bump:
@@ -35,10 +35,5 @@ jobs:
- name: Install locally
run: yarn install --immutable
- name: Bump Prod Version
if: ${{ github.ref == 'refs/heads/main' }}
- name: Bump Version
run: yarn release:prod
- name: Bump Beta Version
if: ${{ github.ref == 'refs/heads/develop' }}
run: yarn release:beta

3
.gitignore vendored
View File

@@ -18,3 +18,6 @@ newrelic_agent.log
packages/files/uploads/*
!packages/files/uploads/.gitkeep
data/*
!data/.gitkeep

823
.pnp.cjs generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

0
data/.gitkeep Normal file
View File

24
docker-compose.yml Normal file
View File

@@ -0,0 +1,24 @@
version: '3.8'
services:
db:
image: mysql:5.6
environment:
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
expose:
- ${DB_PORT}:3306
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8 --collation-server=utf8_general_ci
volumes:
- ./data/mysql:/var/lib/mysql
- ./data/import:/docker-entrypoint-initdb.d
cache:
image: redis:6.0-alpine
volumes:
- ./data/redis/:/data
expose:
- ${REDIS_PORT}:6379
restart: unless-stopped

View File

@@ -16,18 +16,20 @@
"lint:scheduler": "yarn workspace @standardnotes/scheduler-server lint",
"lint:syncing-server": "yarn workspace @standardnotes/syncing-server lint",
"lint:files": "yarn workspace @standardnotes/files-server lint",
"lint:api-gateway": "yarn workspace @standardnotes/api-gateway lint",
"test": "yarn workspaces foreach -p -j 10 --verbose run test",
"test:auth": "yarn workspace @standardnotes/auth-server test",
"test:scheduler": "yarn workspace @standardnotes/scheduler-server test",
"test:syncing-server": "yarn workspace @standardnotes/syncing-server test",
"test:files": "yarn workspace @standardnotes/files-server test",
"clean": "yarn workspaces foreach -p --verbose run clean",
"setup:env": "yarn workspaces foreach -p --verbose run setup:env",
"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",
"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",
@@ -35,7 +37,9 @@
"start:syncing-server-worker": "yarn workspace @standardnotes/syncing-server worker",
"start:files": "yarn workspace @standardnotes/files-server start",
"start:files-worker": "yarn workspace @standardnotes/files-server worker",
"release:beta": "lerna version --conventional-prerelease --conventional-commits --yes -m \"chore(release): publish\""
"start:api-gateway": "yarn workspace @standardnotes/api-gateway start",
"release:prod": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
"postversion": "./scripts/push-tags-one-by-one.sh"
},
"devDependencies": {
"@commitlint/cli": "^17.0.2",
@@ -44,6 +48,8 @@
"@lerna-lite/list": "^1.5.1",
"@lerna-lite/run": "^1.5.1",
"@types/jest": "^28.1.3",
"@types/newrelic": "^7.0.3",
"@types/node": "^18.0.0",
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
@@ -51,5 +57,9 @@
"ts-node": "^10.8.1",
"typescript": "^4.7.4"
},
"packageManager": "yarn@3.2.1"
"packageManager": "yarn@3.2.1",
"dependencies": {
"@sentry/node": "^7.3.0",
"newrelic": "^8.14.1"
}
}

View File

@@ -0,0 +1,33 @@
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development
PORT=3000
SYNCING_SERVER_JS_URL=http://syncing_server_js:3000
AUTH_SERVER_URL=http://auth:3000
PAYMENTS_SERVER_URL=http://payments:3000
FILES_SERVER_URL=http://files:3000
HTTP_CALL_TIMEOUT=60000
AUTH_JWT_SECRET=auth_jwt_secret
# (Optional) New Relic Setup
NEW_RELIC_ENABLED=false
NEW_RELIC_APP_NAME=API Gateway
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
REDIS_URL=redis://cache
REDIS_EVENTS_CHANNEL=events
# (Optional) SNS Setup
SNS_TOPIC_ARN=
SNS_AWS_REGION=
# (Optional) Caching Cross Service Tokens
CROSS_SERVICE_TOKEN_CACHE_TTL=

View File

@@ -0,0 +1,2 @@
dist
test-setup.ts

View File

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

View File

@@ -0,0 +1,58 @@
# 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.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.4...@standardnotes/api-gateway@1.1.5) (2022-06-27)
### Bug Fixes
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/api-gateway/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
## [1.1.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.3...@standardnotes/api-gateway@1.1.4) (2022-06-24)
### Bug Fixes
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/api-gateway/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
## [1.1.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.2...@standardnotes/api-gateway@1.1.3) (2022-06-23)
### Bug Fixes
* curl in the final image ([0d67c55](https://github.com/standardnotes/api-gateway/commit/0d67c55e124eed08bca16824750152b895fceca7))
## [1.1.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.1...@standardnotes/api-gateway@1.1.2) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.1.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0...@standardnotes/api-gateway@1.1.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.1.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0-alpha.4...@standardnotes/api-gateway@1.1.0) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.1.0-alpha.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0-alpha.3...@standardnotes/api-gateway@1.1.0-alpha.4) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.1.0-alpha.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0-alpha.2...@standardnotes/api-gateway@1.1.0-alpha.3) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.1.0-alpha.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0-alpha.1...@standardnotes/api-gateway@1.1.0-alpha.2) (2022-06-23)
### Bug Fixes
* add missing curl to docker image for healthcheck purposes ([7efb48d](https://github.com/standardnotes/api-gateway/commit/7efb48dd2a6066c29601d34bfcbfe6231f644c50))
# [1.1.0-alpha.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.0-alpha.0...@standardnotes/api-gateway@1.1.0-alpha.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/api-gateway
# 1.1.0-alpha.0 (2022-06-23)
### Features
* add api-gateway package ([57c3b9c](https://github.com/standardnotes/api-gateway/commit/57c3b9c29e5b16449c864e59dbc1fd11689125f9))

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/api-gateway/docker/entrypoint.sh" ]
CMD [ "start-web" ]

View File

@@ -0,0 +1,75 @@
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 { DomainEventPublisherInterface, DailyAnalyticsReportGeneratedEvent } from '@standardnotes/domain-events'
import { AnalyticsActivity, AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
const requestReport = async (
analyticsStore: AnalyticsStoreInterface,
statisticsStore: StatisticsStoreInterface,
domainEventPublisher: DomainEventPublisherInterface,
): Promise<void> => {
const event: DailyAnalyticsReportGeneratedEvent = {
type: 'DAILY_ANALYTICS_REPORT_GENERATED',
createdAt: new Date(),
meta: {
correlation: {
userIdentifier: '',
userIdentifierType: 'uuid',
},
},
payload: {
applicationStatistics: await statisticsStore.getYesterdayApplicationUsage(),
snjsStatistics: await statisticsStore.getYesterdaySNJSUsage(),
outOfSyncIncidents: await statisticsStore.getYesterdayOutOfSyncIncidents(),
activityStatistics: [
{
name: AnalyticsActivity.EditingItems,
retention: await analyticsStore.calculateActivityRetention(
AnalyticsActivity.EditingItems,
Period.DayBeforeYesterday,
Period.Yesterday,
),
totalCount: await analyticsStore.calculateActivityTotalCount(
AnalyticsActivity.EditingItems,
Period.Yesterday,
),
},
],
},
}
await domainEventPublisher.publish(event)
}
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 usage report generation...')
const analyticsStore: AnalyticsStoreInterface = container.get(TYPES.AnalyticsStore)
const statisticsStore: StatisticsStoreInterface = container.get(TYPES.StatisticsStore)
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
Promise.resolve(requestReport(analyticsStore, statisticsStore, domainEventPublisher))
.then(() => {
logger.info('Usage report generation complete')
process.exit(0)
})
.catch((error) => {
logger.error(`Could not finish usage report generation: ${error.message}`)
process.exit(1)
})
})

View File

@@ -0,0 +1,113 @@
import 'reflect-metadata'
import 'newrelic'
import * as Sentry from '@sentry/node'
import '../src/Controller/LegacyController'
import '../src/Controller/HealthCheckController'
import '../src/Controller/v1/SessionsController'
import '../src/Controller/v1/UsersController'
import '../src/Controller/v1/ActionsController'
import '../src/Controller/v1/InvoicesController'
import '../src/Controller/v1/RevisionsController'
import '../src/Controller/v1/ItemsController'
import '../src/Controller/v1/PaymentsController'
import '../src/Controller/v1/WebSocketsController'
import '../src/Controller/v1/TokensController'
import '../src/Controller/v1/OfflineController'
import '../src/Controller/v1/FilesController'
import '../src/Controller/v1/SubscriptionInvitesController'
import '../src/Controller/v2/PaymentsControllerV2'
import '../src/Controller/v2/ActionsControllerV2'
import * as helmet from 'helmet'
import * as cors from 'cors'
import { text, 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-API-Gateway-Version', container.get(TYPES.VERSION))
next()
})
/* eslint-disable */
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["https: 'self'"],
baseUri: ["'self'"],
childSrc: ["*", "blob:"],
connectSrc: ["*"],
fontSrc: ["*", "'self'"],
formAction: ["'self'"],
frameAncestors: ["*", "*.standardnotes.org", "*.standardnotes.com"],
frameSrc: ["*", "blob:"],
imgSrc: ["'self'", "*", "data:"],
manifestSrc: ["'self'"],
mediaSrc: ["'self'"],
objectSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"]
}
}
}))
/* eslint-enable */
app.use(json({ limit: '50mb' }))
app.use(
text({
type: ['text/plain', 'application/x-www-form-urlencoded', 'application/x-www-form-urlencoded; charset=utf-8'],
}),
)
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,29 @@
#!/bin/sh
set -e
COMMAND=$1 && shift 1
case "$COMMAND" in
'start-local' )
echo "Building the project..."
yarn workspace @standardnotes/api-gateway build
echo "Starting Web..."
yarn workspace @standardnotes/api-gateway start
;;
'start-web' )
echo "Starting Web..."
yarn workspace @standardnotes/api-gateway start
;;
'report' )
echo "Starting Usage Report Generation..."
yarn workspace @standardnotes/api-gateway report
;;
* )
echo "Unknown command"
;;
esac
exec "$@"

View File

@@ -0,0 +1,18 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base = require('../../jest.config');
module.exports = {
...base,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
coveragePathIgnorePatterns: [
'/Bootstrap/',
'HealthCheckController'
],
setupFilesAfterEnv: [
'./test-setup.ts'
]
};

View File

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

View File

@@ -0,0 +1,60 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.1.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"description": "API Gateway For Standard Notes Services",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"repository": "git@github.com:standardnotes/api-gateway.git",
"author": "Karol Sójko <karolsojko@standardnotes.com>",
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"prebuild": "yarn clean",
"build": "tsc --rootDir ./",
"lint": "eslint . --ext .ts",
"setup:env": "cp .env.sample .env",
"start": "yarn node dist/bin/server.js",
"report": "yarn node dist/bin/report.js"
},
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "^1.4.0",
"@standardnotes/auth": "3.19.2",
"@standardnotes/domain-events": "2.29.0",
"@standardnotes/domain-events-infra": "1.4.127",
"@standardnotes/time": "^1.7.0",
"aws-sdk": "^2.1160.0",
"axios": "0.24.0",
"cors": "2.8.5",
"dotenv": "8.2.0",
"express": "4.17.1",
"helmet": "4.4.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"jsonwebtoken": "8.5.1",
"newrelic": "^8.14.1",
"prettyjson": "1.2.1",
"reflect-metadata": "0.1.13",
"winston": "3.3.3"
},
"devDependencies": {
"@types/cors": "^2.8.9",
"@types/express": "^4.17.11",
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.3",
"@types/jsonwebtoken": "^8.5.0",
"@types/newrelic": "^7.0.3",
"@types/prettyjson": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"eslint": "^8.14.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.1",
"nodemon": "^2.0.16",
"ts-jest": "^28.0.1"
}
}

View File

@@ -0,0 +1,117 @@
import * as winston from 'winston'
import axios, { AxiosInstance } from 'axios'
import Redis from 'ioredis'
import { Container } from 'inversify'
import * as AWS from 'aws-sdk'
import {
AnalyticsStoreInterface,
PeriodKeyGenerator,
RedisAnalyticsStore,
RedisStatisticsStore,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import { RedisDomainEventPublisher, SNSDomainEventPublisher } from '@standardnotes/domain-events-infra'
import { Timer, TimerInterface } from '@standardnotes/time'
import { Env } from './Env'
import TYPES from './Types'
import { AuthMiddleware } from '../Controller/AuthMiddleware'
import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface'
import { HttpService } from '../Service/Http/HttpService'
import { SubscriptionTokenAuthMiddleware } from '../Controller/SubscriptionTokenAuthMiddleware'
import { StatisticsMiddleware } from '../Controller/StatisticsMiddleware'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTokenCache'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
const env: Env = new Env()
env.load()
const container = new Container()
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonEnricher())
}
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)
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)
if (env.get('SNS_AWS_REGION', true)) {
container.bind<AWS.SNS>(TYPES.SNS).toConstantValue(
new AWS.SNS({
apiVersion: 'latest',
region: env.get('SNS_AWS_REGION', true),
}),
)
}
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
// env vars
container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL'))
container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL'))
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.HTTP_CALL_TIMEOUT)
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
container.bind(TYPES.SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true))
container.bind(TYPES.SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true))
container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL'))
container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
// Middleware
container.bind<AuthMiddleware>(TYPES.AuthMiddleware).to(AuthMiddleware)
container
.bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
.to(SubscriptionTokenAuthMiddleware)
container.bind<StatisticsMiddleware>(TYPES.StatisticsMiddleware).to(StatisticsMiddleware)
// Services
container.bind<HttpServiceInterface>(TYPES.HTTPService).to(HttpService)
const periodKeyGenerator = new PeriodKeyGenerator()
container
.bind<AnalyticsStoreInterface>(TYPES.AnalyticsStore)
.toConstantValue(new RedisAnalyticsStore(periodKeyGenerator, container.get(TYPES.Redis)))
container
.bind<StatisticsStoreInterface>(TYPES.StatisticsStore)
.toConstantValue(new RedisStatisticsStore(periodKeyGenerator, container.get(TYPES.Redis)))
container.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache)
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
if (env.get('SNS_TOPIC_ARN', true)) {
container
.bind<SNSDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
} else {
container
.bind<RedisDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(
new RedisDomainEventPublisher(container.get(TYPES.Redis), 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,31 @@
const TYPES = {
Logger: Symbol.for('Logger'),
Redis: Symbol.for('Redis'),
HTTPClient: Symbol.for('HTTPClient'),
SNS: Symbol.for('SNS'),
// env vars
SYNCING_SERVER_JS_URL: Symbol.for('SYNCING_SERVER_JS_URL'),
AUTH_SERVER_URL: Symbol.for('AUTH_SERVER_URL'),
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
VERSION: Symbol.for('VERSION'),
SNS_TOPIC_ARN: Symbol.for('SNS_TOPIC_ARN'),
SNS_AWS_REGION: Symbol.for('SNS_AWS_REGION'),
REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'),
CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'),
// Middleware
StatisticsMiddleware: Symbol.for('StatisticsMiddleware'),
AuthMiddleware: Symbol.for('AuthMiddleware'),
SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'),
// Services
HTTPService: Symbol.for('HTTPService'),
CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'),
AnalyticsStore: Symbol.for('AnalyticsStore'),
StatisticsStore: Symbol.for('StatisticsStore'),
DomainEventPublisher: Symbol.for('DomainEventPublisher'),
Timer: Symbol.for('Timer'),
}
export default TYPES

View File

@@ -0,0 +1,124 @@
import { CrossServiceTokenData } from '@standardnotes/auth'
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
import { verify } from 'jsonwebtoken'
import { AxiosError, AxiosInstance } from 'axios'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
@injectable()
export class AuthMiddleware extends BaseMiddleware {
constructor(
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) private crossServiceTokenCacheTTL: number,
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
const authHeaderValue = request.headers.authorization as string
if (!authHeaderValue) {
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
return
}
try {
let crossServiceTokenFetchedFromCache = true
let crossServiceToken = null
if (this.crossServiceTokenCacheTTL) {
crossServiceToken = await this.crossServiceTokenCache.get(authHeaderValue)
}
if (crossServiceToken === null) {
const authResponse = await this.httpClient.request({
method: 'POST',
headers: {
Authorization: authHeaderValue,
Accept: 'application/json',
},
validateStatus: (status: number) => {
return status >= 200 && status < 500
},
url: `${this.authServerUrl}/sessions/validate`,
})
if (authResponse.status > 200) {
response.setHeader('content-type', authResponse.headers['content-type'])
response.status(authResponse.status).send(authResponse.data)
return
}
crossServiceToken = authResponse.data.authToken
crossServiceTokenFetchedFromCache = false
}
response.locals.authToken = crossServiceToken
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
await this.crossServiceTokenCache.set({
authorizationHeaderValue: authHeaderValue,
encodedCrossServiceToken: crossServiceToken,
expiresAtInSeconds: this.getCrossServiceTokenCacheExpireTimestamp(decodedToken),
userUuid: decodedToken.user.uuid,
})
}
response.locals.userUuid = decodedToken.user.uuid
response.locals.roles = decodedToken.roles
} catch (error) {
const errorMessage = (error as AxiosError).isAxiosError
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error).message
this.logger.error(
`Could not pass the request to ${this.authServerUrl}/sessions/validate on underlying service: ${errorMessage}`,
)
this.logger.debug('Response error: %O', (error as AxiosError).response ?? error)
if ((error as AxiosError).response?.headers['content-type']) {
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
}
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
response.status(errorCode).send(errorMessage)
return
}
return next()
}
private getCrossServiceTokenCacheExpireTimestamp(token: CrossServiceTokenData): number {
const crossServiceTokenDefaultCacheExpiration = this.timer.getTimestampInSeconds() + this.crossServiceTokenCacheTTL
if (token.session === undefined) {
return crossServiceTokenDefaultCacheExpiration
}
const sessionAccessExpiration = this.timer.convertStringDateToSeconds(token.session.access_expiration)
const sessionRefreshExpiration = this.timer.convertStringDateToSeconds(token.session.refresh_expiration)
return Math.min(crossServiceTokenDefaultCacheExpiration, sessionAccessExpiration, sessionRefreshExpiration)
}
}

View File

@@ -0,0 +1,9 @@
import { controller, httpGet } from 'inversify-express-utils'
@controller('/healthcheck')
export class HealthCheckController {
@httpGet('/')
public async get(): Promise<string> {
return 'OK'
}
}

View File

@@ -0,0 +1,124 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { controller, all, BaseHttpController, httpPost, httpGet, results, httpDelete } from 'inversify-express-utils'
import TYPES from '../Bootstrap/Types'
import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface'
@controller('', TYPES.StatisticsMiddleware)
export class LegacyController extends BaseHttpController {
private AUTH_ROUTES: Map<string, string>
private PARAMETRIZED_AUTH_ROUTES: Map<string, string>
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
this.AUTH_ROUTES = new Map([
['POST:/auth', 'POST:auth'],
['POST:/auth/sign_out', 'POST:auth/sign_out'],
['POST:/auth/change_pw', 'PUT:/users/legacy-endpoint-user/attributes/credentials'],
['GET:/sessions', 'GET:sessions'],
['DELETE:/session', 'DELETE:session'],
['DELETE:/session/all', 'DELETE:session/all'],
['POST:/session/refresh', 'POST:session/refresh'],
['POST:/auth/sign_in', 'POST:auth/sign_in'],
['GET:/auth/params', 'GET:auth/params'],
])
this.PARAMETRIZED_AUTH_ROUTES = new Map([
['PATCH:/users/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})', 'users/{uuid}'],
])
}
@httpPost('/items/sync', TYPES.AuthMiddleware)
async legacyItemsSync(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
@httpGet('/items/:item_id/revisions', TYPES.AuthMiddleware)
async legacyGetRevisions(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
@httpGet('/items/:item_id/revisions/:id', TYPES.AuthMiddleware)
async legacyGetRevision(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
@httpGet('/items/mfa/:userUuid')
async blockedMFARequest(): Promise<results.StatusCodeResult> {
return this.statusCode(401)
}
@httpDelete('/items/mfa/:userUuid')
async blockedMFARemoveRequest(): Promise<results.StatusCodeResult> {
return this.statusCode(401)
}
@all('*')
async legacyProxyToSyncingServer(request: Request, response: Response): Promise<void> {
if (request.path === '/') {
response.send('Welcome to the Standard Notes server infrastructure. Learn more at https://docs.standardnotes.com')
return
}
if (this.shouldBeRedirectedToAuthService(request)) {
const methodAndPath = this.getMethodAndPath(request)
request.method = methodAndPath.method
await this.httpService.callAuthServerWithLegacyFormat(request, response, methodAndPath.path, request.body)
return
}
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
private getMethodAndPath(request: Request): { method: string; path: string } {
const requestKey = `${request.method}:${request.path}`
if (this.AUTH_ROUTES.has(requestKey)) {
const legacyRoute = this.AUTH_ROUTES.get(requestKey) as string
const legacyRouteMethodAndPath = legacyRoute.split(':')
return {
method: legacyRouteMethodAndPath[0],
path: legacyRouteMethodAndPath[1],
}
}
for (const key of this.AUTH_ROUTES.keys()) {
const regExp = new RegExp(key)
const matches = regExp.exec(requestKey)
if (matches !== null) {
const legacyRoute = (this.AUTH_ROUTES.get(key) as string).replace('{uuid}', matches[1])
const legacyRouteMethodAndPath = legacyRoute.split(':')
return {
method: legacyRouteMethodAndPath[0],
path: legacyRouteMethodAndPath[1],
}
}
}
throw Error('could not find path for key')
}
private shouldBeRedirectedToAuthService(request: Request): boolean {
const requestKey = `${request.method}:${request.path}`
if (this.AUTH_ROUTES.has(requestKey)) {
return true
}
for (const key of this.PARAMETRIZED_AUTH_ROUTES.keys()) {
const regExp = new RegExp(key)
const matches = regExp.test(requestKey)
if (matches) {
return true
}
}
return false
}
}

View File

@@ -0,0 +1,31 @@
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
import { Logger } from 'winston'
import { StatisticsStoreInterface } from '@standardnotes/analytics'
import TYPES from '../Bootstrap/Types'
@injectable()
export class StatisticsMiddleware extends BaseMiddleware {
constructor(
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
async handler(request: Request, _response: Response, next: NextFunction): Promise<void> {
try {
const snjsVersion = request.headers['x-snjs-version'] ?? 'unknown'
await this.statisticsStore.incrementSNJSVersionUsage(snjsVersion as string)
const applicationVersion = request.headers['x-application-version'] ?? 'unknown'
await this.statisticsStore.incrementApplicationVersionUsage(applicationVersion as string)
} catch (error) {
this.logger.error(`Could not store analytics data: ${(error as Error).message}`)
}
return next()
}
}

View File

@@ -0,0 +1,120 @@
import { OfflineUserTokenData, CrossServiceTokenData } from '@standardnotes/auth'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
import { verify } from 'jsonwebtoken'
import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
@injectable()
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
constructor(
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
const subscriptionToken = request.query.subscription_token
const email = request.headers['x-offline-email']
if (!subscriptionToken) {
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
return
}
response.locals.tokenAuthenticationMethod = email
? TokenAuthenticationMethod.OfflineSubscriptionToken
: TokenAuthenticationMethod.SubscriptionToken
try {
const url =
response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken
? `${this.authServerUrl}/offline/subscription-tokens/${subscriptionToken}/validate`
: `${this.authServerUrl}/subscription-tokens/${subscriptionToken}/validate`
const authResponse = await this.httpClient.request({
method: 'POST',
headers: {
Accept: 'application/json',
},
data: {
email,
},
validateStatus: (status: number) => {
return status >= 200 && status < 500
},
url,
})
if (authResponse.status > 200) {
response.setHeader('content-type', authResponse.headers['content-type'])
response.status(authResponse.status).send(authResponse.data)
return
}
if (response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken) {
this.handleOfflineAuthTokenValidationResponse(response, authResponse)
return next()
}
this.handleAuthTokenValidationResponse(response, authResponse)
return next()
} catch (error) {
const errorMessage = (error as AxiosError).isAxiosError
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error).message
this.logger.error(
`Could not pass the request to ${this.authServerUrl}/subscription-tokens/${subscriptionToken}/validate on underlying service: ${errorMessage}`,
)
this.logger.debug('Response error: %O', (error as AxiosError).response ?? error)
if ((error as AxiosError).response?.headers['content-type']) {
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
}
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
response.status(errorCode).send(errorMessage)
return
}
}
private handleOfflineAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
response.locals.offlineAuthToken = authResponse.data.authToken
const decodedToken = <OfflineUserTokenData>(
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
)
response.locals.offlineUserEmail = decodedToken.userEmail
response.locals.offlineFeaturesToken = decodedToken.featuresToken
}
private handleAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
response.locals.authToken = authResponse.data.authToken
const decodedToken = <CrossServiceTokenData>(
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
)
response.locals.userUuid = decodedToken.user.uuid
response.locals.roles = decodedToken.roles
}
}

View File

@@ -0,0 +1,4 @@
export enum TokenAuthenticationMethod {
OfflineSubscriptionToken = 'OfflineSubscriptionToken',
SubscriptionToken = 'SubscriptionToken',
}

View File

@@ -0,0 +1,52 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1', TYPES.StatisticsMiddleware)
export class ActionsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/login')
async login(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/sign_in', request.body)
}
@httpGet('/login-params')
async loginParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/params', request.body)
}
@httpPost('/logout')
async logout(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/sign_out', request.body)
}
@httpGet('/auth/methods')
async methods(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/methods', request.body)
}
@httpGet('/failed-backups-emails/mute/:settingUuid')
async muteFailedBackupsEmails(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`internal/settings/email_backup/${request.params.settingUuid}/mute`,
request.body,
)
}
@httpGet('/sign-in-emails/mute/:settingUuid')
async muteSignInEmails(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`internal/settings/sign_in/${request.params.settingUuid}/mute`,
request.body,
)
}
}

View File

@@ -0,0 +1,18 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/files', TYPES.StatisticsMiddleware)
export class FilesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/valet-tokens', TYPES.AuthMiddleware)
async createToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'valet-tokens', request.body)
}
}

View File

@@ -0,0 +1,17 @@
import { Request, Response } from 'express'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1', TYPES.StatisticsMiddleware)
export class InvoicesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/invoices/send-latest', TYPES.SubscriptionTokenAuthMiddleware)
async sendLatestInvoice(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-invoice', request.body)
}
}

View File

@@ -0,0 +1,27 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/items', TYPES.StatisticsMiddleware, TYPES.AuthMiddleware)
export class ItemsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/')
async sync(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, 'items/sync', request.body)
}
@httpPost('/check-integrity')
async checkIntegrity(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, 'items/check-integrity', request.body)
}
@httpGet('/:uuid')
async getItem(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, `items/${request.params.uuid}`, request.body)
}
}

View File

@@ -0,0 +1,33 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/offline', TYPES.StatisticsMiddleware)
export class OfflineController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpGet('/features')
async getOfflineFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'offline/features', request.body)
}
@httpPost('/subscription-tokens')
async createOfflineSubscriptionToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'offline/subscription-tokens', request.body)
}
@httpPost('/payments/stripe-setup-intent')
async createStripeSetupIntent(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
'api/pro_users/stripe-setup-intent/offline',
request.body,
)
}
}

View File

@@ -0,0 +1,162 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { all, BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1', TYPES.StatisticsMiddleware)
export class PaymentsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpGet('/downloads')
async downloads(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/downloads', request.body)
}
@httpGet('/downloads/download-info')
async downloadInfo(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/downloads/download-info', request.body)
}
@httpGet('/downloads/platforms')
async platformDownloads(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/downloads/platforms', request.body)
}
@httpGet('/help/categories')
async categoriesHelp(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/help/categories', request.body)
}
@httpGet('/knowledge/categories')
async categoriesKnowledge(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/knowledge/categories', request.body)
}
@httpGet('/extensions')
async extensions(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/extensions', request.body)
}
@httpPost('/subscriptions/tiered', TYPES.SubscriptionTokenAuthMiddleware)
async createTieredSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/tiered', request.body)
}
@all('/subscriptions(/*)?')
async subscriptions(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, request.path.replace('v1', 'api'), request.body)
}
@httpGet('/reset/validate')
async validateReset(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/reset/validate', request.body)
}
@httpDelete('/reset')
async reset(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/reset', request.body)
}
@httpPost('/reset')
async resetRequest(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/reset', request.body)
}
@httpPost('/user-registration')
async userRegistration(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'admin/events/registration', request.body)
}
@httpPost('/admin/graphql')
async adminGraphql(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'admin/graphql', request.body)
}
@httpPost('/admin/auth/login')
async adminLogin(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'admin/auth/login', request.body)
}
@httpPost('/admin/auth/logout')
async adminLogout(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'admin/auth/logout', request.body)
}
@httpPost('/students')
async students(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/students', request.body)
}
@httpPost('/students/:token/approve')
async studentsApprove(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/students/${request.params.token}/approve`,
request.body,
)
}
@httpPost('/email_subscriptions/:token/less')
async subscriptionsLess(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/email_subscriptions/${request.params.token}/less`,
request.body,
)
}
@httpPost('/email_subscriptions/:token/more')
async subscriptionsMore(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/email_subscriptions/${request.params.token}/more`,
request.body,
)
}
@httpPost('/email_subscriptions/:token/mute/:campaignId')
async subscriptionsMute(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/email_subscriptions/${request.params.token}/mute/${request.params.campaignId}`,
request.body,
)
}
@httpPost('/email_subscriptions/:token/unsubscribe')
async subscriptionsUnsubscribe(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/email_subscriptions/${request.params.token}/unsubscribe`,
request.body,
)
}
@httpPost('/payments/stripe-setup-intent', TYPES.SubscriptionTokenAuthMiddleware)
async createStripeSetupIntent(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/stripe-setup-intent', request.body)
}
@httpGet('/pro_users/cp-prepayment-info', TYPES.SubscriptionTokenAuthMiddleware)
async coinpaymentsPrepaymentInfo(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/cp-prepayment-info', request.body)
}
@all('/pro_users(/*)?')
async proUsers(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, request.path.replace('v1', 'api'), request.body)
}
@all('/refunds')
async refunds(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/refunds', request.body)
}
}

View File

@@ -0,0 +1,35 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/items/:item_id/revisions', TYPES.StatisticsMiddleware, TYPES.AuthMiddleware)
export class RevisionsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpGet('/')
async getRevisions(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, `items/${request.params.item_id}/revisions`)
}
@httpGet('/:id')
async getRevision(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(
request,
response,
`items/${request.params.item_id}/revisions/${request.params.id}`,
)
}
@httpDelete('/:id')
async deleteRevision(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(
request,
response,
`items/${request.params.item_id}/revisions/${request.params.id}`,
)
}
}

View File

@@ -0,0 +1,34 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/sessions', TYPES.StatisticsMiddleware)
export class SessionsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpGet('/', TYPES.AuthMiddleware)
async getSessions(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'sessions')
}
@httpDelete('/:uuid', TYPES.AuthMiddleware)
async deleteSession(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session', {
uuid: request.params.uuid,
})
}
@httpDelete('/', TYPES.AuthMiddleware)
async deleteSessions(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session/all')
}
@httpPost('/refresh')
async refreshSession(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session/refresh', request.body)
}
}

View File

@@ -0,0 +1,42 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/subscription-invites', TYPES.StatisticsMiddleware)
export class SubscriptionInvitesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-invites', request.body)
}
@httpGet('/', TYPES.AuthMiddleware)
async listInvites(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-invites', request.body)
}
@httpDelete('/:inviteUuid', TYPES.AuthMiddleware)
async cancelSubscriptionSharing(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `subscription-invites/${request.params.inviteUuid}`)
}
@httpGet('/:inviteUuid/accept')
async acceptInvite(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `subscription-invites/${request.params.inviteUuid}/accept`)
}
@httpGet('/:inviteUuid/decline')
async declineInvite(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`subscription-invites/${request.params.inviteUuid}/decline`,
)
}
}

View File

@@ -0,0 +1,18 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/subscription-tokens', TYPES.StatisticsMiddleware)
export class TokensController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
async createToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-tokens', request.body)
}
}

View File

@@ -0,0 +1,150 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import {
all,
BaseHttpController,
controller,
httpDelete,
httpGet,
httpPatch,
httpPost,
httpPut,
results,
} from 'inversify-express-utils'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
@controller('/v1/users', TYPES.StatisticsMiddleware)
export class UsersController extends BaseHttpController {
constructor(
@inject(TYPES.HTTPService) private httpService: HttpServiceInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
@httpPost('/claim-account')
async claimAccount(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/claim-account', request.body)
}
@httpPost('/send-activation-code', TYPES.SubscriptionTokenAuthMiddleware)
async sendActivationCode(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
}
@httpPatch('/:userId', TYPES.AuthMiddleware)
async updateUser(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userId}`, request.body)
}
@httpPut('/:userUuid/password', TYPES.AuthMiddleware)
async changePassword(request: Request, response: Response): Promise<void> {
this.logger.debug(
'[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password',
)
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/attributes/credentials`,
request.body,
)
}
@httpPut('/:userUuid/attributes/credentials', TYPES.AuthMiddleware)
async changeCredentials(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/attributes/credentials`,
request.body,
)
}
@httpGet('/:userId/params', TYPES.AuthMiddleware)
async getKeyParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/params')
}
@all('/:userId/mfa', TYPES.AuthMiddleware)
async blockMFA(): Promise<results.StatusCodeResult> {
return this.statusCode(401)
}
@httpPost('/:userUuid/integrations/listed', TYPES.AuthMiddleware)
async createListedAccount(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'listed', request.body)
}
@httpPost('/')
async register(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth', request.body)
}
@httpGet('/:userUuid/settings', TYPES.AuthMiddleware)
async listSettings(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/settings`)
}
@httpPut('/:userUuid/settings', TYPES.AuthMiddleware)
async putSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/settings`, request.body)
}
@httpGet('/:userUuid/settings/:settingName', TYPES.AuthMiddleware)
async getSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/settings/${request.params.settingName}`,
)
}
@httpDelete('/:userUuid/settings/:settingName', TYPES.AuthMiddleware)
async deleteSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/settings/${request.params.settingName}`,
request.body,
)
}
@httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.AuthMiddleware)
async getSubscriptionSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/subscription-settings/${request.params.subscriptionSettingName}`,
)
}
@httpGet('/:userUuid/features', TYPES.AuthMiddleware)
async getFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/features`)
}
@httpGet('/:userUuid/subscription', TYPES.AuthMiddleware)
async getSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/subscription`)
}
@httpGet('/subscription', TYPES.SubscriptionTokenAuthMiddleware)
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
await this.httpService.callAuthServer(request, response, 'offline/users/subscription')
return
}
await this.httpService.callAuthServer(request, response, `users/${response.locals.userUuid}/subscription`)
}
@httpDelete('/:userUuid', TYPES.AuthMiddleware)
async deleteUser(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/account', request.body)
}
}

View File

@@ -0,0 +1,34 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/sockets')
export class WebSocketsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
async createWebSocketConnection(request: Request, response: Response): Promise<void> {
if (!request.headers.connectionid) {
response.status(400).send('Missing connection id in the request')
return
}
await this.httpService.callAuthServer(request, response, `sockets/${request.headers.connectionid}`, request.body)
}
@httpDelete('/')
async deleteWebSocketConnection(request: Request, response: Response): Promise<void> {
if (!request.headers.connectionid) {
response.status(400).send('Missing connection id in the request')
return
}
await this.httpService.callAuthServer(request, response, `sockets/${request.headers.connectionid}`, request.body)
}
}

View File

@@ -0,0 +1,23 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v2', TYPES.StatisticsMiddleware)
export class ActionsControllerV2 extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpPost('/login')
async login(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/pkce_sign_in', request.body)
}
@httpPost('/login-params')
async loginParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/pkce_params', request.body)
}
}

View File

@@ -0,0 +1,62 @@
import { Request, Response } from 'express'
import { BaseHttpController, controller, httpDelete, httpGet, httpPatch, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v2', TYPES.StatisticsMiddleware)
export class PaymentsControllerV2 extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
super()
}
@httpGet('/subscriptions')
async getSubscriptionsWithFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
}
@httpGet('/subscriptions/tailored', TYPES.SubscriptionTokenAuthMiddleware)
async getTailoredSubscriptionsWithFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
}
@httpGet('/subscriptions/deltas', TYPES.SubscriptionTokenAuthMiddleware)
async getSubscriptionDeltasForChangingPlan(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas', request.body)
}
@httpPost('/subscriptions/deltas/apply', TYPES.SubscriptionTokenAuthMiddleware)
async applySubscriptionDelta(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas/apply', request.body)
}
@httpGet('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
async getSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/subscriptions/${request.params.subscriptionId}`,
request.body,
)
}
@httpDelete('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
async cancelSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/subscriptions/${request.params.subscriptionId}`,
request.body,
)
}
@httpPatch('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
async updateSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(
request,
response,
`api/subscriptions/${request.params.subscriptionId}`,
request.body,
)
}
}

View File

@@ -0,0 +1,46 @@
import { inject, injectable } from 'inversify'
import * as IORedis from 'ioredis'
import TYPES from '../../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../../Service/Cache/CrossServiceTokenCacheInterface'
@injectable()
export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterface {
private readonly PREFIX = 'cst'
private readonly USER_CST_PREFIX = 'user-cst'
constructor(@inject(TYPES.Redis) private redisClient: IORedis.Redis) {}
async set(dto: {
authorizationHeaderValue: string
encodedCrossServiceToken: string
expiresAtInSeconds: number
userUuid: string
}): Promise<void> {
const pipeline = this.redisClient.pipeline()
pipeline.sadd(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.authorizationHeaderValue)
pipeline.expireat(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.expiresAtInSeconds)
pipeline.set(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.encodedCrossServiceToken)
pipeline.expireat(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.expiresAtInSeconds)
await pipeline.exec()
}
async get(authorizationHeaderValue: string): Promise<string | null> {
return this.redisClient.get(`${this.PREFIX}:${authorizationHeaderValue}`)
}
async invalidate(userUuid: string): Promise<void> {
const userAuthorizationHeaderValues = await this.redisClient.smembers(`${this.USER_CST_PREFIX}:${userUuid}`)
const pipeline = this.redisClient.pipeline()
for (const authorizationHeaderValue of userAuthorizationHeaderValues) {
pipeline.del(`${this.PREFIX}:${authorizationHeaderValue}`)
}
pipeline.del(`${this.USER_CST_PREFIX}:${userUuid}`)
await pipeline.exec()
}
}

View File

@@ -0,0 +1,10 @@
export interface CrossServiceTokenCacheInterface {
set(dto: {
authorizationHeaderValue: string
encodedCrossServiceToken: string
expiresAtInSeconds: number
userUuid: string
}): Promise<void>
get(authorizationHeaderValue: string): Promise<string | null>
invalidate(userUuid: string): Promise<void>
}

View File

@@ -0,0 +1,231 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { AxiosInstance, AxiosError, AxiosResponse, Method } from 'axios'
import { Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
import { HttpServiceInterface } from './HttpServiceInterface'
@injectable()
export class HttpService implements HttpServiceInterface {
constructor(
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
@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.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
async callSyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callLegacySyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callAuthServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.authServerUrl, request, response, endpoint, payload)
}
async callPaymentsServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.paymentsServerUrl) {
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
return
}
await this.callServerWithLegacyFormat(this.paymentsServerUrl, request, response, endpoint, payload)
}
async callAuthServerWithLegacyFormat(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpoint, payload)
}
private async getServerResponse(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<AxiosResponse | undefined> {
try {
const headers: Record<string, string> = {}
for (const headerName of Object.keys(request.headers)) {
headers[headerName] = request.headers[headerName] as string
}
delete headers.host
delete headers['content-length']
if (response.locals.authToken) {
headers['X-Auth-Token'] = response.locals.authToken
}
if (response.locals.offlineAuthToken) {
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
}
this.logger.debug(`Calling [${request.method}] ${serverUrl}/${endpoint},
headers: ${JSON.stringify(headers)},
query: ${JSON.stringify(request.query)},
payload: ${JSON.stringify(payload)}`)
const serviceResponse = await this.httpClient.request({
method: request.method as Method,
headers,
url: `${serverUrl}/${endpoint}`,
data: this.getRequestData(payload),
maxContentLength: Infinity,
maxBodyLength: Infinity,
params: request.query,
timeout: this.httpCallTimeout,
validateStatus: (status: number) => {
return status >= 200 && status < 500
},
})
if (serviceResponse.headers['x-invalidate-cache']) {
const userUuid = serviceResponse.headers['x-invalidate-cache']
await this.crossServiceTokenCache.invalidate(userUuid)
}
return serviceResponse
} catch (error) {
const errorMessage = (error as AxiosError).isAxiosError
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error).message
this.logger.error(`Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${errorMessage}`)
this.logger.debug('Response error: %O', (error as AxiosError).response ?? error)
if ((error as AxiosError).response?.headers['content-type']) {
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
}
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
response.status(errorCode).send(errorMessage)
}
return
}
private async callServer(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
this.logger.debug(`Response from underlying server: ${JSON.stringify(serviceResponse?.data)},
headers: ${JSON.stringify(serviceResponse?.headers)}`)
if (!serviceResponse) {
return
}
this.applyResponseHeaders(serviceResponse, response)
response.status(serviceResponse.status).send({
meta: {
auth: {
userUuid: response.locals.userUuid,
roles: response.locals.roles,
},
server: {
filesServerUrl: this.filesServerUrl,
},
},
data: serviceResponse.data,
})
}
private async callServerWithLegacyFormat(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
if (!serviceResponse) {
return
}
this.applyResponseHeaders(serviceResponse, response)
if (serviceResponse.request._redirectable._redirectCount > 0) {
response.status(302).redirect(serviceResponse.request.res.responseUrl)
} else {
response.status(serviceResponse.status).send(serviceResponse.data)
}
}
private getRequestData(
payload: Record<string, unknown> | string | undefined,
): Record<string, unknown> | string | undefined {
if (
payload === '' ||
payload === null ||
payload === undefined ||
(typeof payload === 'object' && Object.keys(payload).length === 0)
) {
return undefined
}
return payload
}
private applyResponseHeaders(serviceResponse: AxiosResponse, response: Response): void {
const returnedHeadersFromUnderlyingService = [
'access-control-allow-methods',
'access-control-allow-origin',
'access-control-expose-headers',
'authorization',
'content-type',
'x-ssjs-version',
'x-auth-version',
]
returnedHeadersFromUnderlyingService.map((headerName) => {
const headerValue = serviceResponse.headers[headerName]
if (headerValue) {
response.setHeader(headerName, headerValue)
}
})
}
}

View File

@@ -0,0 +1,34 @@
import { Request, Response } from 'express'
export interface HttpServiceInterface {
callAuthServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callAuthServerWithLegacyFormat(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callSyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callLegacySyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
callPaymentsServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void>
}

View File

View File

@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
},
"include": [
"src/**/*",
"bin/**/*",
],
"references": []
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
set -e
host="$1"
shift
port="$1"
shift
cmd="$@"
while ! nc -vz $host $port; do
>&2 echo "$host:$port is unavailable yet - waiting for it to start"
sleep 10
done
>&2 echo "$host:$port is up - executing command"
exec $cmd

View File

@@ -3,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.5](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.4...@standardnotes/auth-server@1.1.5) (2022-06-27)
### Bug Fixes
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/auth/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
## [1.1.4](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.3...@standardnotes/auth-server@1.1.4) (2022-06-24)
### Bug Fixes
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/auth/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
## [1.1.3](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.2...@standardnotes/auth-server@1.1.3) (2022-06-23)
### Bug Fixes
* curl in the final image ([0d67c55](https://github.com/standardnotes/auth/commit/0d67c55e124eed08bca16824750152b895fceca7))
## [1.1.2](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.1...@standardnotes/auth-server@1.1.2) (2022-06-23)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.1.1](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0...@standardnotes/auth-server@1.1.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.1.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.8...@standardnotes/auth-server@1.1.0) (2022-06-23)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.1.0-alpha.8](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.7...@standardnotes/auth-server@1.1.0-alpha.8) (2022-06-23)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.1.0-alpha.7](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.6...@standardnotes/auth-server@1.1.0-alpha.7) (2022-06-23)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.1.0-alpha.6](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.5...@standardnotes/auth-server@1.1.0-alpha.6) (2022-06-23)
### Bug Fixes
* add missing curl to docker image for healthcheck purposes ([7efb48d](https://github.com/standardnotes/auth/commit/7efb48dd2a6066c29601d34bfcbfe6231f644c50))
# [1.1.0-alpha.5](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.4...@standardnotes/auth-server@1.1.0-alpha.5) (2022-06-22)
### Bug Fixes
* make DISABLE_USER_REGISTRATION env var optional ([3110c20](https://github.com/standardnotes/auth/commit/3110c20596b52da8a43551432cfef94e68046385))
# [1.1.0-alpha.4](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.0-alpha.3...@standardnotes/auth-server@1.1.0-alpha.4) (2022-06-22)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -12,6 +12,8 @@ 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.

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.1.0-alpha.4",
"version": "1.1.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -12,6 +12,7 @@
"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",
@@ -27,9 +28,8 @@
"typeorm": "typeorm-ts-node-commonjs"
},
"dependencies": {
"@newrelic/native-metrics": "7.0.2",
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^6.16.1",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "^1.6.0",
"@standardnotes/api": "^1.1.13",
"@standardnotes/auth": "^3.19.2",
@@ -55,7 +55,7 @@
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"mysql2": "^2.3.3",
"newrelic": "8.6.0",
"newrelic": "^8.14.1",
"otplib": "12.0.1",
"prettyjson": "1.2.1",
"reflect-metadata": "0.1.13",
@@ -70,7 +70,7 @@
"@types/express": "^4.17.11",
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.3",
"@types/newrelic": "^7.0.2",
"@types/newrelic": "^7.0.3",
"@types/otplib": "^10.0.0",
"@types/prettyjson": "^0.0.29",
"@types/ua-parser-js": "^0.7.36",

View File

@@ -357,7 +357,9 @@ export class ContainerConfigLoader {
container.bind(TYPES.PSEUDO_KEY_PARAMS_KEY).toConstantValue(env.get('PSEUDO_KEY_PARAMS_KEY'))
container.bind(TYPES.EPHEMERAL_SESSION_AGE).toConstantValue(env.get('EPHEMERAL_SESSION_AGE'))
container.bind(TYPES.REDIS_URL).toConstantValue(env.get('REDIS_URL'))
container.bind(TYPES.DISABLE_USER_REGISTRATION).toConstantValue(env.get('DISABLE_USER_REGISTRATION') === 'true')
container
.bind(TYPES.DISABLE_USER_REGISTRATION)
.toConstantValue(env.get('DISABLE_USER_REGISTRATION', true) === 'true')
container.bind(TYPES.ANALYTICS_ENABLED).toConstantValue(env.get('ANALYTICS_ENABLED', true) === 'true')
container.bind(TYPES.SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true))
container.bind(TYPES.SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true))

View File

@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.4...@standardnotes/files-server@1.1.5) (2022-06-27)
### Bug Fixes
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/files/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
## [1.1.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.3...@standardnotes/files-server@1.1.4) (2022-06-24)
### Bug Fixes
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/files/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
## [1.1.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.2...@standardnotes/files-server@1.1.3) (2022-06-23)
### Bug Fixes
* curl in the final image ([0d67c55](https://github.com/standardnotes/files/commit/0d67c55e124eed08bca16824750152b895fceca7))
## [1.1.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.1...@standardnotes/files-server@1.1.2) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
## [1.1.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0...@standardnotes/files-server@1.1.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
# [1.1.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.6...@standardnotes/files-server@1.1.0) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
# [1.1.0-alpha.6](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.5...@standardnotes/files-server@1.1.0-alpha.6) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
# [1.1.0-alpha.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.4...@standardnotes/files-server@1.1.0-alpha.5) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
# [1.1.0-alpha.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.3...@standardnotes/files-server@1.1.0-alpha.4) (2022-06-23)
### Bug Fixes
* add missing curl to docker image for healthcheck purposes ([7efb48d](https://github.com/standardnotes/files/commit/7efb48dd2a6066c29601d34bfcbfe6231f644c50))
# [1.1.0-alpha.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.2...@standardnotes/files-server@1.1.0-alpha.3) (2022-06-23)
**Note:** Version bump only for package @standardnotes/files-server
# [1.1.0-alpha.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.1...@standardnotes/files-server@1.1.0-alpha.2) (2022-06-23)
### Features
* add api-gateway package ([57c3b9c](https://github.com/standardnotes/files/commit/57c3b9c29e5b16449c864e59dbc1fd11689125f9))
# [1.1.0-alpha.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.0-alpha.0...@standardnotes/files-server@1.1.0-alpha.1) (2022-06-22)
**Note:** Version bump only for package @standardnotes/files-server
# 1.1.0-alpha.0 (2022-06-22)
### Features

View File

@@ -12,6 +12,8 @@ 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.
@@ -20,6 +22,6 @@ 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/auth/docker/entrypoint.sh" ]
ENTRYPOINT [ "/workspace/packages/files/docker/entrypoint.sh" ]
CMD [ "start-web" ]

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.1.0-alpha.0",
"version": "1.1.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -14,6 +14,7 @@
"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",
@@ -23,8 +24,7 @@
"worker": "yarn node dist/bin/worker.js"
},
"dependencies": {
"@newrelic/native-metrics": "7.0.2",
"@sentry/node": "^6.16.1",
"@sentry/node": "^7.3.0",
"@standardnotes/auth": "^3.18.9",
"@standardnotes/common": "^1.19.4",
"@standardnotes/domain-events": "^2.27.6",
@@ -44,7 +44,7 @@
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"jsonwebtoken": "^8.5.1",
"newrelic": "^7.3.1",
"newrelic": "^8.14.1",
"nodemon": "^2.0.15",
"prettyjson": "^1.2.1",
"reflect-metadata": "^0.1.13",
@@ -59,7 +59,7 @@
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.3",
"@types/jsonwebtoken": "^8.5.0",
"@types/newrelic": "^7.0.1",
"@types/newrelic": "^7.0.3",
"@types/prettyjson": "^0.0.29",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^5.29.0",

View File

@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.4...@standardnotes/scheduler-server@1.1.5) (2022-06-27)
### Bug Fixes
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/server/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
## [1.1.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.3...@standardnotes/scheduler-server@1.1.4) (2022-06-24)
### Bug Fixes
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/server/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
## [1.1.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.2...@standardnotes/scheduler-server@1.1.3) (2022-06-23)
### Bug Fixes
* curl in the final image ([0d67c55](https://github.com/standardnotes/server/commit/0d67c55e124eed08bca16824750152b895fceca7))
## [1.1.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.1...@standardnotes/scheduler-server@1.1.2) (2022-06-23)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.1.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0...@standardnotes/scheduler-server@1.1.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.1.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.16...@standardnotes/scheduler-server@1.1.0) (2022-06-23)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.1.0-alpha.16](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.15...@standardnotes/scheduler-server@1.1.0-alpha.16) (2022-06-23)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.1.0-alpha.15](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.14...@standardnotes/scheduler-server@1.1.0-alpha.15) (2022-06-23)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.1.0-alpha.14](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.13...@standardnotes/scheduler-server@1.1.0-alpha.14) (2022-06-23)
### Bug Fixes
* add missing curl to docker image for healthcheck purposes ([7efb48d](https://github.com/standardnotes/server/commit/7efb48dd2a6066c29601d34bfcbfe6231f644c50))
# [1.1.0-alpha.13](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.12...@standardnotes/scheduler-server@1.1.0-alpha.13) (2022-06-22)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.1.0-alpha.12](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.0-alpha.11...@standardnotes/scheduler-server@1.1.0-alpha.12) (2022-06-22)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -12,6 +12,8 @@ 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.

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.1.0-alpha.12",
"version": "1.1.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,8 +22,8 @@
"typeorm": "typeorm-ts-node-commonjs"
},
"dependencies": {
"@newrelic/native-metrics": "7.0.2",
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^7.3.0",
"@standardnotes/common": "^1.23.0",
"@standardnotes/domain-events": "^2.32.0",
"@standardnotes/domain-events-infra": "^1.5.0",
@@ -35,7 +35,7 @@
"inversify": "5.0.5",
"ioredis": "^5.0.6",
"mysql2": "^2.3.3",
"newrelic": "8.6.0",
"newrelic": "^8.14.1",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.3.6",
"winston": "3.3.3"
@@ -43,7 +43,7 @@
"devDependencies": {
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.2",
"@types/newrelic": "^7.0.2",
"@types/newrelic": "^7.0.3",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"eslint-plugin-prettier": "^4.0.0",

View File

@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.4...@standardnotes/syncing-server@1.1.5) (2022-06-27)
### Bug Fixes
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/syncing-server-js/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
## [1.1.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.3...@standardnotes/syncing-server@1.1.4) (2022-06-24)
### Bug Fixes
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/syncing-server-js/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
## [1.1.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.2...@standardnotes/syncing-server@1.1.3) (2022-06-23)
### Bug Fixes
* curl in the final image ([0d67c55](https://github.com/standardnotes/syncing-server-js/commit/0d67c55e124eed08bca16824750152b895fceca7))
## [1.1.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.1...@standardnotes/syncing-server@1.1.2) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.1.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0...@standardnotes/syncing-server@1.1.1) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.10...@standardnotes/syncing-server@1.1.0) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.10](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.9...@standardnotes/syncing-server@1.1.0-alpha.10) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.9](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.8...@standardnotes/syncing-server@1.1.0-alpha.9) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.8](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.7...@standardnotes/syncing-server@1.1.0-alpha.8) (2022-06-23)
### Bug Fixes
* add missing curl to docker image for healthcheck purposes ([7efb48d](https://github.com/standardnotes/syncing-server-js/commit/7efb48dd2a6066c29601d34bfcbfe6231f644c50))
# [1.1.0-alpha.7](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.6...@standardnotes/syncing-server@1.1.0-alpha.7) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.5...@standardnotes/syncing-server@1.1.0-alpha.6) (2022-06-23)
### Bug Fixes
* upgrade time lib for syncing-server ([6c87d36](https://github.com/standardnotes/syncing-server-js/commit/6c87d3614dfb77f6d1cb02d3d4c1884f2164693f))
# [1.1.0-alpha.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.4...@standardnotes/syncing-server@1.1.0-alpha.5) (2022-06-23)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.3...@standardnotes/syncing-server@1.1.0-alpha.4) (2022-06-23)
### Features
* add api-gateway package ([57c3b9c](https://github.com/standardnotes/syncing-server-js/commit/57c3b9c29e5b16449c864e59dbc1fd11689125f9))
# [1.1.0-alpha.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.2...@standardnotes/syncing-server@1.1.0-alpha.3) (2022-06-22)
**Note:** Version bump only for package @standardnotes/syncing-server
# [1.1.0-alpha.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.0-alpha.1...@standardnotes/syncing-server@1.1.0-alpha.2) (2022-06-22)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -12,6 +12,8 @@ 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.
@@ -20,6 +22,6 @@ 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/auth/docker/entrypoint.sh" ]
ENTRYPOINT [ "/workspace/packages/syncing-server/docker/entrypoint.sh" ]
CMD [ "start-web" ]

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.1.0-alpha.2",
"version": "1.1.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -12,6 +12,7 @@
"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",
@@ -21,9 +22,8 @@
"worker": "yarn node dist/bin/worker.js"
},
"dependencies": {
"@newrelic/native-metrics": "7.0.2",
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^6.16.1",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "^1.6.0",
"@standardnotes/auth": "^3.19.2",
"@standardnotes/common": "^1.22.0",
@@ -32,7 +32,7 @@
"@standardnotes/payloads": "^1.5.1",
"@standardnotes/responses": "^1.6.15",
"@standardnotes/settings": "1.14.3",
"@standardnotes/time": "^1.7.0",
"@standardnotes/time": "^1.7.1",
"aws-sdk": "^2.1159.0",
"axios": "0.24.0",
"cors": "2.8.5",
@@ -44,7 +44,7 @@
"ioredis": "^5.0.6",
"jsonwebtoken": "8.5.1",
"mysql2": "^2.3.3",
"newrelic": "8.6.0",
"newrelic": "^8.14.1",
"nodemon": "2.0.7",
"prettyjson": "1.2.1",
"reflect-metadata": "0.1.13",
@@ -61,7 +61,7 @@
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.3",
"@types/jsonwebtoken": "^8.5.0",
"@types/newrelic": "^7.0.2",
"@types/newrelic": "^7.0.3",
"@types/prettyjson": "^0.0.29",
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.0",

16
scripts/push-tags-one-by-one.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# source: https://github.com/lerna/lerna/issues/2218#issuecomment-771876933
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
# > "Note: An event will not be created when you create more than three tags at once."
# GitHub workflows will not run if a commit includes more than 3 tags at once. This
# fix pushes tags up one by one.
# get all tags on this commit
TAGS=$(git tag --points-at HEAD | cat)
# only push tags one by one if there are more than 3
if (($(echo "$TAGS"| wc -l) > 3))
then
echo "Pushing tags one by one to avoid GitHub webhook limit of 3"
echo "$TAGS" | while read line ; do git push origin --no-follow-tags $line; done
fi

View File

@@ -33,6 +33,9 @@
},
{
"path": "./packages/files"
},
{
"path": "./packages/api-gateway"
}
]
}

388
yarn.lock
View File

@@ -629,7 +629,7 @@ __metadata:
languageName: node
linkType: hard
"@grpc/grpc-js@npm:^1.2.11, @grpc/grpc-js@npm:^1.5.5":
"@grpc/grpc-js@npm:^1.5.5":
version: 1.6.7
resolution: "@grpc/grpc-js@npm:1.6.7"
dependencies:
@@ -639,16 +639,6 @@ __metadata:
languageName: node
linkType: hard
"@grpc/proto-loader@npm:^0.5.6":
version: 0.5.6
resolution: "@grpc/proto-loader@npm:0.5.6"
dependencies:
lodash.camelcase: ^4.3.0
protobufjs: ^6.8.6
checksum: 13fe76d84ab1a516f3dc47d06df4dd682f6f1515a7a4aa3f8cddcc8f8256f33cbf529bd0b6729946f548f7459acfcd9b5b026c10572e21d40213a358115658b5
languageName: node
linkType: hard
"@grpc/proto-loader@npm:^0.6.13, @grpc/proto-loader@npm:^0.6.4":
version: 0.6.13
resolution: "@grpc/proto-loader@npm:0.6.13"
@@ -1218,16 +1208,7 @@ __metadata:
languageName: node
linkType: hard
"@newrelic/aws-sdk@npm:^3.1.0":
version: 3.1.0
resolution: "@newrelic/aws-sdk@npm:3.1.0"
peerDependencies:
newrelic: ">=6.11.0"
checksum: 5601d90c78f82d3216d9cacb664c7a74c1b06acfff44471e758a878a36345ac46449896f9ea4a0b44673b7a72308ee7717d999c184e6e87f3478d5b2d3a14d0c
languageName: node
linkType: hard
"@newrelic/aws-sdk@npm:^4.0.1, @newrelic/aws-sdk@npm:^4.1.1":
"@newrelic/aws-sdk@npm:^4.1.1":
version: 4.1.2
resolution: "@newrelic/aws-sdk@npm:4.1.2"
dependencies:
@@ -1238,18 +1219,7 @@ __metadata:
languageName: node
linkType: hard
"@newrelic/koa@npm:^5.0.0":
version: 5.0.0
resolution: "@newrelic/koa@npm:5.0.0"
dependencies:
methods: ^1.1.2
peerDependencies:
newrelic: ">=6.11.0"
checksum: e98d921b96d043817b623bf83752bdae4e8ca9e594e47ad23109cb516d9a8715ca4b79e4949c5fc5e76ca806d99b2e46e2b7afa2861e0af408f6d647b18f292f
languageName: node
linkType: hard
"@newrelic/koa@npm:^6.0.1, @newrelic/koa@npm:^6.1.1":
"@newrelic/koa@npm:^6.1.1":
version: 6.1.2
resolution: "@newrelic/koa@npm:6.1.2"
peerDependencies:
@@ -1258,40 +1228,6 @@ __metadata:
languageName: node
linkType: hard
"@newrelic/native-metrics@npm:7.0.2":
version: 7.0.2
resolution: "@newrelic/native-metrics@npm:7.0.2"
dependencies:
nan: ^2.14.2
node-gyp: latest
semver: ^5.5.1
checksum: 501805d29fc5be83a34b5f9b78f9e9b9ccc7ba047ad8dc77a3da90d7606e260607536795b0c75c242880bdd8bb40464f870f82fe4b2cd6b1503576a01aca5269
languageName: node
linkType: hard
"@newrelic/native-metrics@npm:^6.0.0":
version: 6.0.2
resolution: "@newrelic/native-metrics@npm:6.0.2"
dependencies:
nan: ^2.14.2
node-gyp: latest
semver: ^5.5.1
checksum: 78f92bbe7feb662699b1a148a869b3d963653a16d3c6ffc63159c4b8bea7905e26140e55631444e167dbd4291e6b7c2261bcd3ce4b8917852fe1d92ffb3936f9
languageName: node
linkType: hard
"@newrelic/native-metrics@npm:^7.0.1":
version: 7.1.2
resolution: "@newrelic/native-metrics@npm:7.1.2"
dependencies:
https-proxy-agent: ^5.0.0
nan: ^2.15.0
node-gyp: latest
semver: ^5.5.1
checksum: c8358b5e6170ed2a32cf14b557f0a2c0d1d836c82a80e1be23e2b2f427a54104fc8a99c59c8d4ebf13fb057eca20ddf5ba3f49657d53d51f1afb846b390fbf91
languageName: node
linkType: hard
"@newrelic/native-metrics@npm:^8.0.0":
version: 8.0.0
resolution: "@newrelic/native-metrics@npm:8.0.0"
@@ -1304,18 +1240,7 @@ __metadata:
languageName: node
linkType: hard
"@newrelic/superagent@npm:^4.0.0":
version: 4.0.0
resolution: "@newrelic/superagent@npm:4.0.0"
dependencies:
methods: ^1.1.2
peerDependencies:
newrelic: ">=6.11.0"
checksum: 5fb257ac0530f91bae58265678500c96c28f164bd6fcec5c0dff51958c8e72bc721dd04d1d2c48bace9e077b57b233c47efb8af61f2a005b71d4636e29ae9728
languageName: node
linkType: hard
"@newrelic/superagent@npm:^5.0.1, @newrelic/superagent@npm:^5.1.0":
"@newrelic/superagent@npm:^5.1.0":
version: 5.1.1
resolution: "@newrelic/superagent@npm:5.1.1"
peerDependencies:
@@ -1729,71 +1654,59 @@ __metadata:
languageName: node
linkType: hard
"@sentry/core@npm:6.19.7":
version: 6.19.7
resolution: "@sentry/core@npm:6.19.7"
"@sentry/core@npm:7.3.0":
version: 7.3.0
resolution: "@sentry/core@npm:7.3.0"
dependencies:
"@sentry/hub": 6.19.7
"@sentry/minimal": 6.19.7
"@sentry/types": 6.19.7
"@sentry/utils": 6.19.7
"@sentry/hub": 7.3.0
"@sentry/types": 7.3.0
"@sentry/utils": 7.3.0
tslib: ^1.9.3
checksum: d212e8ef07114549de4a93b81f8bfa217ca1550ca7a5eeaa611e5629faef78ff72663ce561ffa2cff48f3dc556745ef65177044f9965cdd3cbccf617cf3bf675
checksum: 176856840462c881c9a2ea30c5af209b64516201e5d7978e3960473fead6fc10547c556185e3636a2eade6856b293d190212825f9e8ff6fe4331fcdb0f6c213a
languageName: node
linkType: hard
"@sentry/hub@npm:6.19.7":
version: 6.19.7
resolution: "@sentry/hub@npm:6.19.7"
"@sentry/hub@npm:7.3.0":
version: 7.3.0
resolution: "@sentry/hub@npm:7.3.0"
dependencies:
"@sentry/types": 6.19.7
"@sentry/utils": 6.19.7
"@sentry/types": 7.3.0
"@sentry/utils": 7.3.0
tslib: ^1.9.3
checksum: 10bb1c5cba1b0f1e27a3dd0a186c22f94aeaf11c4662890ab07b2774f46f46af78d61e3ba71d76edc750a7b45af86edd032f35efecdb4efa2eaf551080ccdcb1
checksum: a052a7c940e1f6af16ca2c61f1e184eaf0d7874598ec4eae3a28c8b5ea6a19cf3b89106768aaeea19deb9816bdd316ec0218764c2e66cc92a87e2e404d6e0dd6
languageName: node
linkType: hard
"@sentry/minimal@npm:6.19.7":
version: 6.19.7
resolution: "@sentry/minimal@npm:6.19.7"
"@sentry/node@npm:^7.3.0":
version: 7.3.0
resolution: "@sentry/node@npm:7.3.0"
dependencies:
"@sentry/hub": 6.19.7
"@sentry/types": 6.19.7
tslib: ^1.9.3
checksum: 9153ac426ee056fc34c5be898f83d74ec08f559d69f544c5944ec05e584b62ed356b92d1a9b08993a7022ad42b5661c3d72881221adc19bee5fc1af3ad3864a8
languageName: node
linkType: hard
"@sentry/node@npm:^6.16.1":
version: 6.19.7
resolution: "@sentry/node@npm:6.19.7"
dependencies:
"@sentry/core": 6.19.7
"@sentry/hub": 6.19.7
"@sentry/types": 6.19.7
"@sentry/utils": 6.19.7
"@sentry/core": 7.3.0
"@sentry/hub": 7.3.0
"@sentry/types": 7.3.0
"@sentry/utils": 7.3.0
cookie: ^0.4.1
https-proxy-agent: ^5.0.0
lru_map: ^0.3.3
tslib: ^1.9.3
checksum: 2293b0d1d1f9fac3a451eb94f820bc27721c8edddd1f373064666ddd6272f0a4c70dbe58c6c4b3d3ccaf4578aab8f466d71ee69f6f6ff93521bbb02dfe829ce5
checksum: a92c2d2d1b05136b3c04f39cb87bf459519619c7e4f548f4e2a34145e868edd1ccdd505bcf5d3ff56872ea994530c263deb7ce4c498e9f4e39111c5fcacd342f
languageName: node
linkType: hard
"@sentry/types@npm:6.19.7":
version: 6.19.7
resolution: "@sentry/types@npm:6.19.7"
checksum: f46ef74a33376ad6ea9b128115515c58eb9369d89293c60aa67abca26b5d5d519aa4d0a736db56ae0d75ffd816643d62187018298523cbc2e6c2fb3a6b2a9035
"@sentry/types@npm:7.3.0":
version: 7.3.0
resolution: "@sentry/types@npm:7.3.0"
checksum: 3ddbc3c7ebf2caad2d97a4c7a25f14791f642f7baadfa3c02857e433c0377ca7e6df4d3fc7e197717f2d6f8fca876178d01c827b5c1b42adba92115c62e18daf
languageName: node
linkType: hard
"@sentry/utils@npm:6.19.7":
version: 6.19.7
resolution: "@sentry/utils@npm:6.19.7"
"@sentry/utils@npm:7.3.0":
version: 7.3.0
resolution: "@sentry/utils@npm:7.3.0"
dependencies:
"@sentry/types": 6.19.7
"@sentry/types": 7.3.0
tslib: ^1.9.3
checksum: a000223b9c646c64e3565e79cace1eeb75114342b768367c4dddd646476c215eb1bddfb70c63f05e2352d3bce2d7d415344e4757a001605d0e01ac74da5dd306
checksum: 2696b1bfad1ad2e8e2aa7dd822fb0546d680b06aa302c3a8e8c8da0577fb9938da094f3eda589b2a1c216e1c84a81a5119b30136fd013de9c3a4513aece55d05
languageName: node
linkType: hard
@@ -1836,13 +1749,54 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/analytics@npm:^1.6.0":
"@standardnotes/analytics@npm:^1.4.0, @standardnotes/analytics@npm:^1.6.0":
version: 1.6.0
resolution: "@standardnotes/analytics@npm:1.6.0"
checksum: 6a5e86152673ce9ddce43c52b5f699a1b3ba5141e58a944bda8eaa88fc2f4169df27239f82633226752bf3f10f9804f426721b9c919d10fbfbb51f952430eb1f
languageName: node
linkType: hard
"@standardnotes/api-gateway@workspace:packages/api-gateway":
version: 0.0.0-use.local
resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
dependencies:
"@newrelic/winston-enricher": ^2.1.0
"@sentry/node": ^7.3.0
"@standardnotes/analytics": ^1.4.0
"@standardnotes/auth": 3.19.2
"@standardnotes/domain-events": 2.29.0
"@standardnotes/domain-events-infra": 1.4.127
"@standardnotes/time": ^1.7.0
"@types/cors": ^2.8.9
"@types/express": ^4.17.11
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.3
"@types/jsonwebtoken": ^8.5.0
"@types/newrelic": ^7.0.3
"@types/prettyjson": ^0.0.29
"@typescript-eslint/eslint-plugin": ^5.29.0
aws-sdk: ^2.1160.0
axios: 0.24.0
cors: 2.8.5
dotenv: 8.2.0
eslint: ^8.14.0
eslint-plugin-prettier: ^4.0.0
express: 4.17.1
helmet: 4.4.1
inversify: ^6.0.1
inversify-express-utils: ^6.4.3
ioredis: ^5.0.6
jest: ^28.1.1
jsonwebtoken: 8.5.1
newrelic: ^8.14.1
nodemon: ^2.0.16
prettyjson: 1.2.1
reflect-metadata: 0.1.13
ts-jest: ^28.0.1
winston: 3.3.3
languageName: unknown
linkType: soft
"@standardnotes/api@npm:^1.1.13":
version: 1.1.13
resolution: "@standardnotes/api@npm:1.1.13"
@@ -1861,9 +1815,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/auth-server@workspace:packages/auth"
dependencies:
"@newrelic/native-metrics": 7.0.2
"@newrelic/winston-enricher": ^2.1.0
"@sentry/node": ^6.16.1
"@sentry/node": ^7.3.0
"@standardnotes/analytics": ^1.6.0
"@standardnotes/api": ^1.1.13
"@standardnotes/auth": ^3.19.2
@@ -1882,7 +1835,7 @@ __metadata:
"@types/express": ^4.17.11
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.3
"@types/newrelic": ^7.0.2
"@types/newrelic": ^7.0.3
"@types/otplib": ^10.0.0
"@types/prettyjson": ^0.0.29
"@types/ua-parser-js": ^0.7.36
@@ -1903,7 +1856,7 @@ __metadata:
ioredis: ^5.0.6
jest: ^28.1.1
mysql2: ^2.3.3
newrelic: 8.6.0
newrelic: ^8.14.1
nodemon: ^2.0.16
otplib: 12.0.1
prettyjson: 1.2.1
@@ -1916,6 +1869,16 @@ __metadata:
languageName: unknown
linkType: soft
"@standardnotes/auth@npm:3.19.2":
version: 3.19.2
resolution: "@standardnotes/auth@npm:3.19.2"
dependencies:
"@standardnotes/common": ^1.22.0
jsonwebtoken: ^8.5.1
checksum: 2e4b37b3034ca561e42525313c5ddf66249d27da4c423738241fb3f59f8ea990eafb60b113e648fa6904c171e3c86e93abffb7e72a52bb17b0eb6c9025427678
languageName: node
linkType: hard
"@standardnotes/auth@npm:^3.18.9, @standardnotes/auth@npm:^3.19.2, @standardnotes/auth@npm:^3.19.3":
version: 3.19.3
resolution: "@standardnotes/auth@npm:3.19.3"
@@ -1943,6 +1906,21 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/domain-events-infra@npm:1.4.127":
version: 1.4.127
resolution: "@standardnotes/domain-events-infra@npm:1.4.127"
dependencies:
"@standardnotes/domain-events": ^2.29.0
aws-sdk: ^2.1082.0
ioredis: ^4.28.5
newrelic: ^8.8.0
reflect-metadata: ^0.1.13
sqs-consumer: ^5.6.0
winston: ^3.6.0
checksum: 54e37c296ff3b44adc8d425f89fd56eb42d435dded6b04a952ff77ebb867022585bb472635f488863c9f9b6493a04592593f7a0f2bfa1d86bb9354ba341d350f
languageName: node
linkType: hard
"@standardnotes/domain-events-infra@npm:^1.4.135, @standardnotes/domain-events-infra@npm:^1.4.93, @standardnotes/domain-events-infra@npm:^1.5.0, @standardnotes/domain-events-infra@npm:^1.5.2":
version: 1.5.2
resolution: "@standardnotes/domain-events-infra@npm:1.5.2"
@@ -1958,7 +1936,17 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/domain-events@npm:^2.27.6, @standardnotes/domain-events@npm:^2.31.1, @standardnotes/domain-events@npm:^2.32.0, @standardnotes/domain-events@npm:^2.32.2":
"@standardnotes/domain-events@npm:2.29.0":
version: 2.29.0
resolution: "@standardnotes/domain-events@npm:2.29.0"
dependencies:
"@standardnotes/auth": ^3.19.2
"@standardnotes/features": ^1.44.6
checksum: 1b68999e2a7a6a26a9ecd27638cbb878bd4abb987a1a3b254136af0bec5619d4f5f99ede1b27cf93561fd6d6453f645310b5decfe468c8dd644363caed48aeb9
languageName: node
linkType: hard
"@standardnotes/domain-events@npm:^2.27.6, @standardnotes/domain-events@npm:^2.29.0, @standardnotes/domain-events@npm:^2.31.1, @standardnotes/domain-events@npm:^2.32.0, @standardnotes/domain-events@npm:^2.32.2":
version: 2.32.2
resolution: "@standardnotes/domain-events@npm:2.32.2"
dependencies:
@@ -1980,7 +1968,7 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/features@npm:^1.36.3, @standardnotes/features@npm:^1.45.2, @standardnotes/features@npm:^1.45.5":
"@standardnotes/features@npm:^1.36.3, @standardnotes/features@npm:^1.44.6, @standardnotes/features@npm:^1.45.2, @standardnotes/features@npm:^1.45.5":
version: 1.45.5
resolution: "@standardnotes/features@npm:1.45.5"
dependencies:
@@ -1994,8 +1982,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/files-server@workspace:packages/files"
dependencies:
"@newrelic/native-metrics": 7.0.2
"@sentry/node": ^6.16.1
"@sentry/node": ^7.3.0
"@standardnotes/auth": ^3.18.9
"@standardnotes/common": ^1.19.4
"@standardnotes/config": 2.0.1
@@ -2010,7 +1997,7 @@ __metadata:
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.3
"@types/jsonwebtoken": ^8.5.0
"@types/newrelic": ^7.0.1
"@types/newrelic": ^7.0.3
"@types/prettyjson": ^0.0.29
"@types/uuid": ^8.3.0
"@typescript-eslint/eslint-plugin": ^5.29.0
@@ -2029,7 +2016,7 @@ __metadata:
ioredis: ^5.0.6
jest: ^28.1.1
jsonwebtoken: ^8.5.1
newrelic: ^7.3.1
newrelic: ^8.14.1
nodemon: ^2.0.16
prettyjson: ^1.2.1
reflect-metadata: ^0.1.13
@@ -2077,8 +2064,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/scheduler-server@workspace:packages/scheduler"
dependencies:
"@newrelic/native-metrics": 7.0.2
"@newrelic/winston-enricher": ^2.1.0
"@sentry/node": ^7.3.0
"@standardnotes/common": ^1.23.0
"@standardnotes/domain-events": ^2.32.0
"@standardnotes/domain-events-infra": ^1.5.0
@@ -2086,7 +2073,7 @@ __metadata:
"@standardnotes/time": ^1.7.0
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.2
"@types/newrelic": ^7.0.2
"@types/newrelic": ^7.0.3
"@types/node": ^18.0.0
"@typescript-eslint/eslint-plugin": ^5.29.0
aws-sdk: ^2.1158.0
@@ -2097,7 +2084,7 @@ __metadata:
ioredis: ^5.0.6
jest: ^28.1.1
mysql2: ^2.3.3
newrelic: 8.6.0
newrelic: ^8.14.1
reflect-metadata: ^0.1.13
ts-jest: ^28.0.5
typeorm: ^0.3.6
@@ -2123,10 +2110,14 @@ __metadata:
"@lerna-lite/cli": ^1.5.1
"@lerna-lite/list": ^1.5.1
"@lerna-lite/run": ^1.5.1
"@sentry/node": ^7.3.0
"@types/jest": ^28.1.3
"@types/newrelic": ^7.0.3
"@types/node": ^18.0.0
"@typescript-eslint/parser": ^5.29.0
eslint: ^8.17.0
eslint-config-prettier: ^8.5.0
newrelic: ^8.14.1
prettier: ^2.7.1
ts-node: ^10.8.1
typescript: ^4.7.4
@@ -2173,9 +2164,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/syncing-server@workspace:packages/syncing-server"
dependencies:
"@newrelic/native-metrics": 7.0.2
"@newrelic/winston-enricher": ^2.1.0
"@sentry/node": ^6.16.1
"@sentry/node": ^7.3.0
"@standardnotes/analytics": ^1.6.0
"@standardnotes/auth": ^3.19.2
"@standardnotes/common": ^1.22.0
@@ -2184,7 +2174,7 @@ __metadata:
"@standardnotes/payloads": ^1.5.1
"@standardnotes/responses": ^1.6.15
"@standardnotes/settings": 1.14.3
"@standardnotes/time": ^1.7.0
"@standardnotes/time": ^1.7.1
"@types/cors": ^2.8.9
"@types/dotenv": ^8.2.0
"@types/express": ^4.17.9
@@ -2192,7 +2182,7 @@ __metadata:
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.3
"@types/jsonwebtoken": ^8.5.0
"@types/newrelic": ^7.0.2
"@types/newrelic": ^7.0.3
"@types/prettyjson": ^0.0.29
"@types/ua-parser-js": ^0.7.36
"@types/uuid": ^8.3.0
@@ -2211,7 +2201,7 @@ __metadata:
jest: ^28.1.1
jsonwebtoken: 8.5.1
mysql2: ^2.3.3
newrelic: 8.6.0
newrelic: ^8.14.1
nodemon: 2.0.7
prettyjson: 1.2.1
reflect-metadata: 0.1.13
@@ -2234,6 +2224,17 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/time@npm:^1.7.1":
version: 1.7.1
resolution: "@standardnotes/time@npm:1.7.1"
dependencies:
dayjs: ^1.10.8
microtime: ^3.1.0
reflect-metadata: ^0.1.13
checksum: ccb9c4af73d2c77d5b1cfea480930e3a30e87fb426eee3c60eb0ce0f259fa5c1f9b1b29fdc52c72d321821791e3bdc141e4af0bfc668662863012743332c5407
languageName: node
linkType: hard
"@standardnotes/utils@npm:^1.4.6, @standardnotes/utils@npm:^1.6.11":
version: 1.6.11
resolution: "@standardnotes/utils@npm:1.6.11"
@@ -2547,7 +2548,7 @@ __metadata:
languageName: node
linkType: hard
"@types/newrelic@npm:^7.0.1, @types/newrelic@npm:^7.0.2":
"@types/newrelic@npm:^7.0.3":
version: 7.0.3
resolution: "@types/newrelic@npm:7.0.3"
checksum: 31156f61c5cf6c22e3cd227966499d758e4af278a2b0097194578feee9579339473f7a08b07556530046a2f64ba449656a220a553116b475c4bbd8cca177dfd3
@@ -2953,13 +2954,6 @@ __metadata:
languageName: node
linkType: hard
"agent-base@npm:5":
version: 5.1.1
resolution: "agent-base@npm:5.1.1"
checksum: 61ae789f3019f1dc10e8cba6d3ae9826949299a4e54aaa1cfa2fa37c95a108e70e95423b963bb987d7891a703fd9a5c383a506f4901819f3ee56f3147c0aa8ab
languageName: node
linkType: hard
"agent-base@npm:6, agent-base@npm:^6.0.2":
version: 6.0.2
resolution: "agent-base@npm:6.0.2"
@@ -3158,7 +3152,7 @@ __metadata:
languageName: node
linkType: hard
"async@npm:^3.1.0, async@npm:^3.2.0, async@npm:^3.2.3, async@npm:^3.2.4":
"async@npm:^3.1.0, async@npm:^3.2.3, async@npm:^3.2.4":
version: 3.2.4
resolution: "async@npm:3.2.4"
checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89
@@ -3216,6 +3210,23 @@ __metadata:
languageName: node
linkType: hard
"aws-sdk@npm:^2.1160.0":
version: 2.1160.0
resolution: "aws-sdk@npm:2.1160.0"
dependencies:
buffer: 4.9.2
events: 1.1.1
ieee754: 1.1.13
jmespath: 0.16.0
querystring: 0.2.0
sax: 1.2.1
url: 0.10.3
uuid: 8.0.0
xml2js: 0.4.19
checksum: b95647d4de6d07fc7b62ef409d1dd4a915f3711163a4da4057a3451d62b56818f4ea3d7970c2735af376c44a97d603190f2d85c2a11911eb6f97334ad0ace4a7
languageName: node
linkType: hard
"axios@npm:0.24.0":
version: 0.24.0
resolution: "axios@npm:0.24.0"
@@ -5696,6 +5707,13 @@ __metadata:
languageName: node
linkType: hard
"helmet@npm:4.4.1":
version: 4.4.1
resolution: "helmet@npm:4.4.1"
checksum: cfe385e185e1ef6e4cd2ade4c54e160b05dd0454f270a663c528a8666402cbcad14e0ff0df09567fa62b0b4ac3371bbd1c8a253f6e7af37656a22339fe98c869
languageName: node
linkType: hard
"helmet@npm:^4.3.1":
version: 4.6.0
resolution: "helmet@npm:4.6.0"
@@ -5817,16 +5835,6 @@ __metadata:
languageName: node
linkType: hard
"https-proxy-agent@npm:^4.0.0":
version: 4.0.0
resolution: "https-proxy-agent@npm:4.0.0"
dependencies:
agent-base: 5
debug: 4
checksum: 19471d5aae3e747b1c98b17556647e2a1362e68220c6b19585a8527498f32e62e03c41d2872d059d8720d56846bd7460a80ac06f876bccfa786468ff40dd5eef
languageName: node
linkType: hard
"https-proxy-agent@npm:^5.0.0":
version: 5.0.1
resolution: "https-proxy-agent@npm:5.0.1"
@@ -7471,7 +7479,7 @@ __metadata:
languageName: node
linkType: hard
"methods@npm:^1.1.2, methods@npm:~1.1.2":
"methods@npm:~1.1.2":
version: 1.1.2
resolution: "methods@npm:1.1.2"
checksum: 0917ff4041fa8e2f2fda5425a955fe16ca411591fbd123c0d722fcf02b73971ed6f764d85f0a6f547ce49ee0221ce2c19a5fa692157931cecb422984f1dcd13a
@@ -7488,7 +7496,7 @@ __metadata:
languageName: node
linkType: hard
"microtime@npm:^3.0.0":
"microtime@npm:^3.0.0, microtime@npm:^3.1.0":
version: 3.1.0
resolution: "microtime@npm:3.1.0"
dependencies:
@@ -7776,7 +7784,7 @@ __metadata:
languageName: node
linkType: hard
"nan@npm:^2.14.2, nan@npm:^2.15.0":
"nan@npm:^2.15.0":
version: 2.16.0
resolution: "nan@npm:2.16.0"
dependencies:
@@ -7806,59 +7814,7 @@ __metadata:
languageName: node
linkType: hard
"newrelic@npm:8.6.0":
version: 8.6.0
resolution: "newrelic@npm:8.6.0"
dependencies:
"@grpc/grpc-js": ^1.2.11
"@grpc/proto-loader": ^0.5.6
"@newrelic/aws-sdk": ^4.0.1
"@newrelic/koa": ^6.0.1
"@newrelic/native-metrics": ^7.0.1
"@newrelic/superagent": ^5.0.1
"@tyriar/fibonacci-heap": ^2.0.7
async: ^3.2.0
concat-stream: ^2.0.0
https-proxy-agent: ^5.0.0
json-stringify-safe: ^5.0.0
readable-stream: ^3.6.0
semver: ^5.3.0
dependenciesMeta:
"@newrelic/native-metrics":
optional: true
bin:
newrelic-naming-rules: bin/test-naming-rules.js
checksum: 083b65ce8b01b365e901ca3fe989d55fad10157af9df62f3b8b6d2204bedc5b08abde9c1aec4cb8bc0682b60083b1cdc03febd8d95d5f6ee4199aaca4cad24be
languageName: node
linkType: hard
"newrelic@npm:^7.3.1":
version: 7.5.2
resolution: "newrelic@npm:7.5.2"
dependencies:
"@grpc/grpc-js": ^1.2.11
"@grpc/proto-loader": ^0.5.6
"@newrelic/aws-sdk": ^3.1.0
"@newrelic/koa": ^5.0.0
"@newrelic/native-metrics": ^6.0.0
"@newrelic/superagent": ^4.0.0
"@tyriar/fibonacci-heap": ^2.0.7
async: ^3.2.0
concat-stream: ^2.0.0
https-proxy-agent: ^4.0.0
json-stringify-safe: ^5.0.0
readable-stream: ^3.6.0
semver: ^5.3.0
dependenciesMeta:
"@newrelic/native-metrics":
optional: true
bin:
newrelic-naming-rules: bin/test-naming-rules.js
checksum: f6c67dbb7dfc265eaf46ece7bb5fc0fdab4cdb84010be724f52e5d903474d1c108a35f743a801c0f9ef2a3fa9d1c1c17a1b32bf091f3d4f1fcd323d8130a1e36
languageName: node
linkType: hard
"newrelic@npm:^8.8.0":
"newrelic@npm:^8.14.1, newrelic@npm:^8.8.0":
version: 8.14.1
resolution: "newrelic@npm:8.14.1"
dependencies:
@@ -8813,7 +8769,7 @@ __metadata:
languageName: node
linkType: hard
"protobufjs@npm:^6.11.3, protobufjs@npm:^6.8.6":
"protobufjs@npm:^6.11.3":
version: 6.11.3
resolution: "protobufjs@npm:6.11.3"
dependencies: