mirror of
https://github.com/standardnotes/server
synced 2026-01-21 08:04:27 -05:00
Compare commits
63 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0baaf9ea6 | ||
|
|
b7c6dab3ad | ||
|
|
2daa145867 | ||
|
|
4bd5fb22b4 | ||
|
|
78533a6045 | ||
|
|
e1c533a15e | ||
|
|
b6c2bb8023 | ||
|
|
c45653a50a | ||
|
|
d827513b73 | ||
|
|
ad183ca621 | ||
|
|
1d11c5a186 | ||
|
|
e84e78ec55 | ||
|
|
f91e4316ff | ||
|
|
d54b812881 | ||
|
|
28dc5ba2a4 | ||
|
|
979a320ca6 | ||
|
|
c46186b237 | ||
|
|
27cf093f85 | ||
|
|
ec0fb7e0b9 | ||
|
|
90029456fe | ||
|
|
b167b00075 | ||
|
|
b13fab76f3 | ||
|
|
782a9d310d | ||
|
|
537b1f2a29 | ||
|
|
2fad6b62cb | ||
|
|
bf173b4ede | ||
|
|
c52f038c76 | ||
|
|
b12ba98a5c | ||
|
|
dbccdf342b | ||
|
|
49b6d029c4 | ||
|
|
d6469954ce | ||
|
|
5f40550ad4 | ||
|
|
79ccbdf100 | ||
|
|
1983cfcab2 | ||
|
|
753f86707f | ||
|
|
16d0ed505b | ||
|
|
9de09c55f8 | ||
|
|
c3d7a33aa2 | ||
|
|
a9cc00a478 | ||
|
|
ec035ba648 | ||
|
|
5446f3cae4 | ||
|
|
6a550092c2 | ||
|
|
1b691f6bcd | ||
|
|
98f45cc4c2 | ||
|
|
edc4a20859 | ||
|
|
74e1380df8 | ||
|
|
dfa5187ff7 | ||
|
|
c99c4425cd | ||
|
|
2d8919a079 | ||
|
|
f638287213 | ||
|
|
991d885b63 | ||
|
|
bb17efa817 | ||
|
|
deec29c1b4 | ||
|
|
9d872008a7 | ||
|
|
145b4401af | ||
|
|
17bd50c263 | ||
|
|
4cb79de685 | ||
|
|
28ab0b8e46 | ||
|
|
6911802b87 | ||
|
|
5b98924561 | ||
|
|
f13944badc | ||
|
|
af41e6497d | ||
|
|
b1122a3da5 |
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -95,11 +95,6 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/workspace"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
|
||||
20
.github/workflows/common-e2e.yml
vendored
20
.github/workflows/common-e2e.yml
vendored
@@ -26,23 +26,6 @@ jobs:
|
||||
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
|
||||
ports:
|
||||
- 9001:9001
|
||||
mock-event-publisher:
|
||||
image: standardnotes/mock-event-publisher
|
||||
ports:
|
||||
- 3124:3000
|
||||
env:
|
||||
LOG_LEVEL: debug
|
||||
NODE_ENV: production
|
||||
VERSION: snjs-test
|
||||
SNS_TOPIC_ARN: arn:aws:sns:us-east-1:000000000000:payments-local-topic
|
||||
SNS_ENDPOINT: http://localstack:4566
|
||||
SNS_DISABLE_SSL: true
|
||||
SNS_SECRET_ACCESS_KEY: x
|
||||
SNS_ACCESS_KEY_ID: x
|
||||
SNS_AWS_REGION: us-east-1
|
||||
NEW_RELIC_ENABLED: false
|
||||
options: >-
|
||||
--name "mock-event-publisher"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -59,8 +42,5 @@ jobs:
|
||||
- name: Wait for server to start
|
||||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||
|
||||
- name: Connect external containers to self-hosted network
|
||||
run: docker network connect --alias mock-event-publisher standardnotes_self_hosted mock-event-publisher
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
|
||||
|
||||
4
.github/workflows/e2e-test-suite.yml
vendored
4
.github/workflows/e2e-test-suite.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: E2E Test Suite On Self Hosted Server
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */12 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
snjs_image_tag:
|
||||
@@ -13,5 +15,5 @@ jobs:
|
||||
name: E2E
|
||||
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
|
||||
with:
|
||||
snjs_image_tag: ${{ inputs.snjs_image_tag }}
|
||||
snjs_image_tag: ${{ inputs.snjs_image_tag || 'latest' }}
|
||||
secrets: inherit
|
||||
|
||||
78
.github/workflows/pr.yml
vendored
78
.github/workflows/pr.yml
vendored
@@ -94,81 +94,3 @@ jobs:
|
||||
name: E2E
|
||||
uses: standardnotes/server/.github/workflows/common-e2e.yml@main
|
||||
secrets: inherit
|
||||
|
||||
legacy_e2e:
|
||||
needs: build
|
||||
name: Legacy E2E
|
||||
strategy:
|
||||
matrix:
|
||||
application:
|
||||
- { "service_name": "api-gateway", "workspace_name": "@standardnotes/api-gateway", "e2e_tag_parameter_name": "api_gateway_image_tag", "package_path": "packages/api-gateway" }
|
||||
- { "service_name": "auth", "workspace_name": "@standardnotes/auth-server", "e2e_tag_parameter_name": "auth_image_tag", "package_path": "packages/auth" }
|
||||
- { "service_name": "files", "workspace_name": "@standardnotes/files-server", "e2e_tag_parameter_name": "files_image_tag", "package_path": "packages/files" }
|
||||
- { "service_name": "revisions", "workspace_name": "@standardnotes/revisions-server", "e2e_tag_parameter_name": "revisions_image_tag", "package_path": "packages/revisions"}
|
||||
- { "service_name": "syncing-server-js", "workspace_name": "@standardnotes/syncing-server", "e2e_tag_parameter_name": "syncing_server_js_image_tag", "package_path": "packages/syncing-server" }
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Create Bundle Dir
|
||||
id: bundle-dir
|
||||
run: echo "temp_dir=$(mktemp -d -t ${{ matrix.application.service_name }}-${{ github.sha }}-XXXXXXX)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
${{ needs.legacy_e2e.outputs.temp_dir }}
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Build
|
||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||
run: yarn build
|
||||
|
||||
- name: Bundle
|
||||
run: yarn workspace ${{ matrix.application.workspace_name }} bundle --no-compress --output-directory ${{ steps.bundle-dir.outputs.temp_dir }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@master
|
||||
with:
|
||||
platforms: all
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@master
|
||||
|
||||
- name: Publish Docker image for E2E testing
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: ${{ steps.bundle-dir.outputs.temp_dir }}
|
||||
file: ${{ steps.bundle-dir.outputs.temp_dir }}/${{ matrix.application.package_path }}/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: standardnotes/${{ matrix.application.service_name }}:${{ github.sha }}
|
||||
|
||||
- name: Run E2E test suite
|
||||
uses: convictional/trigger-workflow-and-wait@master
|
||||
with:
|
||||
owner: standardnotes
|
||||
repo: self-hosted
|
||||
github_token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
workflow_file_name: testing-with-updating-client-and-server.yml
|
||||
wait_interval: 30
|
||||
client_payload: '{"${{ matrix.application.e2e_tag_parameter_name }}": "${{ github.sha }}"}'
|
||||
propagate_failure: true
|
||||
trigger_workflow: true
|
||||
wait_workflow: true
|
||||
|
||||
22
.github/workflows/proxy.yml
vendored
22
.github/workflows/proxy.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Proxy Server
|
||||
|
||||
concurrency:
|
||||
group: proxy_server
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*standardnotes/proxy-server*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
call_server_application_workflow:
|
||||
name: Server Application
|
||||
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
|
||||
with:
|
||||
service_name: proxy
|
||||
workspace_name: "@standardnotes/proxy-server"
|
||||
deploy_worker: false
|
||||
package_path: packages/proxy
|
||||
secrets: inherit
|
||||
47
.github/workflows/workspace.yml
vendored
47
.github/workflows/workspace.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: Workspace Server
|
||||
|
||||
concurrency:
|
||||
group: workspace
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*standardnotes/workspace-server*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
call_server_application_workflow:
|
||||
name: Server Application
|
||||
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
|
||||
with:
|
||||
service_name: workspace
|
||||
workspace_name: "@standardnotes/workspace-server"
|
||||
package_path: packages/workspace
|
||||
secrets: inherit
|
||||
|
||||
newrelic:
|
||||
needs: call_server_application_workflow
|
||||
|
||||
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_WORKSPACE_WEB_PROD }}
|
||||
revision: "${{ github.sha }}"
|
||||
description: "Automated Deployment via Github Actions"
|
||||
user: "${{ github.actor }}"
|
||||
|
||||
- name: Create New Relic deployment marker for Worker
|
||||
uses: newrelic/deployment-marker-action@v1
|
||||
with:
|
||||
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
|
||||
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
|
||||
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_WORKSPACE_WORKER_PROD }}
|
||||
revision: "${{ github.sha }}"
|
||||
description: "Automated Deployment via Github Actions"
|
||||
user: "${{ github.actor }}"
|
||||
186
.pnp.cjs
generated
186
.pnp.cjs
generated
@@ -53,10 +53,6 @@ const RAW_RUNTIME_STATE =
|
||||
"name": "@standardnotes/predicates",\
|
||||
"reference": "workspace:packages/predicates"\
|
||||
},\
|
||||
{\
|
||||
"name": "@standardnotes/proxy-server",\
|
||||
"reference": "workspace:packages/proxy"\
|
||||
},\
|
||||
{\
|
||||
"name": "@standardnotes/revisions-server",\
|
||||
"reference": "workspace:packages/revisions"\
|
||||
@@ -88,10 +84,6 @@ const RAW_RUNTIME_STATE =
|
||||
{\
|
||||
"name": "@standardnotes/websockets-server",\
|
||||
"reference": "workspace:packages/websockets"\
|
||||
},\
|
||||
{\
|
||||
"name": "@standardnotes/workspace-server",\
|
||||
"reference": "workspace:packages/workspace"\
|
||||
}\
|
||||
],\
|
||||
"enableTopLevelFallback": true,\
|
||||
@@ -107,7 +99,6 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/event-store", ["workspace:packages/event-store"]],\
|
||||
["@standardnotes/files-server", ["workspace:packages/files"]],\
|
||||
["@standardnotes/predicates", ["workspace:packages/predicates"]],\
|
||||
["@standardnotes/proxy-server", ["workspace:packages/proxy"]],\
|
||||
["@standardnotes/revisions-server", ["workspace:packages/revisions"]],\
|
||||
["@standardnotes/scheduler-server", ["workspace:packages/scheduler"]],\
|
||||
["@standardnotes/security", ["workspace:packages/security"]],\
|
||||
@@ -116,8 +107,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/sncrypto-node", ["workspace:packages/sncrypto-node"]],\
|
||||
["@standardnotes/syncing-server", ["workspace:packages/syncing-server"]],\
|
||||
["@standardnotes/time", ["workspace:packages/time"]],\
|
||||
["@standardnotes/websockets-server", ["workspace:packages/websockets"]],\
|
||||
["@standardnotes/workspace-server", ["workspace:packages/workspace"]]\
|
||||
["@standardnotes/websockets-server", ["workspace:packages/websockets"]]\
|
||||
],\
|
||||
"fallbackPool": [\
|
||||
],\
|
||||
@@ -3970,26 +3960,26 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@simplewebauthn/iso-webcrypto", [\
|
||||
["npm:7.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-iso-webcrypto-npm-7.0.0-352babf4a0-c1644f9b68.zip/node_modules/@simplewebauthn/iso-webcrypto/",\
|
||||
["npm:7.0.1", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-iso-webcrypto-npm-7.0.1-bae5f6738c-ed506490e0.zip/node_modules/@simplewebauthn/iso-webcrypto/",\
|
||||
"packageDependencies": [\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.0.0"]\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.0.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@simplewebauthn/server", [\
|
||||
["npm:7.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-server-npm-7.0.0-e34589f137-836eb9fb97.zip/node_modules/@simplewebauthn/server/",\
|
||||
["npm:7.0.1", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-server-npm-7.0.1-ac81233d49-d11c708008.zip/node_modules/@simplewebauthn/server/",\
|
||||
"packageDependencies": [\
|
||||
["@simplewebauthn/server", "npm:7.0.0"],\
|
||||
["@simplewebauthn/server", "npm:7.0.1"],\
|
||||
["@hexagon/base64", "npm:1.1.25"],\
|
||||
["@peculiar/asn1-android", "npm:2.3.3"],\
|
||||
["@peculiar/asn1-ecc", "npm:2.3.4"],\
|
||||
["@peculiar/asn1-rsa", "npm:2.3.4"],\
|
||||
["@peculiar/asn1-schema", "npm:2.3.3"],\
|
||||
["@peculiar/asn1-x509", "npm:2.3.4"],\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.0.0"],\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.0.1"],\
|
||||
["cbor-x", "npm:1.5.0"],\
|
||||
["cross-fetch", "npm:3.1.5"],\
|
||||
["debug", "virtual:b86a9fb34323a98c6519528ed55faa0d9b44ca8879307c0b29aa384bde47ff59a7d0c9051b31246f14521dfb71ba3c5d6d0b35c29fffc17bf875aa6ad977d9e8#npm:4.3.4"]\
|
||||
@@ -4093,17 +4083,17 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/api", [\
|
||||
["npm:1.24.10", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.24.10-63391538ba-1b9a97fdd8.zip/node_modules/@standardnotes/api/",\
|
||||
["npm:1.25.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.25.3-29ba336725-bc7953c440.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/api", "npm:1.25.3"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/encryption", "npm:1.21.9"],\
|
||||
["@standardnotes/models", "npm:1.42.11"],\
|
||||
["@standardnotes/responses", "npm:1.13.6"],\
|
||||
["@standardnotes/encryption", "npm:1.21.17"],\
|
||||
["@standardnotes/models", "npm:1.43.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.16.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.4"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4166,16 +4156,16 @@ const RAW_RUNTIME_STATE =
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.28.1"],\
|
||||
["@sentry/tracing", "npm:7.28.1"],\
|
||||
["@simplewebauthn/server", "npm:7.0.0"],\
|
||||
["@simplewebauthn/server", "npm:7.0.1"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/api", "npm:1.25.3"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/features", "npm:1.58.4"],\
|
||||
["@standardnotes/features", "npm:1.58.9"],\
|
||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||
["@standardnotes/responses", "npm:1.13.4"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
|
||||
@@ -4312,15 +4302,15 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/encryption", [\
|
||||
["npm:1.21.9", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.21.9-092bc2cb51-dc1336cc05.zip/node_modules/@standardnotes/encryption/",\
|
||||
["npm:1.21.17", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.21.17-0801937c9c-ece7ac644e.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.21.9"],\
|
||||
["@standardnotes/encryption", "npm:1.21.17"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.42.11"],\
|
||||
["@standardnotes/responses", "npm:1.13.6"],\
|
||||
["@standardnotes/models", "npm:1.43.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.4"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4358,10 +4348,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/features", [\
|
||||
["npm:1.58.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.58.4-a84962d125-a39afc145a.zip/node_modules/@standardnotes/features/",\
|
||||
["npm:1.58.8", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.58.8-d97ff2aae1-77bac7d0a0.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.58.4"],\
|
||||
["@standardnotes/features", "npm:1.58.8"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
@@ -4369,10 +4359,10 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.58.6", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.58.6-7b1e198c39-98550416f1.zip/node_modules/@standardnotes/features/",\
|
||||
["npm:1.58.9", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.58.9-c278f712cd-218350ee55.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.58.6"],\
|
||||
["@standardnotes/features", "npm:1.58.9"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
@@ -4438,14 +4428,14 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/models", [\
|
||||
["npm:1.42.11", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.42.11-7db16001ef-6ff3409f70.zip/node_modules/@standardnotes/models/",\
|
||||
["npm:1.43.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.43.5-5180388ed4-fd8e3b60bd.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.42.11"],\
|
||||
["@standardnotes/models", "npm:1.43.5"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.58.6"],\
|
||||
["@standardnotes/responses", "npm:1.13.6"],\
|
||||
["@standardnotes/utils", "npm:1.16.3"],\
|
||||
["@standardnotes/features", "npm:1.58.8"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/utils", "npm:1.16.4"],\
|
||||
["lodash", "npm:4.17.21"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4466,40 +4456,13 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/proxy-server", [\
|
||||
["workspace:packages/proxy", {\
|
||||
"packageLocation": "./packages/proxy/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/proxy-server", "workspace:packages/proxy"],\
|
||||
["@types/newrelic", "npm:9.4.0"],\
|
||||
["@types/node", "npm:18.14.0"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:5.48.2"],\
|
||||
["eslint", "npm:8.32.0"],\
|
||||
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
|
||||
["newrelic", "npm:9.8.0"],\
|
||||
["typescript", "patch:typescript@npm%3A4.8.4#optional!builtin<compat/typescript>::version=4.8.4&hash=701156"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/responses", [\
|
||||
["npm:1.13.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.4-70cbd72561-4803ee14bd.zip/node_modules/@standardnotes/responses/",\
|
||||
["npm:1.13.9", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.9-5b1858da5d-5cb5daf9f3.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.13.4"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.58.4"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.6", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.6-5df25fe3dd-c57e3e1fa1.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.13.6"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.58.6"],\
|
||||
["@standardnotes/features", "npm:1.58.8"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
@@ -4515,11 +4478,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-sqs", "npm:3.259.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.28.1"],\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/api", "npm:1.25.3"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
@@ -4637,6 +4601,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/settings/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.48.2"],\
|
||||
["eslint-plugin-prettier", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:4.2.1"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
@@ -4694,12 +4659,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.28.1"],\
|
||||
["@sentry/tracing", "npm:7.28.1"],\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/api", "npm:1.25.3"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.4"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
@@ -4772,10 +4737,10 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.16.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.16.3-87b47ad954-5c34beaafb.zip/node_modules/@standardnotes/utils/",\
|
||||
["npm:1.16.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.16.4-d7c627b154-ed29da54cb.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.16.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.4"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.3"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
@@ -4792,11 +4757,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-sqs", "npm:3.259.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.28.1"],\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/api", "npm:1.25.3"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.16.2"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
@@ -4826,49 +4792,6 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/workspace-server", [\
|
||||
["workspace:packages/workspace", {\
|
||||
"packageLocation": "./packages/workspace/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/workspace-server", "workspace:packages/workspace"],\
|
||||
["@aws-sdk/client-sns", "npm:3.259.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.259.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.28.1"],\
|
||||
["@standardnotes/api", "npm:1.24.10"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/models", "npm:1.42.11"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
["@types/express", "npm:4.17.14"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.1.1"],\
|
||||
["@types/newrelic", "npm:9.4.0"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:5.48.2"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.0.1"],\
|
||||
["eslint", "npm:8.32.0"],\
|
||||
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
|
||||
["express", "npm:4.18.2"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.2.4"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.2"],\
|
||||
["mysql2", "npm:3.0.1"],\
|
||||
["newrelic", "npm:9.8.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.0.3"],\
|
||||
["typeorm", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:0.3.10"],\
|
||||
["typescript", "patch:typescript@npm%3A4.8.4#optional!builtin<compat/typescript>::version=4.8.4&hash=701156"],\
|
||||
["winston", "npm:3.8.2"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@szmarczak/http-timer", [\
|
||||
["npm:5.0.1", {\
|
||||
"packageLocation": "./.yarn/cache/@szmarczak-http-timer-npm-5.0.1-52261e5986-67236cba79.zip/node_modules/@szmarczak/http-timer/",\
|
||||
@@ -5272,13 +5195,6 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/node", "npm:18.11.9"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:18.14.0", {\
|
||||
"packageLocation": "./.yarn/cache/@types-node-npm-18.14.0-ddc1a221d2-d17dff07c7.zip/node_modules/@types/node/",\
|
||||
"packageDependencies": [\
|
||||
["@types/node", "npm:18.14.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/nodemailer", [\
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-api-npm-1.25.3-29ba336725-bc7953c440.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-api-npm-1.25.3-29ba336725-bc7953c440.zip
vendored
Normal file
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.
BIN
.yarn/cache/@standardnotes-responses-npm-1.13.9-5b1858da5d-5cb5daf9f3.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-responses-npm-1.13.9-5b1858da5d-5cb5daf9f3.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -327,7 +327,7 @@ endif
|
||||
|
||||
quiet_cmd_regen_makefile = ACTION Regenerating $@
|
||||
cmd_regen_makefile = cd $(srcdir); /Users/mo/Desktop/sn/dev/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/mo/Library/Caches/node-gyp/18.13.0" "-Dnode_gyp_dir=/Users/mo/Desktop/sn/dev/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp" "-Dnode_lib_file=/Users/mo/Library/Caches/node-gyp/18.13.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/mo/Desktop/sn/dev/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/mo/Desktop/sn/dev/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics/build/config.gypi -I/Users/mo/Desktop/sn/dev/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi -I/Users/mo/Library/Caches/node-gyp/18.13.0/include/node/common.gypi "--toplevel-dir=." binding.gyp
|
||||
Makefile: $(srcdir)/binding.gyp $(srcdir)/build/config.gypi $(srcdir)/../../../../node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi $(srcdir)/../../../../../../../../../../Library/Caches/node-gyp/18.13.0/include/node/common.gypi
|
||||
Makefile: $(srcdir)/../../../../node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../../../../../../Library/Caches/node-gyp/18.13.0/include/node/common.gypi $(srcdir)/build/config.gypi
|
||||
$(call do_cmd,regen_makefile)
|
||||
|
||||
# "all" is a concatenation of the "all" targets from all the included
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
FROM node:18.13.0-alpine
|
||||
FROM node:18.15.0-alpine
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
services:
|
||||
mock-event-publisher:
|
||||
image: standardnotes/mock-event-publisher
|
||||
ports:
|
||||
- 3124:3000
|
||||
environment:
|
||||
LOG_LEVEL: debug
|
||||
NODE_ENV: production
|
||||
VERSION: snjs-test
|
||||
SNS_TOPIC_ARN: arn:aws:sns:us-east-1:000000000000:payments-local-topic
|
||||
SNS_ENDPOINT: http://localstack:4566
|
||||
SNS_DISABLE_SSL: true
|
||||
SNS_SECRET_ACCESS_KEY: x
|
||||
SNS_ACCESS_KEY_ID: x
|
||||
SNS_AWS_REGION: us-east-1
|
||||
NEW_RELIC_ENABLED: false
|
||||
networks:
|
||||
- standardnotes_self_hosted
|
||||
|
||||
server:
|
||||
build: .
|
||||
env_file: .github/ci.env
|
||||
|
||||
@@ -58,7 +58,9 @@ if [ -z "$REDIS_HOST" ]; then
|
||||
export REDIS_HOST="cache"
|
||||
fi
|
||||
|
||||
export REDIS_URL="redis://$REDIS_HOST"
|
||||
if [ -z "$REDIS_URL" ]; then
|
||||
export REDIS_URL="redis://$REDIS_HOST"
|
||||
fi
|
||||
|
||||
##########
|
||||
# SHARED #
|
||||
@@ -349,8 +351,7 @@ export API_GATEWAY_NEW_RELIC_NO_CONFIG_FILE=true
|
||||
|
||||
export API_GATEWAY_SYNCING_SERVER_JS_URL=http://localhost:$SYNCING_SERVER_PORT
|
||||
export API_GATEWAY_AUTH_SERVER_URL=http://localhost:$AUTH_SERVER_PORT
|
||||
export API_GATEWAY_WORKSPACE_SERVER_URL=http://localhost:3004
|
||||
export API_GATEWAY_REVISIONS_SERVER_URL=http://localhost:3005
|
||||
export API_GATEWAY_REVISIONS_SERVER_URL=http://localhost:$REVISIONS_SERVER_PORT
|
||||
if [ -z "$PUBLIC_FILES_SERVER_URL" ]; then
|
||||
export PUBLIC_FILES_SERVER_URL=http://localhost:3125
|
||||
fi
|
||||
|
||||
@@ -91,13 +91,6 @@ TOPIC_CREATED_RESULT=$(create_topic ${SCHEDULER_TOPIC_NAME})
|
||||
echo "created topic: $TOPIC_CREATED_RESULT"
|
||||
SCHEDULER_TOPIC_ARN=$(get_topic_arn_from_name $SCHEDULER_TOPIC_NAME)
|
||||
|
||||
WORKSPACE_TOPIC_NAME="workspace-local-topic"
|
||||
|
||||
echo "creating topic $WORKSPACE_TOPIC_NAME"
|
||||
TOPIC_CREATED_RESULT=$(create_topic ${WORKSPACE_TOPIC_NAME})
|
||||
echo "created topic: $TOPIC_CREATED_RESULT"
|
||||
WORKSPACE_TOPIC_ARN=$(get_topic_arn_from_name $WORKSPACE_TOPIC_NAME)
|
||||
|
||||
QUEUE_NAME="analytics-local-queue"
|
||||
|
||||
echo "creating queue $QUEUE_NAME"
|
||||
@@ -182,13 +175,6 @@ QUEUE_URL=$(create_queue ${QUEUE_NAME})
|
||||
echo "created queue: $QUEUE_URL"
|
||||
SCHEDULER_QUEUE_ARN=$(get_queue_arn_from_name $QUEUE_NAME)
|
||||
|
||||
QUEUE_NAME="workspace-local-queue"
|
||||
|
||||
echo "creating queue $QUEUE_NAME"
|
||||
QUEUE_URL=$(create_queue ${QUEUE_NAME})
|
||||
echo "created queue: $QUEUE_URL"
|
||||
WORKSPACE_QUEUE_ARN=$(get_queue_arn_from_name $QUEUE_NAME)
|
||||
|
||||
echo "all topics are:"
|
||||
echo "$(get_all_topics)"
|
||||
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.21.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.21.2...@standardnotes/analytics@2.21.3) (2023-03-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.21.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.21.1...@standardnotes/analytics@2.21.2) (2023-03-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.21.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.21.0...@standardnotes/analytics@2.21.1) (2023-02-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** add general activity metric to mixpanel ([9d87200](https://github.com/standardnotes/server/commit/9d872008a7df7ccdd9afe7e7d99ccb0f12680319))
|
||||
|
||||
# [2.21.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.20.3...@standardnotes/analytics@2.21.0) (2023-02-23)
|
||||
|
||||
### Features
|
||||
|
||||
* **analytics:** add listening on session created and refreshed events ([6911802](https://github.com/standardnotes/server/commit/6911802b8743d5d21fe1dcc3006e1d8d699fa94d))
|
||||
|
||||
## [2.20.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.20.2...@standardnotes/analytics@2.20.3) (2023-02-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18.13.0-alpine
|
||||
FROM node:18.15.0-alpine
|
||||
|
||||
RUN apk add --update \
|
||||
curl \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.20.3",
|
||||
"version": "2.21.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -55,6 +55,8 @@ import { StatisticMeasureRepositoryInterface } from '../Domain/Statistics/Statis
|
||||
import { StatisticPersistenceRequestedEventHandler } from '../Domain/Handler/StatisticPersistenceRequestedEventHandler'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { SessionCreatedEventHandler } from '../Domain/Handler/SessionCreatedEventHandler'
|
||||
import { SessionRefreshedEventHandler } from '../Domain/Handler/SessionRefreshedEventHandler'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -186,6 +188,8 @@ export class ContainerConfigLoader {
|
||||
.to(AccountDeletionRequestedEventHandler)
|
||||
container.bind<PaymentFailedEventHandler>(TYPES.PaymentFailedEventHandler).to(PaymentFailedEventHandler)
|
||||
container.bind<PaymentSuccessEventHandler>(TYPES.PaymentSuccessEventHandler).to(PaymentSuccessEventHandler)
|
||||
container.bind<SessionCreatedEventHandler>(TYPES.SessionCreatedEventHandler).to(SessionCreatedEventHandler)
|
||||
container.bind<SessionRefreshedEventHandler>(TYPES.SessionRefreshedEventHandler).to(SessionRefreshedEventHandler)
|
||||
container
|
||||
.bind<SubscriptionCancelledEventHandler>(TYPES.SubscriptionCancelledEventHandler)
|
||||
.to(SubscriptionCancelledEventHandler)
|
||||
@@ -234,6 +238,8 @@ export class ContainerConfigLoader {
|
||||
['SUBSCRIPTION_REACTIVATED', container.get(TYPES.SubscriptionReactivatedEventHandler)],
|
||||
['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)],
|
||||
['STATISTIC_PERSISTENCE_REQUESTED', container.get(TYPES.StatisticPersistenceRequestedEventHandler)],
|
||||
['SESSION_CREATED', container.get(TYPES.SessionCreatedEventHandler)],
|
||||
['SESSION_REFRESHED', container.get(TYPES.SessionRefreshedEventHandler)],
|
||||
])
|
||||
|
||||
container
|
||||
|
||||
@@ -37,6 +37,8 @@ const TYPES = {
|
||||
SubscriptionReactivatedEventHandler: Symbol.for('SubscriptionReactivatedEventHandler'),
|
||||
RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'),
|
||||
StatisticPersistenceRequestedEventHandler: Symbol.for('StatisticPersistenceRequestedEventHandler'),
|
||||
SessionCreatedEventHandler: Symbol.for('SessionCreatedEventHandler'),
|
||||
SessionRefreshedEventHandler: Symbol.for('SessionRefreshedEventHandler'),
|
||||
// Maps
|
||||
RevenueModificationMap: Symbol.for('RevenueModificationMap'),
|
||||
// Services
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SessionCreatedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { inject, injectable, optional } from 'inversify'
|
||||
import { Mixpanel } from 'mixpanel'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
|
||||
@injectable()
|
||||
export class SessionCreatedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.MixpanelClient) @optional() private mixpanelClient: Mixpanel | null,
|
||||
) {}
|
||||
|
||||
async handle(event: SessionCreatedEvent): Promise<void> {
|
||||
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: event.payload.userUuid })
|
||||
|
||||
if (this.mixpanelClient !== null) {
|
||||
this.mixpanelClient.track(event.type, {
|
||||
distinct_id: analyticsId.toString(),
|
||||
})
|
||||
|
||||
this.mixpanelClient.track('GENERAL_ACTIVITY', {
|
||||
distinct_id: analyticsId.toString(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SessionRefreshedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { inject, injectable, optional } from 'inversify'
|
||||
import { Mixpanel } from 'mixpanel'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
|
||||
@injectable()
|
||||
export class SessionRefreshedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.MixpanelClient) @optional() private mixpanelClient: Mixpanel | null,
|
||||
) {}
|
||||
|
||||
async handle(event: SessionRefreshedEvent): Promise<void> {
|
||||
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: event.payload.userUuid })
|
||||
|
||||
if (this.mixpanelClient !== null) {
|
||||
this.mixpanelClient.track(event.type, {
|
||||
distinct_id: analyticsId.toString(),
|
||||
})
|
||||
|
||||
this.mixpanelClient.track('GENERAL_ACTIVITY', {
|
||||
distinct_id: analyticsId.toString(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ PORT=3000
|
||||
|
||||
SYNCING_SERVER_JS_URL=http://syncing_server_js:3000
|
||||
AUTH_SERVER_URL=http://auth:3000
|
||||
WORKSPACE_SERVER_URL=http://workspace:3000
|
||||
WEB_SOCKET_SERVER_URL=http://websockets:3000
|
||||
PAYMENTS_SERVER_URL=http://payments:3000
|
||||
FILES_SERVER_URL=http://files:3000
|
||||
|
||||
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.49.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.5...@standardnotes/api-gateway@1.49.6) (2023-03-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.49.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.4...@standardnotes/api-gateway@1.49.5) (2023-03-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.49.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.3...@standardnotes/api-gateway@1.49.4) (2023-02-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.49.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.2...@standardnotes/api-gateway@1.49.3) (2023-02-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateywa:** remove stale proxy references ([dfa5187](https://github.com/standardnotes/api-gateway/commit/dfa5187ff73833bf981d273da79f78ae0309a493))
|
||||
|
||||
## [1.49.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.1...@standardnotes/api-gateway@1.49.2) (2023-02-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.49.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.49.0...@standardnotes/api-gateway@1.49.1) (2023-02-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.49.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.48.3...@standardnotes/api-gateway@1.49.0) (2023-02-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18.13.0-alpine
|
||||
FROM node:18.15.0-alpine
|
||||
|
||||
RUN apk add --update \
|
||||
curl \
|
||||
|
||||
@@ -19,10 +19,7 @@ import '../src/Controller/v1/TokensController'
|
||||
import '../src/Controller/v1/OfflineController'
|
||||
import '../src/Controller/v1/FilesController'
|
||||
import '../src/Controller/v1/SubscriptionInvitesController'
|
||||
import '../src/Controller/v1/WorkspacesController'
|
||||
import '../src/Controller/v1/InvitesController'
|
||||
import '../src/Controller/v1/AuthenticatorsController'
|
||||
import '../src/Controller/v1/ProxyController'
|
||||
|
||||
import '../src/Controller/v2/PaymentsControllerV2'
|
||||
import '../src/Controller/v2/ActionsControllerV2'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.49.0",
|
||||
"version": "1.49.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -59,9 +59,7 @@ export class ContainerConfigLoader {
|
||||
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
||||
container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
|
||||
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
container.bind(TYPES.WORKSPACE_SERVER_URL).toConstantValue(env.get('WORKSPACE_SERVER_URL', true))
|
||||
container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
|
||||
container.bind(TYPES.PROXY_SERVER_URL).toConstantValue(env.get('PROXY_SERVER_URL', true))
|
||||
container
|
||||
.bind(TYPES.HTTP_CALL_TIMEOUT)
|
||||
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
|
||||
|
||||
@@ -9,9 +9,7 @@ const TYPES = {
|
||||
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
|
||||
REVISIONS_SERVER_URL: Symbol.for('REVISIONS_SERVER_URL'),
|
||||
EMAIL_SERVER_URL: Symbol.for('EMAIL_SERVER_URL'),
|
||||
WORKSPACE_SERVER_URL: Symbol.for('WORKSPACE_SERVER_URL'),
|
||||
WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
|
||||
PROXY_SERVER_URL: Symbol.for('PROXY_SERVER_URL'),
|
||||
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
||||
HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
|
||||
VERSION: Symbol.for('VERSION'),
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { inject } from 'inversify'
|
||||
import { Request, Response } from 'express'
|
||||
import { controller, BaseHttpController, httpPost } from 'inversify-express-utils'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/invites', TYPES.AuthMiddleware)
|
||||
export class InvitesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/:inviteUuid/accept')
|
||||
async accept(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`invites/${request.params.inviteUuid}/accept`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -145,11 +145,6 @@ export class PaymentsController extends BaseHttpController {
|
||||
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)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { all, BaseHttpController, controller } from 'inversify-express-utils'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/proxy')
|
||||
export class ProxyController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@all('*', TYPES.AuthMiddleware)
|
||||
async createToken(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callProxyServer(request, response, request.path.replace('/v1/proxy', ''), request.body)
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { inject } from 'inversify'
|
||||
import { Request, Response } from 'express'
|
||||
import { controller, BaseHttpController, httpPost, httpGet } from 'inversify-express-utils'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/workspaces', TYPES.AuthMiddleware)
|
||||
export class WorkspacesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/')
|
||||
async create(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
|
||||
}
|
||||
|
||||
@httpGet('/:workspaceUuid/users')
|
||||
async listWorkspaceUsers(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/users`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/users/:userUuid/keyshare')
|
||||
async initiateKeyshare(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/users/${request.params.userUuid}/keyshare`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
async listWorkspaces(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/invites')
|
||||
async invite(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/invites`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,9 @@ export class HttpService implements HttpServiceInterface {
|
||||
@inject(TYPES.SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
|
||||
@inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
|
||||
@inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string,
|
||||
@inject(TYPES.WORKSPACE_SERVER_URL) private workspaceServerUrl: string,
|
||||
@inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
||||
@inject(TYPES.REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
||||
@inject(TYPES.EMAIL_SERVER_URL) private emailServerUrl: string,
|
||||
@inject(TYPES.PROXY_SERVER_URL) private proxyServerUrl: string,
|
||||
@inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
||||
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@@ -82,21 +80,6 @@ export class HttpService implements HttpServiceInterface {
|
||||
await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callWorkspaceServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
if (!this.workspaceServerUrl) {
|
||||
response.status(400).send({ message: 'Workspace Server not configured' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await this.callServer(this.workspaceServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callWebSocketServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
@@ -112,21 +95,6 @@ export class HttpService implements HttpServiceInterface {
|
||||
await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callProxyServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
if (!this.proxyServerUrl) {
|
||||
this.logger.debug('Proxy Server URL not defined. Skipped request to Proxy.')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await this.callServer(this.proxyServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callPaymentsServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
|
||||
@@ -43,22 +43,10 @@ export interface HttpServiceInterface {
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
callWorkspaceServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
callWebSocketServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
callProxyServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
}
|
||||
|
||||
@@ -3,6 +3,135 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.93.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.5...@standardnotes/auth-server@1.93.6) (2023-03-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.93.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.4...@standardnotes/auth-server@1.93.5) (2023-03-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** remove migrate email settings procedure ([4bd5fb2](https://github.com/standardnotes/server/commit/4bd5fb22b447b0e0fdb136aa46ddc812c8b272cd))
|
||||
|
||||
## [1.93.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.3...@standardnotes/auth-server@1.93.4) (2023-03-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** change response from verifying authenticator registration ([e1c533a](https://github.com/standardnotes/server/commit/e1c533a15e33e215e90fbe15d2d4994605eaa1bd))
|
||||
|
||||
## [1.93.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.2...@standardnotes/auth-server@1.93.3) (2023-03-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** migrate encrypted sign in settings ([d827513](https://github.com/standardnotes/server/commit/d827513b73a57fbdb72c3112f32dc2a296103450))
|
||||
* **auth:** remove authenticator names from server ([c45653a](https://github.com/standardnotes/server/commit/c45653a50a9d25de1e0fc86127ff6931dc98406d))
|
||||
|
||||
## [1.93.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.1...@standardnotes/auth-server@1.93.2) (2023-03-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** authentication options ([1d11c5a](https://github.com/standardnotes/server/commit/1d11c5a1865f81ca57d0ad4313cc3df497b4c445))
|
||||
|
||||
## [1.93.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.93.0...@standardnotes/auth-server@1.93.1) (2023-03-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** migrate muted email notifications settings ([f91e431](https://github.com/standardnotes/server/commit/f91e4316ff4993d032c016bb233b93a9f3356cf3))
|
||||
|
||||
# [1.93.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.92.0...@standardnotes/auth-server@1.93.0) (2023-03-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** setting name value objects in typeorm queries ([28dc5ba](https://github.com/standardnotes/server/commit/28dc5ba2a4e946b7aed86432da160c0be76f839d))
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add internal team user role ([#473](https://github.com/standardnotes/server/issues/473)) ([979a320](https://github.com/standardnotes/server/commit/979a320ca666991ad2b023436f58c59ae168c768))
|
||||
|
||||
# [1.92.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.91.2...@standardnotes/auth-server@1.92.0) (2023-03-08)
|
||||
|
||||
### Features
|
||||
|
||||
* sign in setting refactor ([#472](https://github.com/standardnotes/server/issues/472)) ([27cf093](https://github.com/standardnotes/server/commit/27cf093f85d0f2e208f48e7c7ddcce36b341ffb7))
|
||||
|
||||
## [1.91.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.91.1...@standardnotes/auth-server@1.91.2) (2023-03-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** associate setting with sign in alerts permission ([9002945](https://github.com/standardnotes/server/commit/90029456fe6d654747d6b8b7ae106d3d58b3a3fe))
|
||||
* **auth:** remove sign in emails permission from free accounts ([b167b00](https://github.com/standardnotes/server/commit/b167b0007555b3850ae274354b6c271fe0a1e47f))
|
||||
|
||||
## [1.91.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.91.0...@standardnotes/auth-server@1.91.1) (2023-03-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** disable sign in emails on newly created accounts ([782a9d3](https://github.com/standardnotes/server/commit/782a9d310dc2d2819a49540138ed10b36ebd0d94))
|
||||
|
||||
# [1.91.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.90.1...@standardnotes/auth-server@1.91.0) (2023-03-06)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add cleanup of expired sessions ([2fad6b6](https://github.com/standardnotes/server/commit/2fad6b62cbb5bec38a3171a996d3f9c4eedf7836))
|
||||
|
||||
## [1.90.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.90.0...@standardnotes/auth-server@1.90.1) (2023-03-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** prevent listing sessions on readonly access ([dbccdf3](https://github.com/standardnotes/server/commit/dbccdf342b52f81fb14f246784d5dc6def2ff3fc))
|
||||
|
||||
# [1.90.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.7...@standardnotes/auth-server@1.90.0) (2023-03-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add configurable list of readonly users ([#462](https://github.com/standardnotes/server/issues/462)) ([d646995](https://github.com/standardnotes/server/commit/d6469954ceb24580c465535e61588b04924734ab))
|
||||
|
||||
## [1.89.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.6...@standardnotes/auth-server@1.89.7) (2023-03-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** function naming for more clarity ([79ccbdf](https://github.com/standardnotes/server/commit/79ccbdf1000c699074b5271f3c04a30fcb1b3311))
|
||||
|
||||
## [1.89.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.5...@standardnotes/auth-server@1.89.6) (2023-03-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** changing the updated_at property on sessions ([753f867](https://github.com/standardnotes/server/commit/753f86707ffdbab0d04f49b42275dbb28589780b))
|
||||
|
||||
## [1.89.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.4...@standardnotes/auth-server@1.89.5) (2023-03-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.89.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.3...@standardnotes/auth-server@1.89.4) (2023-03-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** updating counter post authenticator verification ([a9cc00a](https://github.com/standardnotes/server/commit/a9cc00a4783c12e71eb181a3ccf3218b418750d9))
|
||||
|
||||
## [1.89.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.2...@standardnotes/auth-server@1.89.3) (2023-02-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.89.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.1...@standardnotes/auth-server@1.89.2) (2023-02-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add cross-platform authenticator selection option ([edc4a20](https://github.com/standardnotes/server/commit/edc4a2085952efe0b83c8e837a52555087714ef7))
|
||||
|
||||
## [1.89.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.89.0...@standardnotes/auth-server@1.89.1) (2023-02-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.89.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.88.3...@standardnotes/auth-server@1.89.0) (2023-02-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add safety buffer for session access token age ttl ([17bd50c](https://github.com/standardnotes/server/commit/17bd50c263520c4936bf674070e46f0ea0b5cfb1))
|
||||
* **auth:** mark sessions that are longer than configured as expired ([f13944b](https://github.com/standardnotes/server/commit/f13944badc0e04f153626d0d6ace6f8f6d57e533))
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add publishing session created and session refreshed events ([5b98924](https://github.com/standardnotes/server/commit/5b9892456158819831f1f2dcf349ac861d699a94))
|
||||
|
||||
## [1.88.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.88.2...@standardnotes/auth-server@1.88.3) (2023-02-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18.13.0-alpine
|
||||
FROM node:18.15.0-alpine
|
||||
|
||||
RUN apk add --update \
|
||||
curl \
|
||||
|
||||
@@ -32,36 +32,36 @@ const requestBackups = async (
|
||||
): Promise<void> => {
|
||||
let settingName: SettingName,
|
||||
permissionName: PermissionName,
|
||||
muteEmailsSettingName: SettingName,
|
||||
muteEmailsSettingName: string,
|
||||
muteEmailsSettingValue: string,
|
||||
providerTokenSettingName: SettingName
|
||||
switch (backupProvider) {
|
||||
case 'email':
|
||||
settingName = SettingName.EmailBackupFrequency
|
||||
settingName = SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue()
|
||||
permissionName = PermissionName.DailyEmailBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
break
|
||||
case 'dropbox':
|
||||
settingName = SettingName.DropboxBackupFrequency
|
||||
settingName = SettingName.create(SettingName.NAMES.DropboxBackupFrequency).getValue()
|
||||
permissionName = PermissionName.DailyDropboxBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.DropboxBackupToken
|
||||
providerTokenSettingName = SettingName.create(SettingName.NAMES.DropboxBackupToken).getValue()
|
||||
break
|
||||
case 'one_drive':
|
||||
settingName = SettingName.OneDriveBackupFrequency
|
||||
settingName = SettingName.create(SettingName.NAMES.OneDriveBackupFrequency).getValue()
|
||||
permissionName = PermissionName.DailyOneDriveBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.OneDriveBackupToken
|
||||
providerTokenSettingName = SettingName.create(SettingName.NAMES.OneDriveBackupToken).getValue()
|
||||
break
|
||||
case 'google_drive':
|
||||
settingName = SettingName.GoogleDriveBackupFrequency
|
||||
settingName = SettingName.create(SettingName.NAMES.GoogleDriveBackupFrequency).getValue()
|
||||
permissionName = PermissionName.DailyGDriveBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.GoogleDriveBackupToken
|
||||
providerTokenSettingName = SettingName.create(SettingName.NAMES.GoogleDriveBackupToken).getValue()
|
||||
break
|
||||
default:
|
||||
throw new Error(`Not handled backup provider: ${backupProvider}`)
|
||||
|
||||
@@ -8,6 +8,17 @@ import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { CleanupSessionTraces } from '../src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces'
|
||||
import { CleanupExpiredSessions } from '../src/Domain/UseCase/CleanupExpiredSessions/CleanupExpiredSessions'
|
||||
|
||||
const cleanup = async (
|
||||
cleanupSessionTraces: CleanupSessionTraces,
|
||||
cleanupExpiredSessions: CleanupExpiredSessions,
|
||||
): Promise<void> => {
|
||||
const date = new Date()
|
||||
|
||||
await cleanupSessionTraces.execute({ date })
|
||||
await cleanupExpiredSessions.execute({ date })
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
@@ -16,22 +27,19 @@ void container.load().then((container) => {
|
||||
|
||||
const logger: Logger = container.get(TYPES.Logger)
|
||||
|
||||
logger.info('Starting session traces cleanup')
|
||||
logger.info('Starting sessions and session traces cleanup')
|
||||
|
||||
const cleanupSessionTraces: CleanupSessionTraces = container.get(TYPES.CleanupSessionTraces)
|
||||
const cleanupExpiredSessions: CleanupExpiredSessions = container.get(TYPES.CleanupExpiredSessions)
|
||||
|
||||
Promise.resolve(
|
||||
cleanupSessionTraces.execute({
|
||||
date: new Date(),
|
||||
}),
|
||||
)
|
||||
Promise.resolve(cleanup(cleanupSessionTraces, cleanupExpiredSessions))
|
||||
.then(() => {
|
||||
logger.info('Expired session traces cleaned.')
|
||||
logger.info('Expired sessions and session traces cleaned.')
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not clean session traces: ${error.message}`)
|
||||
logger.error(`Could not clean sessions and session traces: ${error.message}`)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -28,7 +28,7 @@ const requestBackups = async (
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
const permissionName = PermissionName.DailyEmailBackup
|
||||
const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
|
||||
if (!backupEmail) {
|
||||
|
||||
@@ -34,7 +34,7 @@ export class moveMfaItemsToUserSettings1627638504691 implements MigrationInterfa
|
||||
|
||||
const setting = new Setting()
|
||||
setting.uuid = item['uuid']
|
||||
setting.name = SettingName.MfaSecret
|
||||
setting.name = SettingName.NAMES.MfaSecret
|
||||
setting.value = item['content']
|
||||
if (item['deleted']) {
|
||||
setting.value = null
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class removeSignInEmailsOnFreeAcounts1678110075698 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'DELETE FROM `role_permissions` WHERE role_uuid="23bf88ca-bee1-4a4c-adf0-b7a48749eea7" AND permission_uuid="2074d312-78bc-4533-b008-38e1232226c0"',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'DELETE FROM `role_permissions` WHERE role_uuid="bde42e26-628c-44e6-9d76-21b08954b0bf" AND permission_uuid="2074d312-78bc-4533-b008-38e1232226c0"',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class addInternalTeamUserRole1678266947362 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// remove beta files user role and permission
|
||||
await queryRunner.query('DELETE FROM `role_permissions` WHERE role_uuid="1cd9ee6e-bc95-4f32-957c-d8c41f94d4ef"')
|
||||
await queryRunner.query('DELETE FROM `user_roles` WHERE role_uuid="1cd9ee6e-bc95-4f32-957c-d8c41f94d4ef"')
|
||||
await queryRunner.query('DELETE FROM `roles` WHERE name="FILES_BETA_USER"')
|
||||
await queryRunner.query('DELETE FROM `permissions` WHERE name="app:files-beta"')
|
||||
|
||||
// add internal team user role and permission
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `roles` (uuid, name, version) VALUES ("9f8d2313-e8d0-48ad-b19c-026601d0ddf4", "INTERNAL_TEAM_USER", 1)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `permissions` (uuid, name) VALUES ("fb13e7d3-936f-4ded-a543-e1650cc99dfd", "server:universal-second-factor")',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (role_uuid, permission_uuid) VALUES ("9f8d2313-e8d0-48ad-b19c-026601d0ddf4", "fb13e7d3-936f-4ded-a543-e1650cc99dfd")',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class removeAuthenticatorNamesFromServer1678340701766 implements MigrationInterface {
|
||||
name = 'removeAuthenticatorNamesFromServer1678340701766'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `name`')
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.88.3",
|
||||
"version": "1.93.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
@@ -39,15 +39,15 @@
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.28.1",
|
||||
"@sentry/tracing": "^7.28.1",
|
||||
"@simplewebauthn/server": "^7.0.0",
|
||||
"@standardnotes/api": "^1.24.10",
|
||||
"@simplewebauthn/server": "^7.0.1",
|
||||
"@standardnotes/api": "^1.25.3",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
"@standardnotes/features": "^1.58.4",
|
||||
"@standardnotes/features": "^1.58.9",
|
||||
"@standardnotes/predicates": "workspace:*",
|
||||
"@standardnotes/responses": "^1.13.4",
|
||||
"@standardnotes/responses": "^1.13.9",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
|
||||
@@ -168,7 +168,6 @@ import { ListSharedSubscriptionInvitations } from '../Domain/UseCase/ListSharedS
|
||||
import { UserSubscriptionServiceInterface } from '../Domain/Subscription/UserSubscriptionServiceInterface'
|
||||
import { UserSubscriptionService } from '../Domain/Subscription/UserSubscriptionService'
|
||||
import { SubscriptionSettingProjector } from '../Projection/SubscriptionSettingProjector'
|
||||
import { GetSubscriptionSetting } from '../Domain/UseCase/GetSubscriptionSetting/GetSubscriptionSetting'
|
||||
import { SubscriptionSettingsAssociationService } from '../Domain/Setting/SubscriptionSettingsAssociationService'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../Domain/Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { PKCERepositoryInterface } from '../Domain/User/PKCERepositoryInterface'
|
||||
@@ -216,6 +215,7 @@ import { DeleteAuthenticator } from '../Domain/UseCase/DeleteAuthenticator/Delet
|
||||
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
||||
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||
import { CleanupExpiredSessions } from '../Domain/UseCase/CleanupExpiredSessions/CleanupExpiredSessions'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -463,6 +463,10 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.U2F_REQUIRE_USER_VERIFICATION)
|
||||
.toConstantValue(env.get('U2F_REQUIRE_USER_VERIFICATION', true) === 'true')
|
||||
container
|
||||
.bind(TYPES.READONLY_USERS)
|
||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
||||
|
||||
// Services
|
||||
container.bind<UAParser>(TYPES.DeviceDetector).toConstantValue(new UAParser())
|
||||
container.bind<SessionService>(TYPES.SessionService).to(SessionService)
|
||||
@@ -612,6 +616,9 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<CleanupSessionTraces>(TYPES.CleanupSessionTraces)
|
||||
.toConstantValue(new CleanupSessionTraces(container.get(TYPES.SessionTraceRepository)))
|
||||
container
|
||||
.bind<CleanupExpiredSessions>(TYPES.CleanupExpiredSessions)
|
||||
.toConstantValue(new CleanupExpiredSessions(container.get(TYPES.SessionRepository)))
|
||||
container.bind<AuthenticateUser>(TYPES.AuthenticateUser).to(AuthenticateUser)
|
||||
container.bind<AuthenticateRequest>(TYPES.AuthenticateRequest).to(AuthenticateRequest)
|
||||
container.bind<RefreshSessionToken>(TYPES.RefreshSessionToken).to(RefreshSessionToken)
|
||||
@@ -683,7 +690,6 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<ListSharedSubscriptionInvitations>(TYPES.ListSharedSubscriptionInvitations)
|
||||
.to(ListSharedSubscriptionInvitations)
|
||||
container.bind<GetSubscriptionSetting>(TYPES.GetSubscriptionSetting).to(GetSubscriptionSetting)
|
||||
container.bind<VerifyPredicate>(TYPES.VerifyPredicate).to(VerifyPredicate)
|
||||
container.bind<CreateCrossServiceToken>(TYPES.CreateCrossServiceToken).to(CreateCrossServiceToken)
|
||||
container.bind<ProcessUserRequest>(TYPES.ProcessUserRequest).to(ProcessUserRequest)
|
||||
|
||||
@@ -97,6 +97,7 @@ const TYPES = {
|
||||
U2F_RELYING_PARTY_NAME: Symbol.for('U2F_RELYING_PARTY_NAME'),
|
||||
U2F_EXPECTED_ORIGIN: Symbol.for('U2F_EXPECTED_ORIGIN'),
|
||||
U2F_REQUIRE_USER_VERIFICATION: Symbol.for('U2F_REQUIRE_USER_VERIFICATION'),
|
||||
READONLY_USERS: Symbol.for('READONLY_USERS'),
|
||||
// use cases
|
||||
AuthenticateUser: Symbol.for('AuthenticateUser'),
|
||||
AuthenticateRequest: Symbol.for('AuthenticateRequest'),
|
||||
@@ -131,12 +132,12 @@ const TYPES = {
|
||||
DeclineSharedSubscriptionInvitation: Symbol.for('DeclineSharedSubscriptionInvitation'),
|
||||
CancelSharedSubscriptionInvitation: Symbol.for('CancelSharedSubscriptionInvitation'),
|
||||
ListSharedSubscriptionInvitations: Symbol.for('ListSharedSubscriptionInvitations'),
|
||||
GetSubscriptionSetting: Symbol.for('GetSubscriptionSetting'),
|
||||
VerifyPredicate: Symbol.for('VerifyPredicate'),
|
||||
CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'),
|
||||
ProcessUserRequest: Symbol.for('ProcessUserRequest'),
|
||||
TraceSession: Symbol.for('TraceSession'),
|
||||
CleanupSessionTraces: Symbol.for('CleanupSessionTraces'),
|
||||
CleanupExpiredSessions: Symbol.for('CleanupExpiredSessions'),
|
||||
PersistStatistics: Symbol.for('PersistStatistics'),
|
||||
GenerateAuthenticatorRegistrationOptions: Symbol.for('GenerateAuthenticatorRegistrationOptions'),
|
||||
VerifyAuthenticatorRegistrationResponse: Symbol.for('VerifyAuthenticatorRegistrationResponse'),
|
||||
|
||||
@@ -69,7 +69,7 @@ export class AdminController extends BaseHttpController {
|
||||
const result = await this.doDeleteSetting.execute({
|
||||
uuid,
|
||||
userUuid,
|
||||
settingName: SettingName.MfaSecret,
|
||||
settingName: SettingName.NAMES.MfaSecret,
|
||||
timestamp: updatedAt,
|
||||
softDelete: true,
|
||||
})
|
||||
@@ -115,7 +115,7 @@ export class AdminController extends BaseHttpController {
|
||||
|
||||
const result = await this.doDeleteSetting.execute({
|
||||
userUuid,
|
||||
settingName: SettingName.EmailBackupFrequency,
|
||||
settingName: SettingName.NAMES.EmailBackupFrequency,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -2,12 +2,12 @@ import { inject, injectable } from 'inversify'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import {
|
||||
ApiVersion,
|
||||
HttpStatusCode,
|
||||
UserDeletionResponse,
|
||||
UserRegistrationRequestParams,
|
||||
UserRegistrationResponse,
|
||||
UserServerInterface,
|
||||
UserDeletionResponseBody,
|
||||
UserRegistrationResponseBody,
|
||||
} from '@standardnotes/api'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
@@ -16,13 +16,13 @@ import { Register } from '../Domain/UseCase/Register'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||
import { SignInWithRecoveryCodesRequestParams } from '../Infra/Http/Request/SignInWithRecoveryCodesRequestParams'
|
||||
import { SignInWithRecoveryCodesResponse } from '../Infra/Http/Response/SignInWithRecoveryCodesResponse'
|
||||
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||
import { RecoveryKeyParamsRequestParams } from '../Infra/Http/Request/RecoveryKeyParamsRequestParams'
|
||||
import { RecoveryKeyParamsResponse } from '../Infra/Http/Response/RecoveryKeyParamsResponse'
|
||||
import { SignInWithRecoveryCodesResponseBody } from '../Infra/Http/Response/SignInWithRecoveryCodesResponseBody'
|
||||
import { RecoveryKeyParamsResponseBody } from '../Infra/Http/Response/RecoveryKeyParamsResponseBody'
|
||||
import { GenerateRecoveryCodesResponseBody } from '../Infra/Http/Response/GenerateRecoveryCodesResponseBody'
|
||||
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
||||
import { GenerateRecoveryCodesRequestParams } from '../Infra/Http/Request/GenerateRecoveryCodesRequestParams'
|
||||
import { GenerateRecoveryCodesResponse } from '../Infra/Http/Response/GenerateRecoveryCodesResponse'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@injectable()
|
||||
@@ -38,11 +38,11 @@ export class AuthController implements UserServerInterface {
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async deleteAccount(_params: never): Promise<UserDeletionResponse> {
|
||||
async deleteAccount(_params: never): Promise<HttpResponse<UserDeletionResponseBody>> {
|
||||
throw new Error('This method is implemented on the payments server.')
|
||||
}
|
||||
|
||||
async register(params: UserRegistrationRequestParams): Promise<UserRegistrationResponse> {
|
||||
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
|
||||
if (!params.email || !params.password) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
@@ -93,7 +93,9 @@ export class AuthController implements UserServerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async generateRecoveryCodes(params: GenerateRecoveryCodesRequestParams): Promise<GenerateRecoveryCodesResponse> {
|
||||
async generateRecoveryCodes(
|
||||
params: GenerateRecoveryCodesRequestParams,
|
||||
): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
|
||||
const result = await this.doGenerateRecoveryCodes.execute({
|
||||
userUuid: params.userUuid,
|
||||
})
|
||||
@@ -119,7 +121,7 @@ export class AuthController implements UserServerInterface {
|
||||
|
||||
async signInWithRecoveryCodes(
|
||||
params: SignInWithRecoveryCodesRequestParams,
|
||||
): Promise<SignInWithRecoveryCodesResponse> {
|
||||
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
@@ -158,7 +160,9 @@ export class AuthController implements UserServerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async recoveryKeyParams(params: RecoveryKeyParamsRequestParams): Promise<RecoveryKeyParamsResponse> {
|
||||
async recoveryKeyParams(
|
||||
params: RecoveryKeyParamsRequestParams,
|
||||
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HttpStatusCode } from '@standardnotes/api'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Authenticator } from '../Domain/Authenticator/Authenticator'
|
||||
import { DeleteAuthenticator } from '../Domain/UseCase/DeleteAuthenticator/DeleteAuthenticator'
|
||||
@@ -13,11 +13,11 @@ import { GenerateAuthenticatorAuthenticationOptionsRequestParams } from '../Infr
|
||||
import { GenerateAuthenticatorRegistrationOptionsRequestParams } from '../Infra/Http/Request/GenerateAuthenticatorRegistrationOptionsRequestParams'
|
||||
import { ListAuthenticatorsRequestParams } from '../Infra/Http/Request/ListAuthenticatorsRequestParams'
|
||||
import { VerifyAuthenticatorRegistrationResponseRequestParams } from '../Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams'
|
||||
import { DeleteAuthenticatorResponse } from '../Infra/Http/Response/DeleteAuthenticatorResponse'
|
||||
import { GenerateAuthenticatorAuthenticationOptionsResponse } from '../Infra/Http/Response/GenerateAuthenticatorAuthenticationOptionsResponse'
|
||||
import { GenerateAuthenticatorRegistrationOptionsResponse } from '../Infra/Http/Response/GenerateAuthenticatorRegistrationOptionsResponse'
|
||||
import { ListAuthenticatorsResponse } from '../Infra/Http/Response/ListAuthenticatorsResponse'
|
||||
import { VerifyAuthenticatorRegistrationResponseResponse } from '../Infra/Http/Response/VerifyAuthenticatorRegistrationResponseResponse'
|
||||
import { DeleteAuthenticatorResponseBody } from '../Infra/Http/Response/DeleteAuthenticatorResponseBody'
|
||||
import { GenerateAuthenticatorAuthenticationOptionsResponseBody } from '../Infra/Http/Response/GenerateAuthenticatorAuthenticationOptionsResponseBody'
|
||||
import { GenerateAuthenticatorRegistrationOptionsResponseBody } from '../Infra/Http/Response/GenerateAuthenticatorRegistrationOptionsResponseBody'
|
||||
import { ListAuthenticatorsResponseBody } from '../Infra/Http/Response/ListAuthenticatorsResponseBody'
|
||||
import { VerifyAuthenticatorRegistrationResponseResponseBody } from '../Infra/Http/Response/VerifyAuthenticatorRegistrationResponseResponseBody'
|
||||
|
||||
export class AuthenticatorsController {
|
||||
constructor(
|
||||
@@ -29,7 +29,7 @@ export class AuthenticatorsController {
|
||||
private authenticatorHttpMapper: MapperInterface<Authenticator, AuthenticatorHttpProjection>,
|
||||
) {}
|
||||
|
||||
async list(params: ListAuthenticatorsRequestParams): Promise<ListAuthenticatorsResponse> {
|
||||
async list(params: ListAuthenticatorsRequestParams): Promise<HttpResponse<ListAuthenticatorsResponseBody>> {
|
||||
const result = await this.listAuthenticators.execute({
|
||||
userUuid: params.userUuid,
|
||||
})
|
||||
@@ -44,7 +44,7 @@ export class AuthenticatorsController {
|
||||
}
|
||||
}
|
||||
|
||||
async delete(params: DeleteAuthenticatorRequestParams): Promise<DeleteAuthenticatorResponse> {
|
||||
async delete(params: DeleteAuthenticatorRequestParams): Promise<HttpResponse<DeleteAuthenticatorResponseBody>> {
|
||||
const result = await this.deleteAuthenticator.execute({
|
||||
userUuid: params.userUuid,
|
||||
authenticatorId: params.authenticatorId,
|
||||
@@ -60,7 +60,7 @@ export class AuthenticatorsController {
|
||||
|
||||
async generateRegistrationOptions(
|
||||
params: GenerateAuthenticatorRegistrationOptionsRequestParams,
|
||||
): Promise<GenerateAuthenticatorRegistrationOptionsResponse> {
|
||||
): Promise<HttpResponse<GenerateAuthenticatorRegistrationOptionsResponseBody>> {
|
||||
const result = await this.generateAuthenticatorRegistrationOptions.execute({
|
||||
userUuid: params.userUuid,
|
||||
username: params.username,
|
||||
@@ -85,10 +85,9 @@ export class AuthenticatorsController {
|
||||
|
||||
async verifyRegistrationResponse(
|
||||
params: VerifyAuthenticatorRegistrationResponseRequestParams,
|
||||
): Promise<VerifyAuthenticatorRegistrationResponseResponse> {
|
||||
): Promise<HttpResponse<VerifyAuthenticatorRegistrationResponseResponseBody>> {
|
||||
const result = await this.verifyAuthenticatorRegistrationResponse.execute({
|
||||
userUuid: params.userUuid,
|
||||
name: params.name,
|
||||
attestationResponse: params.attestationResponse,
|
||||
})
|
||||
|
||||
@@ -105,13 +104,13 @@ export class AuthenticatorsController {
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: { success: result.getValue() },
|
||||
data: { id: result.getValue().toString() },
|
||||
}
|
||||
}
|
||||
|
||||
async generateAuthenticationOptions(
|
||||
params: GenerateAuthenticatorAuthenticationOptionsRequestParams,
|
||||
): Promise<GenerateAuthenticatorAuthenticationOptionsResponse> {
|
||||
): Promise<HttpResponse<GenerateAuthenticatorAuthenticationOptionsResponseBody>> {
|
||||
const result = await this.generateAuthenticatorAuthenticationOptions.execute({
|
||||
username: params.username,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpPost, results } from 'inversify-exp
|
||||
import { Request, Response } from 'express'
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
import { CreateListedAccount } from '../Domain/UseCase/CreateListedAccount/CreateListedAccount'
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
|
||||
@controller('/listed')
|
||||
export class ListedController extends BaseHttpController {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
|
||||
@@ -58,6 +58,10 @@ export class SessionsController extends BaseHttpController {
|
||||
|
||||
@httpGet('/', TYPES.AuthMiddleware, TYPES.SessionMiddleware)
|
||||
async getSessions(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json([])
|
||||
}
|
||||
|
||||
const useCaseResponse = await this.getActiveSessionsForUser.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('SettingsController', () => {
|
||||
const httpResponse = <results.JsonResult>await createController().getSetting(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'test' })
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'TEST' })
|
||||
|
||||
expect(result.statusCode).toEqual(200)
|
||||
})
|
||||
@@ -124,7 +124,7 @@ describe('SettingsController', () => {
|
||||
const httpResponse = <results.JsonResult>await createController().getSetting(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'test' })
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'TEST' })
|
||||
|
||||
expect(result.statusCode).toEqual(400)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
@@ -61,7 +61,7 @@ export class SettingsController extends BaseHttpController {
|
||||
}
|
||||
|
||||
const { userUuid, settingName } = request.params
|
||||
const result = await this.doGetSetting.execute({ userUuid, settingName })
|
||||
const result = await this.doGetSetting.execute({ userUuid, settingName: settingName.toUpperCase() })
|
||||
|
||||
if (result.success) {
|
||||
return this.json(result)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {
|
||||
AppleIAPConfirmRequestParams,
|
||||
AppleIAPConfirmResponse,
|
||||
HttpStatusCode,
|
||||
AppleIAPConfirmResponseBody,
|
||||
SubscriptionInviteAcceptRequestParams,
|
||||
SubscriptionInviteAcceptResponse,
|
||||
SubscriptionInviteAcceptResponseBody,
|
||||
SubscriptionInviteCancelRequestParams,
|
||||
SubscriptionInviteCancelResponse,
|
||||
SubscriptionInviteCancelResponseBody,
|
||||
SubscriptionInviteDeclineRequestParams,
|
||||
SubscriptionInviteDeclineResponse,
|
||||
SubscriptionInviteDeclineResponseBody,
|
||||
SubscriptionInviteListRequestParams,
|
||||
SubscriptionInviteListResponse,
|
||||
SubscriptionInviteListResponseBody,
|
||||
SubscriptionInviteRequestParams,
|
||||
SubscriptionInviteResponse,
|
||||
SubscriptionInviteResponseBody,
|
||||
SubscriptionServerInterface,
|
||||
} from '@standardnotes/api'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
@@ -37,11 +37,13 @@ export class SubscriptionInvitesController implements SubscriptionServerInterfac
|
||||
private listSharedSubscriptionInvitations: ListSharedSubscriptionInvitations,
|
||||
) {}
|
||||
|
||||
async confirmAppleIAP(_params: AppleIAPConfirmRequestParams): Promise<AppleIAPConfirmResponse> {
|
||||
async confirmAppleIAP(_params: AppleIAPConfirmRequestParams): Promise<HttpResponse<AppleIAPConfirmResponseBody>> {
|
||||
throw new Error('Method implemented on the payments service.')
|
||||
}
|
||||
|
||||
async acceptInvite(params: SubscriptionInviteAcceptRequestParams): Promise<SubscriptionInviteAcceptResponse> {
|
||||
async acceptInvite(
|
||||
params: SubscriptionInviteAcceptRequestParams,
|
||||
): Promise<HttpResponse<SubscriptionInviteAcceptResponseBody>> {
|
||||
const result = await this.acceptSharedSubscriptionInvitation.execute({
|
||||
sharedSubscriptionInvitationUuid: params.inviteUuid,
|
||||
})
|
||||
@@ -59,7 +61,9 @@ export class SubscriptionInvitesController implements SubscriptionServerInterfac
|
||||
}
|
||||
}
|
||||
|
||||
async declineInvite(params: SubscriptionInviteDeclineRequestParams): Promise<SubscriptionInviteDeclineResponse> {
|
||||
async declineInvite(
|
||||
params: SubscriptionInviteDeclineRequestParams,
|
||||
): Promise<HttpResponse<SubscriptionInviteDeclineResponseBody>> {
|
||||
const result = await this.declineSharedSubscriptionInvitation.execute({
|
||||
sharedSubscriptionInvitationUuid: params.inviteUuid,
|
||||
})
|
||||
@@ -77,7 +81,7 @@ export class SubscriptionInvitesController implements SubscriptionServerInterfac
|
||||
}
|
||||
}
|
||||
|
||||
async invite(params: SubscriptionInviteRequestParams): Promise<SubscriptionInviteResponse> {
|
||||
async invite(params: SubscriptionInviteRequestParams): Promise<HttpResponse<SubscriptionInviteResponseBody>> {
|
||||
if (!params.identifier) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
@@ -109,7 +113,9 @@ export class SubscriptionInvitesController implements SubscriptionServerInterfac
|
||||
}
|
||||
}
|
||||
|
||||
async cancelInvite(params: SubscriptionInviteCancelRequestParams): Promise<SubscriptionInviteCancelResponse> {
|
||||
async cancelInvite(
|
||||
params: SubscriptionInviteCancelRequestParams,
|
||||
): Promise<HttpResponse<SubscriptionInviteCancelResponseBody>> {
|
||||
const result = await this.cancelSharedSubscriptionInvitation.execute({
|
||||
sharedSubscriptionInvitationUuid: params.inviteUuid,
|
||||
inviterEmail: params.inviterEmail as string,
|
||||
@@ -128,7 +134,9 @@ export class SubscriptionInvitesController implements SubscriptionServerInterfac
|
||||
}
|
||||
}
|
||||
|
||||
async listInvites(params: SubscriptionInviteListRequestParams): Promise<SubscriptionInviteListResponse> {
|
||||
async listInvites(
|
||||
params: SubscriptionInviteListRequestParams,
|
||||
): Promise<HttpResponse<SubscriptionInviteListResponseBody>> {
|
||||
const result = await this.listSharedSubscriptionInvitations.execute({
|
||||
inviterEmail: params.inviterEmail as string,
|
||||
})
|
||||
|
||||
@@ -4,24 +4,24 @@ import * as express from 'express'
|
||||
|
||||
import { results } from 'inversify-express-utils'
|
||||
import { User } from '../Domain/User/User'
|
||||
import { GetSubscriptionSetting } from '../Domain/UseCase/GetSubscriptionSetting/GetSubscriptionSetting'
|
||||
import { SubscriptionSettingsController } from './SubscriptionSettingsController'
|
||||
import { GetSetting } from '../Domain/UseCase/GetSetting/GetSetting'
|
||||
|
||||
describe('SubscriptionSettingsController', () => {
|
||||
let getSubscriptionSetting: GetSubscriptionSetting
|
||||
let getSetting: GetSetting
|
||||
|
||||
let request: express.Request
|
||||
let response: express.Response
|
||||
let user: User
|
||||
|
||||
const createController = () => new SubscriptionSettingsController(getSubscriptionSetting)
|
||||
const createController = () => new SubscriptionSettingsController(getSetting)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
user.uuid = '123'
|
||||
|
||||
getSubscriptionSetting = {} as jest.Mocked<GetSubscriptionSetting>
|
||||
getSubscriptionSetting.execute = jest.fn()
|
||||
getSetting = {} as jest.Mocked<GetSetting>
|
||||
getSetting.execute = jest.fn()
|
||||
|
||||
request = {
|
||||
headers: {},
|
||||
@@ -41,12 +41,12 @@ describe('SubscriptionSettingsController', () => {
|
||||
uuid: '1-2-3',
|
||||
}
|
||||
|
||||
getSubscriptionSetting.execute = jest.fn().mockReturnValue({ success: true })
|
||||
getSetting.execute = jest.fn().mockReturnValue({ success: true })
|
||||
|
||||
const httpResponse = <results.JsonResult>await createController().getSubscriptionSetting(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(getSubscriptionSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', subscriptionSettingName: 'test' })
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'TEST' })
|
||||
|
||||
expect(result.statusCode).toEqual(200)
|
||||
})
|
||||
@@ -58,12 +58,12 @@ describe('SubscriptionSettingsController', () => {
|
||||
uuid: '1-2-3',
|
||||
}
|
||||
|
||||
getSubscriptionSetting.execute = jest.fn().mockReturnValue({ success: false })
|
||||
getSetting.execute = jest.fn().mockReturnValue({ success: false })
|
||||
|
||||
const httpResponse = <results.JsonResult>await createController().getSubscriptionSetting(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(getSubscriptionSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', subscriptionSettingName: 'test' })
|
||||
expect(getSetting.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', settingName: 'TEST' })
|
||||
|
||||
expect(result.statusCode).toEqual(400)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
@@ -9,19 +8,19 @@ import {
|
||||
results,
|
||||
} from 'inversify-express-utils'
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
import { GetSubscriptionSetting } from '../Domain/UseCase/GetSubscriptionSetting/GetSubscriptionSetting'
|
||||
import { GetSetting } from '../Domain/UseCase/GetSetting/GetSetting'
|
||||
|
||||
@controller('/users/:userUuid')
|
||||
export class SubscriptionSettingsController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.GetSubscriptionSetting) private doGetSubscriptionSetting: GetSubscriptionSetting) {
|
||||
constructor(@inject(TYPES.GetSetting) private doGetSetting: GetSetting) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/subscription-settings/:subscriptionSettingName', TYPES.ApiGatewayAuthMiddleware)
|
||||
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.doGetSubscriptionSetting.execute({
|
||||
const result = await this.doGetSetting.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
subscriptionSettingName: request.params.subscriptionSettingName as SubscriptionSettingName,
|
||||
settingName: request.params.subscriptionSettingName.toUpperCase(),
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
@@ -77,7 +77,7 @@ export class SubscriptionTokensController extends BaseHttpController {
|
||||
const user = authenticateTokenResponse.user as User
|
||||
let extensionKey = undefined
|
||||
const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ExtensionKey,
|
||||
settingName: SettingName.create(SettingName.NAMES.ExtensionKey).getValue(),
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (extensionKeySetting !== null) {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import {
|
||||
HttpStatusCode,
|
||||
UserRequestRequestParams,
|
||||
UserRequestResponse,
|
||||
UserRequestServerInterface,
|
||||
} from '@standardnotes/api'
|
||||
import { UserRequestRequestParams, UserRequestResponseBody, UserRequestServerInterface } from '@standardnotes/api'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/ProcessUserRequest'
|
||||
@@ -12,7 +8,7 @@ import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/Process
|
||||
export class UserRequestsController implements UserRequestServerInterface {
|
||||
constructor(@inject(TYPES.ProcessUserRequest) private processUserRequest: ProcessUserRequest) {}
|
||||
|
||||
async submitUserRequest(params: UserRequestRequestParams): Promise<UserRequestResponse> {
|
||||
async submitUserRequest(params: UserRequestRequestParams): Promise<HttpResponse<UserRequestResponseBody>> {
|
||||
const result = await this.processUserRequest.execute({
|
||||
requestType: params.requestType,
|
||||
userEmail: params.userEmail as string,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import {
|
||||
BaseHttpController,
|
||||
controller,
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
results,
|
||||
} from 'inversify-express-utils'
|
||||
import { CreateValetTokenPayload } from '@standardnotes/responses'
|
||||
import { ErrorTag } from '@standardnotes/api'
|
||||
import { CreateValetTokenPayload, ErrorTag } from '@standardnotes/responses'
|
||||
import { ValetTokenOperation } from '@standardnotes/security'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
||||
import { KeyParamsFactoryInterface } from '../User/KeyParamsFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { AuthResponseFactory20200115 } from './AuthResponseFactory20200115'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
describe('AuthResponseFactory20200115', () => {
|
||||
let sessionService: SessionServiceInterface
|
||||
@@ -18,13 +20,24 @@ describe('AuthResponseFactory20200115', () => {
|
||||
let sessionPayload: SessionBody
|
||||
let logger: Logger
|
||||
let tokenEncoder: TokenEncoderInterface<SessionTokenData>
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
|
||||
const createFactory = () =>
|
||||
new AuthResponseFactory20200115(sessionService, keyParamsFactory, userProjector, tokenEncoder, logger)
|
||||
new AuthResponseFactory20200115(
|
||||
sessionService,
|
||||
keyParamsFactory,
|
||||
userProjector,
|
||||
tokenEncoder,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
logger.error = jest.fn()
|
||||
|
||||
sessionPayload = {
|
||||
access_token: 'access_token',
|
||||
@@ -52,6 +65,12 @@ describe('AuthResponseFactory20200115', () => {
|
||||
|
||||
tokenEncoder = {} as jest.Mocked<TokenEncoderInterface<SessionTokenData>>
|
||||
tokenEncoder.encodeToken = jest.fn().mockReturnValue('foobar')
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createSessionCreatedEvent = jest.fn().mockReturnValue({})
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
})
|
||||
|
||||
it('should create a 20161215 auth response if user does not support sessions', async () => {
|
||||
@@ -82,6 +101,37 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
},
|
||||
session: {
|
||||
access_token: 'access_token',
|
||||
refresh_token: 'refresh_token',
|
||||
access_expiration: 123,
|
||||
refresh_expiration: 234,
|
||||
readonly_access: false,
|
||||
},
|
||||
user: {
|
||||
foo: 'bar',
|
||||
},
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should create a 20200115 auth response even if publishing the domain event fails', async () => {
|
||||
domainEventPublisher.publish = jest.fn().mockRejectedValue(new Error('test'))
|
||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20200115',
|
||||
userAgent: 'Google Chrome',
|
||||
ephemeralSession: false,
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
|
||||
@@ -3,18 +3,22 @@ import {
|
||||
SessionTokenData,
|
||||
TokenEncoderInterface,
|
||||
} from '@standardnotes/security'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { SessionBody } from '@standardnotes/responses'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
||||
import { KeyParamsFactoryInterface } from '../User/KeyParamsFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { AuthResponseFactory20190520 } from './AuthResponseFactory20190520'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||
import { AuthResponseFactory20190520 } from './AuthResponseFactory20190520'
|
||||
|
||||
@injectable()
|
||||
export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
@@ -23,6 +27,8 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
@inject(TYPES.KeyParamsFactory) private keyParamsFactory: KeyParamsFactoryInterface,
|
||||
@inject(TYPES.UserProjector) userProjector: ProjectorInterface<User>,
|
||||
@inject(TYPES.SessionTokenEncoder) protected override tokenEncoder: TokenEncoderInterface<SessionTokenData>,
|
||||
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.Logger) logger: Logger,
|
||||
) {
|
||||
super(userProjector, tokenEncoder, logger)
|
||||
@@ -67,6 +73,16 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
return this.sessionService.createNewEphemeralSessionForUser(dto)
|
||||
}
|
||||
|
||||
return this.sessionService.createNewSessionForUser(dto)
|
||||
const session = this.sessionService.createNewSessionForUser(dto)
|
||||
|
||||
try {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSessionCreatedEvent({ userUuid: dto.user.uuid }),
|
||||
)
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to publish session created event: ${(error as Error).message}`)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ describe('Authenticator', () => {
|
||||
it('should create an entity', () => {
|
||||
const entityOrError = Authenticator.create({
|
||||
counter: 1,
|
||||
name: 'my-key',
|
||||
credentialBackedUp: true,
|
||||
credentialDeviceType: 'singleDevice',
|
||||
credentialId: Buffer.from('credentialId'),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Dates, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface AuthenticatorProps {
|
||||
name: string
|
||||
userUuid: Uuid
|
||||
credentialId: Uint8Array
|
||||
credentialPublicKey: Uint8Array
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface AuthenticatorRepositoryInterface {
|
||||
findById(id: UniqueEntityId): Promise<Authenticator | null>
|
||||
findByUserUuidAndCredentialId(userUuid: Uuid, credentialId: string): Promise<Authenticator | null>
|
||||
save(authenticator: Authenticator): Promise<void>
|
||||
updateCounter(id: UniqueEntityId, counter: number): Promise<void>
|
||||
remove(authenticator: Authenticator): Promise<void>
|
||||
removeByUserUuid(userUuid: Uuid): Promise<void>
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
MuteEmailsSettingChangedEvent,
|
||||
EmailRequestedEvent,
|
||||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
@@ -31,6 +33,36 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
|
||||
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
|
||||
return {
|
||||
type: 'SESSION_CREATED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent {
|
||||
return {
|
||||
type: 'SESSION_REFRESHED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createStatisticPersistenceRequestedEvent(dto: {
|
||||
statisticMeasureName: string
|
||||
value: number
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
MuteEmailsSettingChangedEvent,
|
||||
EmailRequestedEvent,
|
||||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
||||
|
||||
@@ -92,4 +94,6 @@ export interface DomainEventFactoryInterface {
|
||||
value: number
|
||||
date: number
|
||||
}): StatisticPersistenceRequestedEvent
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
|
||||
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ describe('FeatureService', () => {
|
||||
])
|
||||
|
||||
const nonSubscriptionRole = {
|
||||
name: RoleName.NAMES.FilesBetaUser,
|
||||
name: RoleName.NAMES.InternalTeamUser,
|
||||
uuid: 'role-files-beta',
|
||||
permissions: Promise.resolve([nonSubscriptionPermission]),
|
||||
} as jest.Mocked<Role>
|
||||
|
||||
@@ -27,13 +27,13 @@ export class EmailSubscriptionUnsubscribedEventHandler implements DomainEventHan
|
||||
private getSettingNameFromLevel(level: string): string {
|
||||
switch (level) {
|
||||
case EmailLevel.LEVELS.FailedCloudBackup:
|
||||
return SettingName.MuteFailedCloudBackupsEmails
|
||||
return SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
case EmailLevel.LEVELS.FailedEmailBackup:
|
||||
return SettingName.MuteFailedBackupsEmails
|
||||
return SettingName.NAMES.MuteFailedBackupsEmails
|
||||
case EmailLevel.LEVELS.Marketing:
|
||||
return SettingName.MuteMarketingEmails
|
||||
return SettingName.NAMES.MuteMarketingEmails
|
||||
case EmailLevel.LEVELS.SignIn:
|
||||
return SettingName.MuteSignInEmails
|
||||
return SettingName.NAMES.MuteSignInEmails
|
||||
default:
|
||||
throw new Error(`Unknown level: ${level}`)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class ExtensionKeyGrantedEventHandler implements DomainEventHandlerInterf
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -38,7 +38,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: user.uuid,
|
||||
userSubscriptionUuid: subscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(),
|
||||
})
|
||||
if (bytesUsedSetting === null) {
|
||||
this.logger.warn(`Could not find bytes used setting for user with uuid: ${user.uuid}`)
|
||||
@@ -51,7 +51,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
await this.subscriptionSettingService.createOrReplace({
|
||||
userSubscription: subscription,
|
||||
props: {
|
||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -47,7 +47,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: (await subscription.user).uuid,
|
||||
userSubscriptionUuid: subscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(),
|
||||
})
|
||||
if (bytesUsedSetting !== null) {
|
||||
bytesUsed = bytesUsedSetting.value as string
|
||||
@@ -56,7 +56,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||
await this.subscriptionSettingService.createOrReplace({
|
||||
userSubscription: subscription,
|
||||
props: {
|
||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
|
||||
let authSecrets: ListedAuthorSecretsData = [newSecret]
|
||||
|
||||
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ListedAuthorSecrets,
|
||||
settingName: SettingName.create(SettingName.NAMES.ListedAuthorSecrets).getValue(),
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (listedAuthorSecretsSetting !== null) {
|
||||
@@ -40,7 +40,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ListedAuthorSecrets,
|
||||
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||
unencryptedValue: JSON.stringify(authSecrets),
|
||||
sensitive: false,
|
||||
},
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
||||
}
|
||||
|
||||
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ListedAuthorSecrets,
|
||||
settingName: SettingName.create(SettingName.NAMES.ListedAuthorSecrets).getValue(),
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (listedAuthorSecretsSetting === null) {
|
||||
@@ -43,7 +43,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ListedAuthorSecrets,
|
||||
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||
unencryptedValue: JSON.stringify(filteredSecrets),
|
||||
sensitive: false,
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -95,7 +95,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('RoleToSubscriptionMap', () => {
|
||||
name: RoleName.NAMES.CoreUser,
|
||||
} as jest.Mocked<Role>,
|
||||
{
|
||||
name: RoleName.NAMES.FilesBetaUser,
|
||||
name: RoleName.NAMES.InternalTeamUser,
|
||||
} as jest.Mocked<Role>,
|
||||
{
|
||||
name: RoleName.NAMES.PlusUser,
|
||||
@@ -38,7 +38,7 @@ describe('RoleToSubscriptionMap', () => {
|
||||
name: RoleName.NAMES.CoreUser,
|
||||
},
|
||||
{
|
||||
name: RoleName.NAMES.FilesBetaUser,
|
||||
name: RoleName.NAMES.InternalTeamUser,
|
||||
},
|
||||
])
|
||||
})
|
||||
@@ -49,7 +49,7 @@ describe('RoleToSubscriptionMap', () => {
|
||||
name: RoleName.NAMES.CoreUser,
|
||||
} as jest.Mocked<Role>,
|
||||
{
|
||||
name: RoleName.NAMES.FilesBetaUser,
|
||||
name: RoleName.NAMES.InternalTeamUser,
|
||||
} as jest.Mocked<Role>,
|
||||
{
|
||||
name: RoleName.NAMES.PlusUser,
|
||||
|
||||
@@ -12,7 +12,7 @@ export class RoleToSubscriptionMap implements RoleToSubscriptionMapInterface {
|
||||
[RoleName.NAMES.ProUser, SubscriptionName.ProPlan],
|
||||
])
|
||||
|
||||
private readonly nonSubscriptionRoles = [RoleName.NAMES.CoreUser, RoleName.NAMES.FilesBetaUser]
|
||||
private readonly nonSubscriptionRoles = [RoleName.NAMES.CoreUser, RoleName.NAMES.InternalTeamUser]
|
||||
|
||||
filterNonSubscriptionRoles(roles: Role[]): Array<Role> {
|
||||
return roles.filter((role) => this.nonSubscriptionRoles.includes(role.name))
|
||||
|
||||
@@ -12,4 +12,5 @@ export interface SessionRepositoryInterface {
|
||||
save(session: Session): Promise<Session>
|
||||
remove(session: Session): Promise<Session>
|
||||
clearUserAgentByUserUuid(userUuid: string): Promise<void>
|
||||
removeExpiredBefore(date: Date): Promise<void>
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ describe('SessionService', () => {
|
||||
let cryptoNode: CryptoNode
|
||||
let traceSession: TraceSession
|
||||
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
|
||||
const readonlyUsers = ['demo@standardnotes.com']
|
||||
|
||||
const createService = () =>
|
||||
new SessionService(
|
||||
@@ -49,6 +50,7 @@ describe('SessionService', () => {
|
||||
cryptoNode,
|
||||
traceSession,
|
||||
userSubscriptionRepository,
|
||||
readonlyUsers,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -59,6 +61,7 @@ describe('SessionService', () => {
|
||||
session.apiVersion = ApiVersion.v20200115
|
||||
session.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
session.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
session.readonlyAccess = false
|
||||
|
||||
revokedSession = {} as jest.Mocked<RevokedSession>
|
||||
revokedSession.uuid = '2e1e43'
|
||||
@@ -182,6 +185,42 @@ describe('SessionService', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should create new readonly session for a user that is readonly restricted', async () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
user.email = 'demo@standardnotes.com'
|
||||
user.uuid = '123'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(sessionRepository.save).toHaveBeenCalledWith(expect.any(Session))
|
||||
expect(sessionRepository.save).toHaveBeenCalledWith({
|
||||
accessExpiration: expect.any(Date),
|
||||
apiVersion: '003',
|
||||
createdAt: expect.any(Date),
|
||||
hashedAccessToken: expect.any(String),
|
||||
hashedRefreshToken: expect.any(String),
|
||||
refreshExpiration: expect.any(Date),
|
||||
updatedAt: expect.any(Date),
|
||||
userAgent: 'Google Chrome',
|
||||
userUuid: '123',
|
||||
uuid: expect.any(String),
|
||||
readonlyAccess: true,
|
||||
})
|
||||
|
||||
expect(sessionPayload).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
refresh_token: expect.any(String),
|
||||
readonly_access: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should create new session for a user with disabled user agent logging', async () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
user.uuid = '123'
|
||||
@@ -409,9 +448,9 @@ describe('SessionService', () => {
|
||||
})
|
||||
|
||||
it('should determine if a refresh token is valid', async () => {
|
||||
expect(createService().isRefreshTokenValid(session, '1:2:3')).toBeTruthy()
|
||||
expect(createService().isRefreshTokenValid(session, '1:2:4')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenValid(session, '1:2')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:3')).toBeTruthy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:4')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return device info based on user agent', () => {
|
||||
|
||||
@@ -39,6 +39,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
@inject(TYPES.CryptoNode) private cryptoNode: CryptoNode,
|
||||
@inject(TYPES.TraceSession) private traceSession: TraceSession,
|
||||
@inject(TYPES.UserSubscriptionRepository) private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||
@inject(TYPES.READONLY_USERS) private readonlyUsers: string[],
|
||||
) {}
|
||||
|
||||
async createNewSessionForUser(dto: {
|
||||
@@ -113,7 +114,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
return sessionPayload
|
||||
}
|
||||
|
||||
isRefreshTokenValid(session: Session, token: string): boolean {
|
||||
isRefreshTokenMatchingHashedSessionToken(session: Session, token: string): boolean {
|
||||
const tokenParts = token.split(':')
|
||||
const refreshToken = tokenParts[2]
|
||||
if (!refreshToken) {
|
||||
@@ -268,7 +269,9 @@ export class SessionService implements SessionServiceInterface {
|
||||
session.apiVersion = dto.apiVersion
|
||||
session.createdAt = this.timer.getUTCDate()
|
||||
session.updatedAt = this.timer.getUTCDate()
|
||||
session.readonlyAccess = dto.readonlyAccess
|
||||
|
||||
const userIsReadonly = this.readonlyUsers.includes(dto.user.email)
|
||||
session.readonlyAccess = userIsReadonly || dto.readonlyAccess
|
||||
|
||||
return session
|
||||
}
|
||||
@@ -302,13 +305,13 @@ export class SessionService implements SessionServiceInterface {
|
||||
refresh_token: `${SessionService.SESSION_TOKEN_VERSION}:${session.uuid}:${refreshToken}`,
|
||||
access_expiration: this.timer.convertStringDateToMilliseconds(accessTokenExpiration.toString()),
|
||||
refresh_expiration: this.timer.convertStringDateToMilliseconds(refreshTokenExpiration.toString()),
|
||||
readonly_access: false,
|
||||
readonly_access: session.readonlyAccess,
|
||||
}
|
||||
}
|
||||
|
||||
private async isLoggingUserAgentEnabledOnSessions(user: User): Promise<boolean> {
|
||||
const loggingSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.LogSessionUserAgent,
|
||||
settingName: SettingName.create(SettingName.NAMES.LogSessionUserAgent).getValue(),
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface SessionServiceInterface {
|
||||
getRevokedSessionFromToken(token: string): Promise<RevokedSession | null>
|
||||
markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession>
|
||||
deleteSessionByToken(token: string): Promise<string | null>
|
||||
isRefreshTokenValid(session: Session, token: string): boolean
|
||||
isRefreshTokenMatchingHashedSessionToken(session: Session, token: string): boolean
|
||||
getDeviceInfo(session: Session): string
|
||||
getOperatingSystemInfoFromUserAgent(userAgent: string): string
|
||||
getBrowserInfoFromUserAgent(userAgent: string): string
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
|
||||
export type FindSubscriptionSettingDTO = {
|
||||
userUuid: string
|
||||
userSubscriptionUuid: string
|
||||
subscriptionSettingName: SubscriptionSettingName
|
||||
subscriptionSettingName: SettingName
|
||||
settingUuid?: string
|
||||
}
|
||||
|
||||
@@ -70,12 +70,11 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger session cleanup if user is disabling session user agent logging', async () => {
|
||||
const setting = {
|
||||
name: SettingName.LogSessionUserAgent,
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, LogSessionUserAgentOption.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
user,
|
||||
LogSessionUserAgentOption.Disabled,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
||||
@@ -85,55 +84,50 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Daily,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Daily,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false)
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails muted', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Daily,
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Daily,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '6-7-8', true)
|
||||
})
|
||||
|
||||
it('should not trigger backup if email backup setting is disabled', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Disabled,
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Disabled,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should trigger cloud backup if dropbox backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.DropboxBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'test-token')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.DropboxBackupToken, user, 'test-token')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
@@ -146,17 +140,13 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger cloud backup if dropbox backup setting is created - muted emails', async () => {
|
||||
const setting = {
|
||||
name: SettingName.DropboxBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedCloudBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'test-token')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.DropboxBackupToken, user, 'test-token')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
@@ -169,13 +159,9 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger cloud backup if google drive backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.GoogleDriveBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'test-token')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.GoogleDriveBackupToken, user, 'test-token')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
@@ -188,13 +174,9 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger cloud backup if one drive backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'test-token')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.OneDriveBackupToken, user, 'test-token')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
@@ -207,13 +189,13 @@ describe('SettingInterpreter', () => {
|
||||
})
|
||||
|
||||
it('should trigger mute subscription emails rejection if mute setting changed', async () => {
|
||||
const setting = {
|
||||
name: SettingName.MuteMarketingEmails,
|
||||
value: MuteMarketingEmailsOption.Muted,
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, MuteMarketingEmailsOption.Muted)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
user,
|
||||
MuteMarketingEmailsOption.Muted,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createMuteEmailsSettingChangedEvent).toHaveBeenCalledWith({
|
||||
@@ -225,19 +207,13 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if backup frequency setting is updated and a backup token setting is present', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
name: SettingName.NAMES.OneDriveBackupToken,
|
||||
serverEncryptionVersion: 1,
|
||||
value: 'encrypted-backup-token',
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: 'daily',
|
||||
sensitive: false,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.OneDriveBackupFrequency, user, 'daily')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
@@ -251,19 +227,17 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should not trigger cloud backup if backup frequency setting is updated as disabled', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
name: SettingName.NAMES.OneDriveBackupToken,
|
||||
serverEncryptionVersion: 1,
|
||||
value: 'encrypted-backup-token',
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: OneDriveBackupFrequency.Disabled,
|
||||
sensitive: false,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, OneDriveBackupFrequency.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.OneDriveBackupFrequency,
|
||||
user,
|
||||
OneDriveBackupFrequency.Disabled,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
@@ -271,14 +245,8 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should not trigger cloud backup if backup frequency setting is updated and a backup token setting is not present', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce(null)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: 'daily',
|
||||
sensitive: false,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||
await createInterpreter().interpretSettingUpdated(SettingName.NAMES.OneDriveBackupFrequency, user, 'daily')
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
|
||||
@@ -15,7 +15,6 @@ import { Logger } from 'winston'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { Setting } from './Setting'
|
||||
import { SettingDecrypterInterface } from './SettingDecrypterInterface'
|
||||
import { SettingInterpreterInterface } from './SettingInterpreterInterface'
|
||||
import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
||||
@@ -23,15 +22,15 @@ import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
||||
@injectable()
|
||||
export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
private readonly cloudBackupTokenSettings = [
|
||||
SettingName.DropboxBackupToken,
|
||||
SettingName.GoogleDriveBackupToken,
|
||||
SettingName.OneDriveBackupToken,
|
||||
SettingName.NAMES.DropboxBackupToken,
|
||||
SettingName.NAMES.GoogleDriveBackupToken,
|
||||
SettingName.NAMES.OneDriveBackupToken,
|
||||
]
|
||||
|
||||
private readonly cloudBackupFrequencySettings = [
|
||||
SettingName.DropboxBackupFrequency,
|
||||
SettingName.GoogleDriveBackupFrequency,
|
||||
SettingName.OneDriveBackupFrequency,
|
||||
SettingName.NAMES.DropboxBackupFrequency,
|
||||
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||
SettingName.NAMES.OneDriveBackupFrequency,
|
||||
]
|
||||
|
||||
private readonly cloudBackupFrequencyDisabledValues = [
|
||||
@@ -40,11 +39,11 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
OneDriveBackupFrequency.Disabled,
|
||||
]
|
||||
|
||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<SettingName, string> = new Map([
|
||||
[SettingName.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||
[SettingName.MuteFailedCloudBackupsEmails, EmailLevel.LEVELS.FailedCloudBackup],
|
||||
[SettingName.MuteMarketingEmails, EmailLevel.LEVELS.Marketing],
|
||||
[SettingName.MuteSignInEmails, EmailLevel.LEVELS.SignIn],
|
||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<string, string> = new Map([
|
||||
[SettingName.NAMES.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||
[SettingName.NAMES.MuteFailedCloudBackupsEmails, EmailLevel.LEVELS.FailedCloudBackup],
|
||||
[SettingName.NAMES.MuteMarketingEmails, EmailLevel.LEVELS.Marketing],
|
||||
[SettingName.NAMES.MuteSignInEmails, EmailLevel.LEVELS.SignIn],
|
||||
])
|
||||
|
||||
constructor(
|
||||
@@ -55,20 +54,24 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async interpretSettingUpdated(updatedSetting: Setting, user: User, unencryptedValue: string | null): Promise<void> {
|
||||
if (this.isChangingMuteEmailsSetting(updatedSetting)) {
|
||||
await this.triggerEmailSubscriptionChange(user, updatedSetting.name as SettingName, unencryptedValue)
|
||||
async interpretSettingUpdated(
|
||||
updatedSettingName: string,
|
||||
user: User,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
if (this.isChangingMuteEmailsSetting(updatedSettingName)) {
|
||||
await this.triggerEmailSubscriptionChange(user, updatedSettingName, unencryptedValue)
|
||||
}
|
||||
|
||||
if (this.isEnablingEmailBackupSetting(updatedSetting)) {
|
||||
if (this.isEnablingEmailBackupSetting(updatedSettingName, unencryptedValue)) {
|
||||
await this.triggerEmailBackup(user.uuid)
|
||||
}
|
||||
|
||||
if (this.isEnablingCloudBackupSetting(updatedSetting)) {
|
||||
await this.triggerCloudBackup(updatedSetting, user.uuid, unencryptedValue)
|
||||
if (this.isEnablingCloudBackupSetting(updatedSettingName, unencryptedValue)) {
|
||||
await this.triggerCloudBackup(updatedSettingName, user.uuid, unencryptedValue)
|
||||
}
|
||||
|
||||
if (this.isDisablingSessionUserAgentLogging(updatedSetting)) {
|
||||
if (this.isDisablingSessionUserAgentLogging(updatedSettingName, unencryptedValue)) {
|
||||
await this.triggerSessionUserAgentCleanup(user)
|
||||
}
|
||||
}
|
||||
@@ -77,7 +80,7 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
let userHasEmailsMuted = false
|
||||
let muteEmailsSettingUuid = ''
|
||||
const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
userUuid,
|
||||
)
|
||||
if (muteFailedEmailsBackupSetting !== null) {
|
||||
@@ -90,36 +93,39 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
)
|
||||
}
|
||||
|
||||
private isChangingMuteEmailsSetting(setting: Setting): boolean {
|
||||
private isChangingMuteEmailsSetting(settingName: string): boolean {
|
||||
return [
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.MuteSignInEmails,
|
||||
].includes(setting.name as SettingName)
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
].includes(settingName)
|
||||
}
|
||||
|
||||
private isEnablingEmailBackupSetting(setting: Setting): boolean {
|
||||
return setting.name === SettingName.EmailBackupFrequency && setting.value !== EmailBackupFrequency.Disabled
|
||||
}
|
||||
|
||||
private isEnablingCloudBackupSetting(setting: Setting): boolean {
|
||||
private isEnablingEmailBackupSetting(settingName: string, newValue: string | null): boolean {
|
||||
return (
|
||||
(this.cloudBackupFrequencySettings.includes(setting.name as SettingName) ||
|
||||
this.cloudBackupTokenSettings.includes(setting.name as SettingName)) &&
|
||||
settingName === SettingName.NAMES.EmailBackupFrequency &&
|
||||
[EmailBackupFrequency.Daily, EmailBackupFrequency.Weekly].includes(newValue as EmailBackupFrequency)
|
||||
)
|
||||
}
|
||||
|
||||
private isEnablingCloudBackupSetting(settingName: string, newValue: string | null): boolean {
|
||||
return (
|
||||
(this.cloudBackupFrequencySettings.includes(settingName) ||
|
||||
this.cloudBackupTokenSettings.includes(settingName)) &&
|
||||
!this.cloudBackupFrequencyDisabledValues.includes(
|
||||
setting.value as DropboxBackupFrequency | OneDriveBackupFrequency | GoogleDriveBackupFrequency,
|
||||
newValue as DropboxBackupFrequency | OneDriveBackupFrequency | GoogleDriveBackupFrequency,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private isDisablingSessionUserAgentLogging(setting: Setting): boolean {
|
||||
return SettingName.LogSessionUserAgent === setting.name && LogSessionUserAgentOption.Disabled === setting.value
|
||||
private isDisablingSessionUserAgentLogging(settingName: string, newValue: string | null): boolean {
|
||||
return SettingName.NAMES.LogSessionUserAgent === settingName && LogSessionUserAgentOption.Disabled === newValue
|
||||
}
|
||||
|
||||
private async triggerEmailSubscriptionChange(
|
||||
user: User,
|
||||
settingName: SettingName,
|
||||
settingName: string,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
@@ -140,33 +146,34 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
)
|
||||
}
|
||||
|
||||
private async triggerCloudBackup(setting: Setting, userUuid: string, unencryptedValue: string | null): Promise<void> {
|
||||
private async triggerCloudBackup(
|
||||
settingName: string,
|
||||
userUuid: string,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
let cloudProvider
|
||||
let tokenSettingName
|
||||
switch (setting.name) {
|
||||
case SettingName.DropboxBackupToken:
|
||||
case SettingName.DropboxBackupFrequency:
|
||||
switch (settingName) {
|
||||
case SettingName.NAMES.DropboxBackupToken:
|
||||
case SettingName.NAMES.DropboxBackupFrequency:
|
||||
cloudProvider = 'DROPBOX'
|
||||
tokenSettingName = SettingName.DropboxBackupToken
|
||||
tokenSettingName = SettingName.NAMES.DropboxBackupToken
|
||||
break
|
||||
case SettingName.GoogleDriveBackupToken:
|
||||
case SettingName.GoogleDriveBackupFrequency:
|
||||
case SettingName.NAMES.GoogleDriveBackupToken:
|
||||
case SettingName.NAMES.GoogleDriveBackupFrequency:
|
||||
cloudProvider = 'GOOGLE_DRIVE'
|
||||
tokenSettingName = SettingName.GoogleDriveBackupToken
|
||||
tokenSettingName = SettingName.NAMES.GoogleDriveBackupToken
|
||||
break
|
||||
case SettingName.OneDriveBackupToken:
|
||||
case SettingName.OneDriveBackupFrequency:
|
||||
case SettingName.NAMES.OneDriveBackupToken:
|
||||
case SettingName.NAMES.OneDriveBackupFrequency:
|
||||
cloudProvider = 'ONE_DRIVE'
|
||||
tokenSettingName = SettingName.OneDriveBackupToken
|
||||
tokenSettingName = SettingName.NAMES.OneDriveBackupToken
|
||||
break
|
||||
}
|
||||
|
||||
let backupToken = null
|
||||
if (this.cloudBackupFrequencySettings.includes(setting.name as SettingName)) {
|
||||
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(
|
||||
tokenSettingName as SettingName,
|
||||
userUuid,
|
||||
)
|
||||
if (this.cloudBackupFrequencySettings.includes(settingName)) {
|
||||
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(tokenSettingName as string, userUuid)
|
||||
if (tokenSetting !== null) {
|
||||
backupToken = await this.settingDecrypter.decryptSettingValue(tokenSetting, userUuid)
|
||||
}
|
||||
@@ -183,7 +190,7 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
let userHasEmailsMuted = false
|
||||
let muteEmailsSettingUuid = ''
|
||||
const muteFailedCloudBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
userUuid,
|
||||
)
|
||||
if (muteFailedCloudBackupSetting !== null) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { User } from '../User/User'
|
||||
import { Setting } from './Setting'
|
||||
|
||||
export interface SettingInterpreterInterface {
|
||||
interpretSettingUpdated(updatedSetting: Setting, user: User, newUnencryptedValue: string | null): Promise<void>
|
||||
interpretSettingUpdated(updatedSettingName: string, user: User, newUnencryptedValue: string | null): Promise<void>
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user